diff options
Diffstat (limited to 'src/Server.cs')
-rw-r--r-- | src/Server.cs | 707 |
1 files changed, 707 insertions, 0 deletions
diff --git a/src/Server.cs b/src/Server.cs new file mode 100644 index 0000000..1f19bf9 --- /dev/null +++ b/src/Server.cs | |||
@@ -0,0 +1,707 @@ | |||
1 | /* | ||
2 | * Copyright (c) OpenSim project, http://osgrid.org/ | ||
3 | * All rights reserved. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the <organization> nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using libsecondlife; | ||
31 | using System.Collections; | ||
32 | using libsecondlife.Packets; | ||
33 | using libsecondlife.AssetSystem; | ||
34 | using System.Net; | ||
35 | using System.Net.Sockets; | ||
36 | using System.Timers; | ||
37 | |||
38 | //really hacked , messy code | ||
39 | |||
40 | namespace OpenSim | ||
41 | { | ||
42 | /// <summary> | ||
43 | /// Description of Server. | ||
44 | /// </summary> | ||
45 | public interface ServerCallback | ||
46 | { | ||
47 | //should replace with delegates | ||
48 | void MainCallback(Packet pack, UserAgentInfo User_info); | ||
49 | void NewUserCallback(UserAgentInfo User_info); | ||
50 | void ErrorCallback(string text); | ||
51 | } | ||
52 | public class Server | ||
53 | { | ||
54 | /// <summary>A public reference to the client that this Simulator object | ||
55 | /// is attached to</summary> | ||
56 | //public SecondLife Client; | ||
57 | |||
58 | /// <summary>The Region class that this Simulator wraps</summary> | ||
59 | // public Region Region; | ||
60 | |||
61 | /// <summary> | ||
62 | /// Used internally to track sim disconnections, do not modify this | ||
63 | /// variable | ||
64 | /// </summary> | ||
65 | public bool DisconnectCandidate = false; | ||
66 | |||
67 | /// <summary> | ||
68 | /// The ID number associated with this particular connection to the | ||
69 | /// simulator, used to emulate TCP connections. This is used | ||
70 | /// internally for packets that have a CircuitCode field | ||
71 | /// </summary> | ||
72 | public uint CircuitCode | ||
73 | { | ||
74 | get { return circuitCode; } | ||
75 | set { circuitCode = value; } | ||
76 | } | ||
77 | |||
78 | /// <summary> | ||
79 | /// The IP address and port of the server | ||
80 | /// </summary> | ||
81 | public IPEndPoint IPEndPoint | ||
82 | { | ||
83 | get { return ipEndPoint; } | ||
84 | } | ||
85 | |||
86 | /// <summary> | ||
87 | /// A boolean representing whether there is a working connection to the | ||
88 | /// simulator or not | ||
89 | /// </summary> | ||
90 | public bool Connected | ||
91 | { | ||
92 | get { return connected; } | ||
93 | } | ||
94 | |||
95 | private ServerCallback CallbackObject; | ||
96 | private uint Sequence = 0; | ||
97 | private object SequenceLock = new object(); | ||
98 | private byte[] RecvBuffer = new byte[4096]; | ||
99 | private byte[] ZeroBuffer = new byte[8192]; | ||
100 | private byte[] ZeroOutBuffer = new byte[4096]; | ||
101 | private Socket Connection = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); | ||
102 | private AsyncCallback ReceivedData; | ||
103 | private bool connected = false; | ||
104 | private uint circuitCode; | ||
105 | private IPEndPoint ipEndPoint; | ||
106 | private EndPoint endPoint; | ||
107 | private IPEndPoint ipeSender; | ||
108 | private EndPoint epSender; | ||
109 | private System.Timers.Timer AckTimer; | ||
110 | private Server_Settings Settings=new Server_Settings(); | ||
111 | public ArrayList User_agents=new ArrayList(); | ||
112 | |||
113 | /// <summary> | ||
114 | /// Constructor for Simulator | ||
115 | /// </summary> | ||
116 | /// <param name="client"></param> | ||
117 | /// <param name="callbacks"></param> | ||
118 | /// <param name="circuit"></param> | ||
119 | /// <param name="ip"></param> | ||
120 | /// <param name="port"></param> | ||
121 | public Server(ServerCallback s_callback) | ||
122 | { | ||
123 | |||
124 | this.CallbackObject=s_callback; //should be using delegate | ||
125 | AckTimer = new System.Timers.Timer(Settings.NETWORK_TICK_LENGTH); | ||
126 | AckTimer.Elapsed += new ElapsedEventHandler(AckTimer_Elapsed); | ||
127 | |||
128 | // Initialize the callback for receiving a new packet | ||
129 | ReceivedData = new AsyncCallback(this.OnReceivedData); | ||
130 | |||
131 | // Client.Log("Connecting to " + ip.ToString() + ":" + port, Helpers.LogLevel.Info); | ||
132 | |||
133 | try | ||
134 | { | ||
135 | // Create an endpoint that we will be communicating with (need it in two | ||
136 | // types due to .NET weirdness) | ||
137 | // ipEndPoint = new IPEndPoint(ip, port); | ||
138 | ipEndPoint = new IPEndPoint(IPAddress.Any, Globals.Instance.IpPort); | ||
139 | endPoint = (EndPoint)ipEndPoint; | ||
140 | |||
141 | // Associate this simulator's socket with the given ip/port and start listening | ||
142 | Connection.Bind(endPoint); | ||
143 | ipeSender = new IPEndPoint(IPAddress.Any, 0); | ||
144 | //The epSender identifies the incoming clients | ||
145 | epSender = (EndPoint) ipeSender; | ||
146 | Connection.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, ReceivedData, null); | ||
147 | |||
148 | // Start the ACK timer | ||
149 | AckTimer.Start(); | ||
150 | } | ||
151 | catch (Exception e) | ||
152 | { | ||
153 | |||
154 | System.Console.WriteLine(e.Message); | ||
155 | } | ||
156 | } | ||
157 | |||
158 | /// <summary> | ||
159 | /// Disconnect a Simulator | ||
160 | /// </summary> | ||
161 | public void Disconnect() | ||
162 | { | ||
163 | if (connected) | ||
164 | { | ||
165 | connected = false; | ||
166 | AckTimer.Stop(); | ||
167 | |||
168 | // Send the CloseCircuit notice | ||
169 | CloseCircuitPacket close = new CloseCircuitPacket(); | ||
170 | |||
171 | if (Connection.Connected) | ||
172 | { | ||
173 | try | ||
174 | { | ||
175 | // Connection.Send(close.ToBytes()); | ||
176 | } | ||
177 | catch (SocketException) | ||
178 | { | ||
179 | // There's a high probability of this failing if the network is | ||
180 | // disconnecting, so don't even bother logging the error | ||
181 | } | ||
182 | } | ||
183 | |||
184 | try | ||
185 | { | ||
186 | // Shut the socket communication down | ||
187 | // Connection.Shutdown(SocketShutdown.Both); | ||
188 | } | ||
189 | catch (SocketException) | ||
190 | { | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | |||
195 | /// <summary> | ||
196 | /// Sends a packet | ||
197 | /// </summary> | ||
198 | /// <param name="packet">Packet to be sent</param> | ||
199 | /// <param name="incrementSequence">Increment sequence number?</param> | ||
200 | public void SendPacket(Packet packet, bool incrementSequence, UserAgentInfo User_info) | ||
201 | { | ||
202 | Console.WriteLine("OUTGOING"); | ||
203 | Console.WriteLine(packet.ToString()); | ||
204 | byte[] buffer; | ||
205 | int bytes; | ||
206 | |||
207 | if (!connected && packet.Type != PacketType.UseCircuitCode) | ||
208 | { | ||
209 | Console.WriteLine("Trying to send a " + packet.Type.ToString() + " packet when the socket is closed"); | ||
210 | |||
211 | |||
212 | return; | ||
213 | } | ||
214 | |||
215 | /*if (packet.Header.AckList.Length > 0) | ||
216 | { | ||
217 | // Scrub any appended ACKs since all of the ACK handling is done here | ||
218 | packet.Header.AckList = new uint[0]; | ||
219 | } | ||
220 | packet.Header.AppendedAcks = false; | ||
221 | |||
222 | // Keep track of when this packet was sent out | ||
223 | packet.TickCount = Environment.TickCount; | ||
224 | */ | ||
225 | if (incrementSequence) | ||
226 | { | ||
227 | // Set the sequence number | ||
228 | lock (SequenceLock) | ||
229 | { | ||
230 | if (Sequence > Settings.MAX_SEQUENCE) | ||
231 | Sequence = 1; | ||
232 | else | ||
233 | Sequence++; | ||
234 | packet.Header.Sequence = Sequence; | ||
235 | } | ||
236 | |||
237 | if (packet.Header.Reliable) | ||
238 | { | ||
239 | lock (User_info.NeedAck) | ||
240 | { | ||
241 | if (!User_info.NeedAck.ContainsKey(packet.Header.Sequence)) | ||
242 | { | ||
243 | User_info.NeedAck.Add(packet.Header.Sequence, packet); | ||
244 | } | ||
245 | else | ||
246 | { | ||
247 | // Client.Log("Attempted to add a duplicate sequence number (" + | ||
248 | // packet.Header.Sequence + ") to the NeedAck dictionary for packet type " + | ||
249 | // packet.Type.ToString(), Helpers.LogLevel.Warning); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | // Don't append ACKs to resent packets, in case that's what was causing the | ||
254 | // delivery to fail | ||
255 | if (!packet.Header.Resent) | ||
256 | { | ||
257 | // Append any ACKs that need to be sent out to this packet | ||
258 | lock (User_info.PendingAcks) | ||
259 | { | ||
260 | if (User_info.PendingAcks.Count > 0 && User_info.PendingAcks.Count < Settings.MAX_APPENDED_ACKS && | ||
261 | packet.Type != PacketType.PacketAck && | ||
262 | packet.Type != PacketType.LogoutRequest) | ||
263 | { | ||
264 | packet.Header.AckList = new uint[User_info.PendingAcks.Count]; | ||
265 | int i = 0; | ||
266 | |||
267 | foreach (uint ack in User_info.PendingAcks.Values) | ||
268 | { | ||
269 | packet.Header.AckList[i] = ack; | ||
270 | i++; | ||
271 | } | ||
272 | |||
273 | User_info.PendingAcks.Clear(); | ||
274 | packet.Header.AppendedAcks = true; | ||
275 | } | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | |||
281 | // Serialize the packet | ||
282 | buffer = packet.ToBytes(); | ||
283 | bytes = buffer.Length; | ||
284 | |||
285 | try | ||
286 | { | ||
287 | // Zerocode if needed | ||
288 | if (packet.Header.Zerocoded) | ||
289 | { | ||
290 | lock (ZeroOutBuffer) | ||
291 | { | ||
292 | bytes = Helpers.ZeroEncode(buffer, bytes, ZeroOutBuffer); | ||
293 | Connection.SendTo(ZeroOutBuffer, bytes, SocketFlags.None,User_info.endpoint); | ||
294 | } | ||
295 | } | ||
296 | else | ||
297 | { | ||
298 | |||
299 | Connection.SendTo(buffer, bytes, SocketFlags.None,User_info.endpoint); | ||
300 | } | ||
301 | } | ||
302 | catch (SocketException) | ||
303 | { | ||
304 | //Client.Log("Tried to send a " + packet.Type.ToString() + " on a closed socket", | ||
305 | // Helpers.LogLevel.Warning); | ||
306 | |||
307 | Disconnect(); | ||
308 | } | ||
309 | } | ||
310 | |||
311 | /// <summary> | ||
312 | /// Send a raw byte array payload as a packet | ||
313 | /// </summary> | ||
314 | /// <param name="payload">The packet payload</param> | ||
315 | /// <param name="setSequence">Whether the second, third, and fourth bytes | ||
316 | /// should be modified to the current stream sequence number</param> | ||
317 | /// <summary> | ||
318 | /// Returns Simulator Name as a String | ||
319 | /// </summary> | ||
320 | /// <returns></returns> | ||
321 | public override string ToString() | ||
322 | { | ||
323 | return( " (" + ipEndPoint.ToString() + ")"); | ||
324 | } | ||
325 | |||
326 | /// <summary> | ||
327 | /// Sends out pending acknowledgements | ||
328 | /// </summary> | ||
329 | private void SendAcks(UserAgentInfo User_info) | ||
330 | { | ||
331 | lock (User_info.PendingAcks) | ||
332 | { | ||
333 | if (connected && User_info.PendingAcks.Count > 0) | ||
334 | { | ||
335 | if (User_info.PendingAcks.Count > 250) | ||
336 | { | ||
337 | // FIXME: Handle the odd case where we have too many pending ACKs queued up | ||
338 | //Client.Log("Too many ACKs queued up!", Helpers.LogLevel.Error); | ||
339 | return; | ||
340 | } | ||
341 | |||
342 | int i = 0; | ||
343 | PacketAckPacket acks = new PacketAckPacket(); | ||
344 | acks.Packets = new PacketAckPacket.PacketsBlock[User_info.PendingAcks.Count]; | ||
345 | |||
346 | foreach (uint ack in User_info.PendingAcks.Values) | ||
347 | { | ||
348 | acks.Packets[i] = new PacketAckPacket.PacketsBlock(); | ||
349 | acks.Packets[i].ID = ack; | ||
350 | i++; | ||
351 | } | ||
352 | |||
353 | acks.Header.Reliable = false; | ||
354 | //SendPacket(acks, true,User_info); | ||
355 | |||
356 | User_info.PendingAcks.Clear(); | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | /// <summary> | ||
361 | /// Resend unacknowledged packets | ||
362 | /// </summary> | ||
363 | private void ResendUnacked(UserAgentInfo User_info) | ||
364 | { | ||
365 | if (connected) | ||
366 | { | ||
367 | int now = Environment.TickCount; | ||
368 | |||
369 | lock (User_info.NeedAck) | ||
370 | { | ||
371 | foreach (Packet packet in User_info.NeedAck.Values) | ||
372 | { | ||
373 | if (now - packet.TickCount > Settings.RESEND_TIMEOUT) | ||
374 | { | ||
375 | // Client.Log("Resending " + packet.Type.ToString() + " packet, " + | ||
376 | // (now - packet.TickCount) + "ms have passed", Helpers.LogLevel.Info); | ||
377 | |||
378 | //packet.Header.Resent = true; | ||
379 | // SendPacket(packet, false,User_info); | ||
380 | } | ||
381 | } | ||
382 | } | ||
383 | } | ||
384 | } | ||
385 | /// <summary> | ||
386 | /// Callback handler for incomming data | ||
387 | /// </summary> | ||
388 | /// <param name="result"></param> | ||
389 | private void OnReceivedData(IAsyncResult result) | ||
390 | { | ||
391 | ipeSender = new IPEndPoint(IPAddress.Any, 0); | ||
392 | epSender = (EndPoint)ipeSender; | ||
393 | Packet packet = null; | ||
394 | int numBytes; | ||
395 | UserAgentInfo tempinfo; | ||
396 | |||
397 | // If we're receiving data the sim connection is open | ||
398 | connected = true; | ||
399 | |||
400 | // Update the disconnect flag so this sim doesn't time out | ||
401 | DisconnectCandidate = false; | ||
402 | UserAgentInfo User_info=null; | ||
403 | |||
404 | lock (RecvBuffer) | ||
405 | { | ||
406 | // Retrieve the incoming packet | ||
407 | try | ||
408 | { | ||
409 | numBytes = Connection.EndReceiveFrom(result, ref epSender); | ||
410 | |||
411 | //find user_agent_info | ||
412 | |||
413 | int packetEnd = numBytes - 1; | ||
414 | packet = Packet.BuildPacket(RecvBuffer, ref packetEnd, ZeroBuffer); | ||
415 | |||
416 | Console.WriteLine("INCOMING PACKET" + packet.TickCount.ToString() + " " + packet.Header.Sequence.ToString()); | ||
417 | Console.WriteLine(packet.ToString()); | ||
418 | libsecondlife.Packets.PacketAckPacket ack_it = new PacketAckPacket(); | ||
419 | ack_it.Packets = new PacketAckPacket.PacketsBlock[1]; | ||
420 | ack_it.Packets[0] = new PacketAckPacket.PacketsBlock(); | ||
421 | ack_it.Packets[0].ID = packet.Header.ID; | ||
422 | ack_it.Header.Reliable = false; | ||
423 | |||
424 | tempinfo = new UserAgentInfo(); | ||
425 | tempinfo.endpoint = epSender; | ||
426 | this.SendPacket(ack_it, false, tempinfo); | ||
427 | if (packet.Header.Resent) | ||
428 | { | ||
429 | this.CallbackObject.ErrorCallback("resent"); | ||
430 | } | ||
431 | |||
432 | if ((packet.Type == PacketType.StartPingCheck) && (packet.Header.Resent == false)) | ||
433 | { | ||
434 | //reply to pingcheck | ||
435 | libsecondlife.Packets.StartPingCheckPacket startping = (libsecondlife.Packets.StartPingCheckPacket)packet; | ||
436 | libsecondlife.Packets.CompletePingCheckPacket endping = new CompletePingCheckPacket(); | ||
437 | endping.PingID.PingID = startping.PingID.PingID; | ||
438 | endping.Header.Reliable = false; | ||
439 | tempinfo = new UserAgentInfo(); | ||
440 | tempinfo.endpoint = epSender; | ||
441 | this.SendPacket(endping, true, tempinfo); | ||
442 | } | ||
443 | |||
444 | //should check if login/useconnection packet first | ||
445 | if ((packet.Type == PacketType.UseCircuitCode) && (packet.Header.Resent == false)) | ||
446 | { | ||
447 | Console.WriteLine("Got UseCircuitCode, confirming with grid..."); | ||
448 | UseCircuitCodePacket cir_pack=(UseCircuitCodePacket)packet; | ||
449 | |||
450 | ArrayList requestParams = new ArrayList(); | ||
451 | requestParams.Add(Globals.Instance.GridSendKey); | ||
452 | requestParams.Add(cir_pack.CircuitCode.SessionID.ToString()); | ||
453 | requestParams.Add(cir_pack.CircuitCode.ID.ToString()); | ||
454 | |||
455 | |||
456 | Nwc.XmlRpc.XmlRpcRequest GridSessionInfo = new Nwc.XmlRpc.XmlRpcRequest("get_session_info",requestParams); | ||
457 | |||
458 | Nwc.XmlRpc.XmlRpcResponse gridresponse = GridSessionInfo.Send(Globals.Instance.GridURL, 5000); | ||
459 | |||
460 | Console.WriteLine("Processing response from grid server..."); | ||
461 | Hashtable gridreply = (Hashtable)gridresponse.Value; | ||
462 | if (gridresponse.IsFault) | ||
463 | { | ||
464 | Console.WriteLine("XML-RPC error when talking to grid: " + gridresponse.FaultString); | ||
465 | Connection.Disconnect(false); | ||
466 | } | ||
467 | if (((string)gridreply["agent_id"]).ToLower().Equals(cir_pack.CircuitCode.ID.ToString()) == false) | ||
468 | { | ||
469 | Console.WriteLine("Bad agent ID!"); | ||
470 | Connection.Disconnect(false); | ||
471 | } | ||
472 | else if (((string)gridreply["session_id"]).ToLower().Equals(cir_pack.CircuitCode.SessionID.ToString()) == false) | ||
473 | { | ||
474 | Console.WriteLine("Bad session ID!"); | ||
475 | Connection.Disconnect(false); | ||
476 | } | ||
477 | UserAgentInfo new_user = new UserAgentInfo(); | ||
478 | |||
479 | new_user.AgentID = cir_pack.CircuitCode.ID; | ||
480 | new_user.circuitCode=cir_pack.CircuitCode.Code; | ||
481 | new_user.AgentID=cir_pack.CircuitCode.ID; | ||
482 | new_user.SessionID=cir_pack.CircuitCode.SessionID; | ||
483 | new_user.endpoint=epSender; | ||
484 | new_user.Inbox = new Queue<uint>(Settings.INBOX_SIZE); | ||
485 | new_user.first_name = (string)gridreply["firstname"]; | ||
486 | new_user.first_name = (string)gridreply["lastname"]; | ||
487 | |||
488 | |||
489 | this.CallbackObject.NewUserCallback(new_user); | ||
490 | this.User_agents.Add(new_user); | ||
491 | |||
492 | } | ||
493 | |||
494 | |||
495 | UserAgentInfo temp_agent=null; | ||
496 | IPEndPoint send_ip=(IPEndPoint)epSender; | ||
497 | Console.WriteLine("incoming: address is "+send_ip.Address +"port number is: "+send_ip.Port.ToString()); | ||
498 | |||
499 | for(int ii=0; ii<this.User_agents.Count ; ii++) | ||
500 | { | ||
501 | temp_agent=(UserAgentInfo)this.User_agents[ii]; | ||
502 | IPEndPoint ag_ip=(IPEndPoint)temp_agent.endpoint; | ||
503 | Console.WriteLine("searching: address is "+ag_ip.Address +"port number is: "+ag_ip.Port.ToString()); | ||
504 | |||
505 | if((ag_ip.Address.ToString()==send_ip.Address.ToString()) && (ag_ip.Port.ToString()==send_ip.Port.ToString())) | ||
506 | { | ||
507 | Console.WriteLine("found user"); | ||
508 | User_info=temp_agent; | ||
509 | break; | ||
510 | } | ||
511 | } | ||
512 | |||
513 | Connection.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, ReceivedData, null); | ||
514 | } | ||
515 | catch (SocketException) | ||
516 | { | ||
517 | // Client.Log(endPoint.ToString() + " socket is closed, shutting down " + this.Region.Name, | ||
518 | // Helpers.LogLevel.Info); | ||
519 | |||
520 | connected = false; | ||
521 | //Network.DisconnectSim(this); | ||
522 | return; | ||
523 | } | ||
524 | } | ||
525 | if(User_info==null) | ||
526 | { | ||
527 | this.CallbackObject.ErrorCallback("no user found"); | ||
528 | return; | ||
529 | } | ||
530 | |||
531 | // Fail-safe check | ||
532 | if (packet == null) | ||
533 | { | ||
534 | this.CallbackObject.ErrorCallback("couldn't build packet"); | ||
535 | // Client.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning); | ||
536 | return; | ||
537 | } | ||
538 | //this.callback_object.error("past tests"); | ||
539 | // Track the sequence number for this packet if it's marked as reliable | ||
540 | if (packet.Header.Reliable) | ||
541 | { | ||
542 | if (User_info.PendingAcks.Count > Settings.MAX_PENDING_ACKS) | ||
543 | { | ||
544 | SendAcks(User_info); | ||
545 | } | ||
546 | |||
547 | // Check if we already received this packet | ||
548 | if (User_info.Inbox.Contains(packet.Header.Sequence)) | ||
549 | { | ||
550 | //Client.Log("Received a duplicate " + packet.Type.ToString() + ", sequence=" + | ||
551 | // packet.Header.Sequence + ", resent=" + ((packet.Header.Resent) ? "Yes" : "No") + | ||
552 | // ", Inbox.Count=" + Inbox.Count + ", NeedAck.Count=" + NeedAck.Count, | ||
553 | // Helpers.LogLevel.Info); | ||
554 | |||
555 | // Send an ACK for this packet immediately | ||
556 | |||
557 | |||
558 | // TESTING: Try just queuing up ACKs for resent packets instead of immediately triggering an ACK | ||
559 | /* lock (User_info.PendingAcks) | ||
560 | { | ||
561 | uint sequence = (uint)packet.Header.Sequence; | ||
562 | if (!User_info.PendingAcks.ContainsKey(sequence)) { User_info.PendingAcks[sequence] = sequence; } | ||
563 | }*/ | ||
564 | |||
565 | // Avoid firing a callback twice for the same packet | ||
566 | this.CallbackObject.ErrorCallback("Avoiding callback"); | ||
567 | // this.callback_object.error("avoiding callback"); | ||
568 | return; | ||
569 | } | ||
570 | else | ||
571 | { | ||
572 | lock (User_info.PendingAcks) | ||
573 | { | ||
574 | uint sequence = (uint)packet.Header.Sequence; | ||
575 | // if (!User_info.PendingAcks.ContainsKey(sequence)) { User_info.PendingAcks[sequence] = sequence; } | ||
576 | } | ||
577 | } | ||
578 | } | ||
579 | |||
580 | // Add this packet to our inbox | ||
581 | lock (User_info.Inbox) | ||
582 | { | ||
583 | while (User_info.Inbox.Count >= Settings.INBOX_SIZE) | ||
584 | { | ||
585 | User_info.Inbox.Dequeue(); | ||
586 | } | ||
587 | User_info.Inbox.Enqueue(packet.Header.Sequence); | ||
588 | } | ||
589 | |||
590 | // Handle appended ACKs | ||
591 | if (packet.Header.AppendedAcks) | ||
592 | { | ||
593 | lock (User_info.NeedAck) | ||
594 | { | ||
595 | foreach (uint ack in packet.Header.AckList) | ||
596 | { | ||
597 | User_info.NeedAck.Remove(ack); | ||
598 | } | ||
599 | } | ||
600 | } | ||
601 | |||
602 | // Handle PacketAck packets | ||
603 | if (packet.Type == PacketType.PacketAck) | ||
604 | { | ||
605 | PacketAckPacket ackPacket = (PacketAckPacket)packet; | ||
606 | |||
607 | lock (User_info.NeedAck) | ||
608 | { | ||
609 | foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets) | ||
610 | { | ||
611 | User_info.NeedAck.Remove(block.ID); | ||
612 | } | ||
613 | } | ||
614 | } | ||
615 | |||
616 | this.CallbackObject.MainCallback(packet,User_info); | ||
617 | |||
618 | } | ||
619 | |||
620 | private void AckTimer_Elapsed(object sender, ElapsedEventArgs ea) | ||
621 | { | ||
622 | if (connected) | ||
623 | { | ||
624 | |||
625 | //TODO for each user_agent_info | ||
626 | for(int i=0; i<this.User_agents.Count; i++) | ||
627 | { | ||
628 | UserAgentInfo user=(UserAgentInfo)this.User_agents[i]; | ||
629 | |||
630 | SendAcks(user); | ||
631 | ResendUnacked(user); | ||
632 | } | ||
633 | } | ||
634 | } | ||
635 | } | ||
636 | |||
637 | public class Server_Settings | ||
638 | { | ||
639 | /// <summary>The version of libsecondlife (not the SL protocol itself)</summary> | ||
640 | public string VERSION = "libsecondlife 0.0.9"; | ||
641 | /// <summary>XML-RPC login server to connect to</summary> | ||
642 | public string LOGIN_SERVER = "http://www.garethnelson.com/ogs/login/"; | ||
643 | |||
644 | /// <summary>Millisecond interval between ticks, where all ACKs are | ||
645 | /// sent out and the age of unACKed packets is checked</summary> | ||
646 | public readonly int NETWORK_TICK_LENGTH = 500; | ||
647 | /// <summary>The maximum value of a packet sequence number. After that | ||
648 | /// we assume the sequence number just rolls over? Or maybe the | ||
649 | /// protocol isn't able to sustain a connection past that</summary> | ||
650 | public readonly int MAX_SEQUENCE = 0xFFFFFF; | ||
651 | /// <summary>Number of milliseconds before a teleport attempt will time | ||
652 | /// out</summary> | ||
653 | public readonly int TELEPORT_TIMEOUT = 18 * 1000; | ||
654 | |||
655 | /// <summary>Number of milliseconds before NetworkManager.Logout() will time out</summary> | ||
656 | public int LOGOUT_TIMEOUT = 5 * 1000; | ||
657 | /// <summary>Number of milliseconds for xml-rpc to timeout</summary> | ||
658 | public int LOGIN_TIMEOUT = 30 * 1000; | ||
659 | /// <summary>The maximum size of the sequence number inbox, used to | ||
660 | /// check for resent and/or duplicate packets</summary> | ||
661 | public int INBOX_SIZE = 100; | ||
662 | /// <summary>Milliseconds before a packet is assumed lost and resent</summary> | ||
663 | public int RESEND_TIMEOUT = 4000; | ||
664 | /// <summary>Milliseconds before the connection to a simulator is | ||
665 | /// assumed lost</summary> | ||
666 | public int SIMULATOR_TIMEOUT = 15000; | ||
667 | /// <summary>Maximum number of queued ACKs to be sent before SendAcks() | ||
668 | /// is forced</summary> | ||
669 | public int MAX_PENDING_ACKS = 10; | ||
670 | /// <summary>Maximum number of ACKs to append to a packet</summary> | ||
671 | public int MAX_APPENDED_ACKS = 10; | ||
672 | /// <summary>Cost of uploading an asset</summary> | ||
673 | public int UPLOAD_COST { get { return priceUpload; } } | ||
674 | |||
675 | |||
676 | private int priceUpload = 0; | ||
677 | |||
678 | public Server_Settings() | ||
679 | { | ||
680 | |||
681 | } | ||
682 | } | ||
683 | |||
684 | public class UserAgentInfo | ||
685 | { | ||
686 | public EndPoint endpoint; | ||
687 | public LLUUID AgentID; | ||
688 | public LLUUID SessionID; | ||
689 | public uint circuitCode; | ||
690 | public string name; | ||
691 | public uint localID; | ||
692 | public string first_name; | ||
693 | public string last_name; | ||
694 | |||
695 | public Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>(); | ||
696 | // Sequence numbers of packets we've received from the simulator | ||
697 | public Queue<uint> Inbox; | ||
698 | // ACKs that are queued up to be sent to the simulator | ||
699 | public Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>(); | ||
700 | |||
701 | public UserAgentInfo() | ||
702 | { | ||
703 | |||
704 | } | ||
705 | } | ||
706 | |||
707 | } | ||