aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/OpenSimClient.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/OpenSimClient.cs')
-rw-r--r--src/OpenSimClient.cs339
1 files changed, 339 insertions, 0 deletions
diff --git a/src/OpenSimClient.cs b/src/OpenSimClient.cs
new file mode 100644
index 0000000..d3f90b7
--- /dev/null
+++ b/src/OpenSimClient.cs
@@ -0,0 +1,339 @@
1/*
2Copyright (c) OpenSim project, http://osgrid.org/
3*
4* Redistribution and use in source and binary forms, with or without
5* modification, are permitted provided that the following conditions are met:
6* * Redistributions of source code must retain the above copyright
7* notice, this list of conditions and the following disclaimer.
8* * Redistributions in binary form must reproduce the above copyright
9* notice, this list of conditions and the following disclaimer in the
10* documentation and/or other materials provided with the distribution.
11* * Neither the name of the <organization> nor the
12* names of its contributors may be used to endorse or promote products
13* derived from this software without specific prior written permission.
14*
15* THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY
16* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
19* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25*/
26
27using System;
28using System.Collections;
29using System.Collections.Generic;
30using libsecondlife;
31using libsecondlife.Packets;
32using System.Net;
33using System.Net.Sockets;
34using System.IO;
35using System.Threading;
36using System.Timers;
37
38namespace OpenSim
39{
40 /// <summary>
41 /// Handles new client connections
42 /// Constructor takes a single Packet and authenticates everything
43 /// </summary>
44 public class OpenSimClient {
45
46 public LLUUID AgentID;
47 public LLUUID SessionID;
48 public uint CircuitCode;
49 public world.Avatar ClientAvatar;
50 private UseCircuitCodePacket cirpack;
51 private Thread ClientThread;
52 private EndPoint userEP;
53 private BlockingQueue<QueItem> PacketQueue;
54 private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>();
55 private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>();
56 private System.Timers.Timer AckTimer;
57 private uint Sequence = 0;
58 private object SequenceLock = new object();
59 private const int MAX_APPENDED_ACKS = 10;
60 private const int RESEND_TIMEOUT = 4000;
61 private const int MAX_SEQUENCE = 0xFFFFFF;
62
63 public void ack_pack(Packet Pack) {
64 //libsecondlife.Packets.PacketAckPacket ack_it = new PacketAckPacket();
65 //ack_it.Packets = new PacketAckPacket.PacketsBlock[1];
66 //ack_it.Packets[0] = new PacketAckPacket.PacketsBlock();
67 //ack_it.Packets[0].ID = Pack.Header.ID;
68 //ack_it.Header.Reliable = false;
69
70 //OutPacket(ack_it);
71
72 if (Pack.Header.Reliable) {
73 lock (PendingAcks) {
74 uint sequence = (uint)Pack.Header.Sequence;
75 if (!PendingAcks.ContainsKey(sequence)) { PendingAcks[sequence] = sequence; }
76 }
77 }
78 }
79
80 public void ProcessInPacket(Packet Pack) {
81 ack_pack(Pack);
82 switch(Pack.Type) {
83 case PacketType.CompleteAgentMovement:
84 ClientAvatar.CompleteMovement(OpenSim_Main.local_world);
85 break;
86 }
87 }
88
89 private void ResendUnacked()
90 {
91 int now = Environment.TickCount;
92
93 lock (NeedAck)
94 {
95 foreach (Packet packet in NeedAck.Values)
96 {
97 if (now - packet.TickCount > RESEND_TIMEOUT)
98 {
99 Console.WriteLine("Resending " + packet.Type.ToString() + " packet, " +
100 (now - packet.TickCount) + "ms have passed", Helpers.LogLevel.Info);
101
102 packet.Header.Resent = true;
103 OutPacket(packet);
104 }
105 }
106 }
107 }
108
109 private void SendAcks()
110 {
111 lock (PendingAcks)
112 {
113 if (PendingAcks.Count > 0)
114 {
115 if (PendingAcks.Count > 250)
116 {
117 // FIXME: Handle the odd case where we have too many pending ACKs queued up
118 Console.WriteLine("Too many ACKs queued up!", Helpers.LogLevel.Error);
119 return;
120 }
121
122 Console.WriteLine("Sending PacketAck");
123
124
125 int i = 0;
126 PacketAckPacket acks = new PacketAckPacket();
127 acks.Packets = new PacketAckPacket.PacketsBlock[PendingAcks.Count];
128
129 foreach (uint ack in PendingAcks.Values)
130 {
131 acks.Packets[i] = new PacketAckPacket.PacketsBlock();
132 acks.Packets[i].ID = ack;
133 i++;
134 }
135
136 acks.Header.Reliable = false;
137 OutPacket(acks);
138
139 PendingAcks.Clear();
140 }
141 }
142 }
143
144 private void AckTimer_Elapsed(object sender, ElapsedEventArgs ea)
145 {
146 SendAcks(); ResendUnacked();
147 }
148
149 public void ProcessOutPacket(Packet Pack) {
150
151 // Keep track of when this packet was sent out
152 Pack.TickCount = Environment.TickCount;
153
154 if (!Pack.Header.Resent)
155 {
156 // Set the sequence number
157 lock (SequenceLock)
158 {
159 if (Sequence >= MAX_SEQUENCE)
160 Sequence = 1;
161 else
162 Sequence++;
163 Pack.Header.Sequence = Sequence;
164 }
165
166 if (Pack.Header.Reliable)
167 {
168 lock (NeedAck)
169 {
170 if (!NeedAck.ContainsKey(Pack.Header.Sequence))
171 {
172 NeedAck.Add(Pack.Header.Sequence, Pack);
173 }
174 else
175 {
176 // Client.Log("Attempted to add a duplicate sequence number (" +
177 // packet.Header.Sequence + ") to the NeedAck dictionary for packet type " +
178 // packet.Type.ToString(), Helpers.LogLevel.Warning);
179 }
180 }
181
182 // Don't append ACKs to resent packets, in case that's what was causing the
183 // delivery to fail
184 if (!Pack.Header.Resent)
185 {
186 // Append any ACKs that need to be sent out to this packet
187 lock (PendingAcks)
188 {
189 if (PendingAcks.Count > 0 && PendingAcks.Count < MAX_APPENDED_ACKS &&
190 Pack.Type != PacketType.PacketAck &&
191 Pack.Type != PacketType.LogoutRequest)
192 {
193 Pack.Header.AckList = new uint[PendingAcks.Count];
194 int i = 0;
195
196 foreach (uint ack in PendingAcks.Values)
197 {
198 Pack.Header.AckList[i] = ack;
199 i++;
200 }
201
202 PendingAcks.Clear();
203 Pack.Header.AppendedAcks = true;
204 }
205 }
206 }
207 }
208 }
209
210 Console.WriteLine("OUT: \n" + Pack.ToString());
211
212 byte[] ZeroOutBuffer = new byte[4096];
213 byte[] sendbuffer;
214 sendbuffer = Pack.ToBytes();
215
216 try {
217 if (Pack.Header.Zerocoded) {
218 int packetsize = Helpers.ZeroEncode(sendbuffer, sendbuffer.Length, ZeroOutBuffer);
219 OpenSim_Main.Server.SendTo(ZeroOutBuffer, packetsize, SocketFlags.None,userEP);
220 } else {
221 OpenSim_Main.Server.SendTo(sendbuffer, sendbuffer.Length, SocketFlags.None,userEP);
222 }
223 } catch (Exception) {
224 Console.WriteLine("OpenSimClient.cs:ProcessOutPacket() - WARNING: Socket exception occurred on connection " + userEP.ToString() + " - killing thread");
225 ClientThread.Abort();
226 }
227
228 }
229
230 public void InPacket(Packet NewPack) {
231 // Handle appended ACKs
232 if (NewPack.Header.AppendedAcks)
233 {
234 lock (NeedAck)
235 {
236 foreach (uint ack in NewPack.Header.AckList)
237 {
238 NeedAck.Remove(ack);
239 }
240 }
241 }
242
243 // Handle PacketAck packets
244 if (NewPack.Type == PacketType.PacketAck)
245 {
246 PacketAckPacket ackPacket = (PacketAckPacket)NewPack;
247
248 lock (NeedAck)
249 {
250 foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets)
251 {
252 NeedAck.Remove(block.ID);
253 }
254 }
255 } else if( ( NewPack.Type == PacketType.StartPingCheck ) ) {
256 //reply to pingcheck
257 libsecondlife.Packets.StartPingCheckPacket startPing = (libsecondlife.Packets.StartPingCheckPacket)NewPack;
258 libsecondlife.Packets.CompletePingCheckPacket endPing = new CompletePingCheckPacket();
259 endPing.PingID.PingID = startPing.PingID.PingID;
260 OutPacket(endPing);
261 }
262 else
263 {
264 QueItem item = new QueItem();
265 item.Packet = NewPack;
266 item.Incoming = true;
267 this.PacketQueue.Enqueue(item);
268 }
269
270 }
271
272 public void OutPacket(Packet NewPack) {
273 QueItem item = new QueItem();
274 item.Packet = NewPack;
275 item.Incoming = false;
276 this.PacketQueue.Enqueue(item);
277 }
278
279 public OpenSimClient(EndPoint remoteEP, UseCircuitCodePacket initialcirpack) {
280 Console.WriteLine("OpenSimClient.cs - Started up new client thread to handle incoming request");
281 cirpack = initialcirpack;
282 userEP = remoteEP;
283 PacketQueue = new BlockingQueue<QueItem>();
284 AckTimer = new System.Timers.Timer(500);
285 AckTimer.Elapsed += new ElapsedEventHandler(AckTimer_Elapsed);
286 AckTimer.Start();
287
288 Thread ClientThread = new Thread(new ThreadStart(AuthUser));
289 ClientThread.IsBackground = true;
290 ClientThread.Start();
291 }
292
293 private void ClientLoop() {
294 Console.WriteLine("OpenSimClient.cs:ClientLoop() - Entered loop");
295 while(true) {
296 QueItem nextPacket = PacketQueue.Dequeue();
297 if(nextPacket.Incoming)
298 {
299 //is a incoming packet
300 ProcessInPacket(nextPacket.Packet);
301 }
302 else
303 {
304 //is a out going packet
305 ProcessOutPacket(nextPacket.Packet);
306 }
307 }
308 }
309
310 private void InitNewClient() {
311 Console.WriteLine("OpenSimClient.cs:InitNewClient() - Adding viewer agent to world");
312 OpenSim_Main.local_world.AddViewerAgent(this);
313 world.Entity tempent=OpenSim_Main.local_world.Entities[this.AgentID];
314 this.ClientAvatar=(world.Avatar)tempent;
315 }
316
317 private void AuthUser() {
318 Console.WriteLine("OpenSimClient.cs:AuthUser() - Authenticating new user request with grid");
319 WebRequest CheckSession = WebRequest.Create(OpenSim_Main.cfg.GridURL + "/usersessions/" + OpenSim_Main.cfg.GridSendKey + "/" + cirpack.CircuitCode.ID.ToString() + "/" + cirpack.CircuitCode.Code.ToString() + "/exists");
320 WebResponse GridResponse = CheckSession.GetResponse();
321 StreamReader sr = new StreamReader(GridResponse.GetResponseStream());
322 String grTest = sr.ReadLine();
323 sr.Close();
324 GridResponse.Close();
325 if(grTest.Equals("1")) { // YAY! Valid login
326 Console.WriteLine("OpenSimClient.cs:AuthUser() - Got authenticated connection from " + userEP.ToString());
327 this.AgentID=cirpack.CircuitCode.ID;
328 this.SessionID=cirpack.CircuitCode.SessionID;
329 this.CircuitCode=cirpack.CircuitCode.Code;
330 InitNewClient();
331 ClientLoop();
332 } else { // Invalid
333 Console.WriteLine("OpenSimClient.cs:AuthUser() - New user request denied to " + userEP.ToString());
334 ClientThread.Abort();
335 }
336 }
337 }
338
339}