aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/OpenSimClient.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/OpenSimClient.cs')
-rw-r--r--src/OpenSimClient.cs387
1 files changed, 387 insertions, 0 deletions
diff --git a/src/OpenSimClient.cs b/src/OpenSimClient.cs
new file mode 100644
index 0000000..8021d59
--- /dev/null
+++ b/src/OpenSimClient.cs
@@ -0,0 +1,387 @@
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;
37using OpenSim.GridServers;
38
39namespace OpenSim
40{
41 /// <summary>
42 /// Handles new client connections
43 /// Constructor takes a single Packet and authenticates everything
44 /// </summary>
45 public class OpenSimClient {
46
47 public LLUUID AgentID;
48 public LLUUID SessionID;
49 public uint CircuitCode;
50 public world.Avatar ClientAvatar;
51 private UseCircuitCodePacket cirpack;
52 private Thread ClientThread;
53 public EndPoint userEP;
54 private BlockingQueue<QueItem> PacketQueue;
55 private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>();
56 private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>();
57 private System.Timers.Timer AckTimer;
58 private uint Sequence = 0;
59 private object SequenceLock = new object();
60 private const int MAX_APPENDED_ACKS = 10;
61 private const int RESEND_TIMEOUT = 4000;
62 private const int MAX_SEQUENCE = 0xFFFFFF;
63
64 public void ack_pack(Packet Pack) {
65 //libsecondlife.Packets.PacketAckPacket ack_it = new PacketAckPacket();
66 //ack_it.Packets = new PacketAckPacket.PacketsBlock[1];
67 //ack_it.Packets[0] = new PacketAckPacket.PacketsBlock();
68 //ack_it.Packets[0].ID = Pack.Header.ID;
69 //ack_it.Header.Reliable = false;
70
71 //OutPacket(ack_it);
72
73 if (Pack.Header.Reliable) {
74 lock (PendingAcks) {
75 uint sequence = (uint)Pack.Header.Sequence;
76 if (!PendingAcks.ContainsKey(sequence)) { PendingAcks[sequence] = sequence; }
77 }
78 }
79 }
80
81 public void ProcessInPacket(Packet Pack) {
82 ack_pack(Pack);
83 switch(Pack.Type) {
84 case PacketType.CompleteAgentMovement:
85 ClientAvatar.CompleteMovement(OpenSim_Main.local_world);
86 ClientAvatar.SendInitialPosition();
87 break;
88 case PacketType.RegionHandshakeReply:
89 OpenSim_Main.local_world.SendLayerData(this);
90 break;
91 case PacketType.AgentWearablesRequest:
92 ClientAvatar.SendInitialAppearance();
93 break;
94 case PacketType.ObjectAdd:
95 OpenSim_Main.local_world.AddNewPrim((ObjectAddPacket)Pack, this);
96 break;
97 case PacketType.TransferRequest:
98 //Console.WriteLine("OpenSimClient.cs:ProcessInPacket() - Got transfer request");
99 TransferRequestPacket transfer = (TransferRequestPacket)Pack;
100 OpenSim_Main.sim.assetCache.AddAssetRequest(this, transfer);
101 break;
102 case PacketType.AgentUpdate:
103 ClientAvatar.HandleUpdate((AgentUpdatePacket)Pack);
104 break;
105 case PacketType.LogoutRequest:
106 ServerConsole.MainConsole.Instance.WriteLine("OpenSimClient.cs:ProcessInPacket() - Got a logout request");
107 lock(OpenSim_Main.local_world.Entities) {
108 OpenSim_Main.local_world.Entities.Remove(this.AgentID);
109 }
110 //need to do other cleaning up here too
111 OpenSim_Main.gridServers.GridServer.LogoutSession(cirpack.CircuitCode.SessionID, cirpack.CircuitCode.ID, cirpack.CircuitCode.Code);
112 this.ClientThread.Abort();
113 break;
114 case PacketType.ChatFromViewer:
115 ChatFromViewerPacket inchatpack = (ChatFromViewerPacket)Pack;
116 if(Helpers.FieldToString(inchatpack.ChatData.Message)=="") break;
117
118 System.Text.Encoding _enc = System.Text.Encoding.ASCII;
119 libsecondlife.Packets.ChatFromSimulatorPacket reply = new ChatFromSimulatorPacket();
120 reply.ChatData.Audible = 1;
121 reply.ChatData.Message = inchatpack.ChatData.Message;
122 reply.ChatData.ChatType = 1;
123 reply.ChatData.SourceType = 1;
124 reply.ChatData.Position = this.ClientAvatar.position;
125 reply.ChatData.FromName = _enc.GetBytes(this.ClientAvatar.firstname + " " + this.ClientAvatar.lastname + "\0");
126 reply.ChatData.OwnerID = this.AgentID;
127 reply.ChatData.SourceID = this.AgentID;
128 foreach(OpenSimClient client in OpenSim_Main.sim.ClientThreads.Values) {
129 client.OutPacket(reply);
130 }
131 break;
132 }
133 }
134
135 private void ResendUnacked()
136 {
137 int now = Environment.TickCount;
138
139 lock (NeedAck)
140 {
141 foreach (Packet packet in NeedAck.Values)
142 {
143 if (now - packet.TickCount > RESEND_TIMEOUT)
144 {
145 ServerConsole.MainConsole.Instance.WriteLine("Resending " + packet.Type.ToString() + " packet, " +
146 (now - packet.TickCount) + "ms have passed");
147
148 packet.Header.Resent = true;
149 OutPacket(packet);
150 }
151 }
152 }
153 }
154
155 private void SendAcks()
156 {
157 lock (PendingAcks)
158 {
159 if (PendingAcks.Count > 0)
160 {
161 if (PendingAcks.Count > 250)
162 {
163 // FIXME: Handle the odd case where we have too many pending ACKs queued up
164 ServerConsole.MainConsole.Instance.WriteLine("Too many ACKs queued up!");
165 return;
166 }
167
168 ServerConsole.MainConsole.Instance.WriteLine("Sending PacketAck");
169
170
171 int i = 0;
172 PacketAckPacket acks = new PacketAckPacket();
173 acks.Packets = new PacketAckPacket.PacketsBlock[PendingAcks.Count];
174
175 foreach (uint ack in PendingAcks.Values)
176 {
177 acks.Packets[i] = new PacketAckPacket.PacketsBlock();
178 acks.Packets[i].ID = ack;
179 i++;
180 }
181
182 acks.Header.Reliable = false;
183 OutPacket(acks);
184
185 PendingAcks.Clear();
186 }
187 }
188 }
189
190 private void AckTimer_Elapsed(object sender, ElapsedEventArgs ea)
191 {
192 SendAcks();
193 ResendUnacked();
194 }
195
196 public void ProcessOutPacket(Packet Pack) {
197
198 // Keep track of when this packet was sent out
199 Pack.TickCount = Environment.TickCount;
200
201 if (!Pack.Header.Resent)
202 {
203 // Set the sequence number
204 lock (SequenceLock)
205 {
206 if (Sequence >= MAX_SEQUENCE)
207 Sequence = 1;
208 else
209 Sequence++;
210 Pack.Header.Sequence = Sequence;
211 }
212
213 if (Pack.Header.Reliable) //DIRTY HACK
214 {
215 lock (NeedAck)
216 {
217 if (!NeedAck.ContainsKey(Pack.Header.Sequence))
218 {
219 NeedAck.Add(Pack.Header.Sequence, Pack);
220 }
221 else
222 {
223 // Client.Log("Attempted to add a duplicate sequence number (" +
224 // packet.Header.Sequence + ") to the NeedAck dictionary for packet type " +
225 // packet.Type.ToString(), Helpers.LogLevel.Warning);
226 }
227 }
228
229 // Don't append ACKs to resent packets, in case that's what was causing the
230 // delivery to fail
231 if (!Pack.Header.Resent)
232 {
233 // Append any ACKs that need to be sent out to this packet
234 lock (PendingAcks)
235 {
236 if (PendingAcks.Count > 0 && PendingAcks.Count < MAX_APPENDED_ACKS &&
237 Pack.Type != PacketType.PacketAck &&
238 Pack.Type != PacketType.LogoutRequest)
239 {
240 Pack.Header.AckList = new uint[PendingAcks.Count];
241 int i = 0;
242
243 foreach (uint ack in PendingAcks.Values)
244 {
245 Pack.Header.AckList[i] = ack;
246 i++;
247 }
248
249 PendingAcks.Clear();
250 Pack.Header.AppendedAcks = true;
251 }
252 }
253 }
254 }
255 }
256
257
258 byte[] ZeroOutBuffer = new byte[4096];
259 byte[] sendbuffer;
260 sendbuffer = Pack.ToBytes();
261
262 try {
263 if (Pack.Header.Zerocoded) {
264 int packetsize = Helpers.ZeroEncode(sendbuffer, sendbuffer.Length, ZeroOutBuffer);
265 OpenSim_Main.Server.SendTo(ZeroOutBuffer, packetsize, SocketFlags.None,userEP);
266 } else {
267 OpenSim_Main.Server.SendTo(sendbuffer, sendbuffer.Length, SocketFlags.None,userEP);
268 }
269 } catch (Exception) {
270 ServerConsole.MainConsole.Instance.WriteLine("OpenSimClient.cs:ProcessOutPacket() - WARNING: Socket exception occurred on connection " + userEP.ToString() + " - killing thread");
271 ClientThread.Abort();
272 }
273
274 }
275
276 public void InPacket(Packet NewPack) {
277 // Handle appended ACKs
278 if (NewPack.Header.AppendedAcks)
279 {
280 lock (NeedAck)
281 {
282 foreach (uint ack in NewPack.Header.AckList)
283 {
284 NeedAck.Remove(ack);
285 }
286 }
287 }
288
289 // Handle PacketAck packets
290 if (NewPack.Type == PacketType.PacketAck)
291 {
292 PacketAckPacket ackPacket = (PacketAckPacket)NewPack;
293
294 lock (NeedAck)
295 {
296 foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets)
297 {
298 NeedAck.Remove(block.ID);
299 }
300 }
301 } else if( ( NewPack.Type == PacketType.StartPingCheck ) ) {
302 //reply to pingcheck
303 libsecondlife.Packets.StartPingCheckPacket startPing = (libsecondlife.Packets.StartPingCheckPacket)NewPack;
304 libsecondlife.Packets.CompletePingCheckPacket endPing = new CompletePingCheckPacket();
305 endPing.PingID.PingID = startPing.PingID.PingID;
306 OutPacket(endPing);
307 }
308 else
309 {
310 QueItem item = new QueItem();
311 item.Packet = NewPack;
312 item.Incoming = true;
313 this.PacketQueue.Enqueue(item);
314 }
315
316 }
317
318 public void OutPacket(Packet NewPack) {
319 QueItem item = new QueItem();
320 item.Packet = NewPack;
321 item.Incoming = false;
322 this.PacketQueue.Enqueue(item);
323 }
324
325 public OpenSimClient(EndPoint remoteEP, UseCircuitCodePacket initialcirpack) {
326 ServerConsole.MainConsole.Instance.WriteLine("OpenSimClient.cs - Started up new client thread to handle incoming request");
327 cirpack = initialcirpack;
328 userEP = remoteEP;
329 PacketQueue = new BlockingQueue<QueItem>();
330 AckTimer = new System.Timers.Timer(500);
331 AckTimer.Elapsed += new ElapsedEventHandler(AckTimer_Elapsed);
332 AckTimer.Start();
333
334 ClientThread = new Thread(new ThreadStart(AuthUser));
335 ClientThread.IsBackground = true;
336 ClientThread.Start();
337 }
338
339 private void ClientLoop() {
340 ServerConsole.MainConsole.Instance.WriteLine("OpenSimClient.cs:ClientLoop() - Entered loop");
341 while(true) {
342 QueItem nextPacket = PacketQueue.Dequeue();
343 if(nextPacket.Incoming)
344 {
345 //is a incoming packet
346 ProcessInPacket(nextPacket.Packet);
347 }
348 else
349 {
350 //is a out going packet
351 ProcessOutPacket(nextPacket.Packet);
352 }
353 }
354 }
355
356 private void InitNewClient() {
357 ServerConsole.MainConsole.Instance.WriteLine("OpenSimClient.cs:InitNewClient() - Adding viewer agent to world");
358 OpenSim_Main.local_world.AddViewerAgent(this);
359 world.Entity tempent=OpenSim_Main.local_world.Entities[this.AgentID];
360 this.ClientAvatar=(world.Avatar)tempent;
361 }
362
363 private void AuthUser()
364 {
365 AuthenticateResponse sessionInfo = OpenSim_Main.gridServers.GridServer.AuthenticateSession(cirpack.CircuitCode.SessionID, cirpack.CircuitCode.ID, cirpack.CircuitCode.Code);
366 if(!sessionInfo.Authorised)
367 {
368 //session/circuit not authorised
369 ServerConsole.MainConsole.Instance.WriteLine("OpenSimClient.cs:AuthUser() - New user request denied to " + userEP.ToString());
370 ClientThread.Abort();
371 }
372 else
373 {
374 ServerConsole.MainConsole.Instance.WriteLine("OpenSimClient.cs:AuthUser() - Got authenticated connection from " + userEP.ToString());
375 //session is authorised
376 this.AgentID=cirpack.CircuitCode.ID;
377 this.SessionID=cirpack.CircuitCode.SessionID;
378 this.CircuitCode=cirpack.CircuitCode.Code;
379 InitNewClient();
380 this.ClientAvatar.firstname = sessionInfo.LoginInfo.First;
381 this.ClientAvatar.lastname = sessionInfo.LoginInfo.Last;
382 ClientLoop();
383 }
384 }
385 }
386
387}