From a82950672860eedeaa23da75421c74e094b5c0a4 Mon Sep 17 00:00:00 2001 From: cw Date: Wed, 31 Jan 2007 22:09:20 +0000 Subject: Making trunk; applying appropriate license to each file --- Server.cs | 756 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 756 insertions(+) create mode 100644 Server.cs (limited to 'Server.cs') diff --git a/Server.cs b/Server.cs new file mode 100644 index 0000000..1917922 --- /dev/null +++ b/Server.cs @@ -0,0 +1,756 @@ +/* + * * Copyright (c) , +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using System; +using System.Collections.Generic; +using libsecondlife; +using System.Collections; +using libsecondlife.Packets; +using libsecondlife.AssetSystem; +using System.Net; +using System.Net.Sockets; +using System.Timers; + +//really hacked , messy code + +namespace Second_server +{ + /// + /// Description of Server. + /// + public interface Server_callback + { + void main_callback(Packet pack, User_Agent_info User_info); + void new_user(User_Agent_info User_info); + void error(string text); + } + public class Server + { + /// A public reference to the client that this Simulator object + /// is attached to + //public SecondLife Client; + + /// The Region class that this Simulator wraps + // public Region Region; + + /// + /// Used internally to track sim disconnections, do not modify this + /// variable + /// + public bool DisconnectCandidate = false; + + /// + /// The ID number associated with this particular connection to the + /// simulator, used to emulate TCP connections. This is used + /// internally for packets that have a CircuitCode field + /// + public uint CircuitCode + { + get { return circuitCode; } + set { circuitCode = value; } + } + + /// + /// The IP address and port of the server + /// + public IPEndPoint IPEndPoint + { + get { return ipEndPoint; } + } + + /// + /// A boolean representing whether there is a working connection to the + /// simulator or not + /// + public bool Connected + { + get { return connected; } + } + + private Server_callback callback_object; + //private NetworkManager Network; + // private Dictionary> Callbacks; + private uint Sequence = 0; + private object SequenceLock = new object(); + private byte[] RecvBuffer = new byte[4096]; + private byte[] ZeroBuffer = new byte[8192]; + private byte[] ZeroOutBuffer = new byte[4096]; + private Socket Connection = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + private AsyncCallback ReceivedData; + // Packets we sent out that need ACKs from the simulator + // private Dictionary NeedAck = new Dictionary(); + // Sequence numbers of packets we've received from the simulator + // private Queue Inbox; + // ACKs that are queued up to be sent to the simulator + //private Dictionary PendingAcks = new Dictionary(); + private bool connected = false; + private uint circuitCode; + private IPEndPoint ipEndPoint; + private EndPoint endPoint; + private IPEndPoint ipeSender; + private EndPoint epSender; + private System.Timers.Timer AckTimer; + private Server_Settings Settings=new Server_Settings(); + public ArrayList User_agents=new ArrayList(); + + /// + /// Constructor for Simulator + /// + /// + /// + /// + /// + /// + public Server(Server_callback s_callback) + { + + this.callback_object=s_callback; + // Client = client; + // Network = client.Network; + // Callbacks = callbacks; + // Region = new Region(client); + // circuitCode = circuit; + // Inbox = new Queue(Settings.INBOX_SIZE); + AckTimer = new System.Timers.Timer(Settings.NETWORK_TICK_LENGTH); + AckTimer.Elapsed += new ElapsedEventHandler(AckTimer_Elapsed); + + // Initialize the callback for receiving a new packet + ReceivedData = new AsyncCallback(this.OnReceivedData); + + // Client.Log("Connecting to " + ip.ToString() + ":" + port, Helpers.LogLevel.Info); + + try + { + // Create an endpoint that we will be communicating with (need it in two + // types due to .NET weirdness) + // ipEndPoint = new IPEndPoint(ip, port); + ipEndPoint = new IPEndPoint(IPAddress.Any, 1000); + + + endPoint = (EndPoint)ipEndPoint; + + // Associate this simulator's socket with the given ip/port and start listening + Connection.Bind(endPoint); + + ipeSender = new IPEndPoint(IPAddress.Any, 0); + //The epSender identifies the incoming clients + epSender = (EndPoint) ipeSender; + Connection.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, ReceivedData, null); + + + // Start the ACK timer + AckTimer.Start(); + + + + // Track the current time for timeout purposes + //int start = Environment.TickCount; + + /* while (true) + { + if (connected || Environment.TickCount - start > Settings.SIMULATOR_TIMEOUT) + { + return; + } + System.Threading.Thread.Sleep(10); + }*/ + } + catch (Exception e) + { + // Client.Log(e.ToString(), Helpers.LogLevel.Error); + System.Console.WriteLine(e.Message); + } + } + + /// + /// Disconnect a Simulator + /// + public void Disconnect() + { + if (connected) + { + connected = false; + AckTimer.Stop(); + + // Send the CloseCircuit notice + CloseCircuitPacket close = new CloseCircuitPacket(); + + if (Connection.Connected) + { + try + { + // Connection.Send(close.ToBytes()); + } + catch (SocketException) + { + // There's a high probability of this failing if the network is + // disconnecting, so don't even bother logging the error + } + } + + try + { + // Shut the socket communication down + // Connection.Shutdown(SocketShutdown.Both); + } + catch (SocketException) + { + } + } + } + + /// + /// Sends a packet + /// + /// Packet to be sent + /// Increment sequence number? + public void SendPacket(Packet packet, bool incrementSequence, User_Agent_info User_info) + { + byte[] buffer; + int bytes; + + if (!connected && packet.Type != PacketType.UseCircuitCode) + { + // Client.Log("Trying to send a " + packet.Type.ToString() + " packet when the socket is closed", + // Helpers.LogLevel.Info); + + return; + } + + if (packet.Header.AckList.Length > 0) + { + // Scrub any appended ACKs since all of the ACK handling is done here + packet.Header.AckList = new uint[0]; + } + packet.Header.AppendedAcks = false; + + // Keep track of when this packet was sent out + packet.TickCount = Environment.TickCount; + + if (incrementSequence) + { + // Set the sequence number + lock (SequenceLock) + { + if (Sequence > Settings.MAX_SEQUENCE) + Sequence = 1; + else + Sequence++; + packet.Header.Sequence = Sequence; + } + + if (packet.Header.Reliable) + { + lock (User_info.NeedAck) + { + if (!User_info.NeedAck.ContainsKey(packet.Header.Sequence)) + { + User_info.NeedAck.Add(packet.Header.Sequence, packet); + } + else + { + // Client.Log("Attempted to add a duplicate sequence number (" + + // packet.Header.Sequence + ") to the NeedAck dictionary for packet type " + + // packet.Type.ToString(), Helpers.LogLevel.Warning); + } + } + + // Don't append ACKs to resent packets, in case that's what was causing the + // delivery to fail + if (!packet.Header.Resent) + { + // Append any ACKs that need to be sent out to this packet + lock (User_info.PendingAcks) + { + if (User_info.PendingAcks.Count > 0 && User_info.PendingAcks.Count < Settings.MAX_APPENDED_ACKS && + packet.Type != PacketType.PacketAck && + packet.Type != PacketType.LogoutRequest) + { + packet.Header.AckList = new uint[User_info.PendingAcks.Count]; + + int i = 0; + + foreach (uint ack in User_info.PendingAcks.Values) + { + packet.Header.AckList[i] = ack; + i++; + } + + User_info.PendingAcks.Clear(); + packet.Header.AppendedAcks = true; + } + } + } + } + } + + // Serialize the packet + buffer = packet.ToBytes(); + bytes = buffer.Length; + + try + { + // Zerocode if needed + if (packet.Header.Zerocoded) + { + lock (ZeroOutBuffer) + { + bytes = Helpers.ZeroEncode(buffer, bytes, ZeroOutBuffer); + Connection.SendTo(ZeroOutBuffer, bytes, SocketFlags.None,User_info.endpoint); + } + } + else + { + + Connection.SendTo(buffer, bytes, SocketFlags.None,User_info.endpoint); + } + } + catch (SocketException) + { + //Client.Log("Tried to send a " + packet.Type.ToString() + " on a closed socket", + // Helpers.LogLevel.Warning); + + Disconnect(); + } + } + + /// + /// Send a raw byte array payload as a packet + /// + /// The packet payload + /// Whether the second, third, and fourth bytes + /// should be modified to the current stream sequence number + /* public void SendPacket(byte[] payload, bool setSequence) + { + if (connected) + { + try + { + if (setSequence && payload.Length > 3) + { + lock (SequenceLock) + { + payload[1] = (byte)(Sequence >> 16); + payload[2] = (byte)(Sequence >> 8); + payload[3] = (byte)(Sequence % 256); + Sequence++; + } + } + + Connection.Send(payload, payload.Length, SocketFlags.None); + } + catch (SocketException e) + { + // Client.Log(e.ToString(), Helpers.LogLevel.Error); + } + } + else + { + // Client.Log("Attempted to send a " + payload.Length + " byte payload when " + + // "we are disconnected", Helpers.LogLevel.Warning); + } + } + */ + /// + /// Returns Simulator Name as a String + /// + /// + public override string ToString() + { + return( " (" + ipEndPoint.ToString() + ")"); + } + + /// + /// Sends out pending acknowledgements + /// + private void SendAcks(User_Agent_info User_info) + { + lock (User_info.PendingAcks) + { + if (connected && User_info.PendingAcks.Count > 0) + { + if (User_info.PendingAcks.Count > 250) + { + // FIXME: Handle the odd case where we have too many pending ACKs queued up + //Client.Log("Too many ACKs queued up!", Helpers.LogLevel.Error); + return; + } + + int i = 0; + PacketAckPacket acks = new PacketAckPacket(); + acks.Packets = new PacketAckPacket.PacketsBlock[User_info.PendingAcks.Count]; + + foreach (uint ack in User_info.PendingAcks.Values) + { + acks.Packets[i] = new PacketAckPacket.PacketsBlock(); + acks.Packets[i].ID = ack; + i++; + } + + acks.Header.Reliable = false; + SendPacket(acks, true,User_info); + + User_info.PendingAcks.Clear(); + } + } + } + /// + /// Resend unacknowledged packets + /// + private void ResendUnacked(User_Agent_info User_info) + { + if (connected) + { + int now = Environment.TickCount; + + lock (User_info.NeedAck) + { + foreach (Packet packet in User_info.NeedAck.Values) + { + if (now - packet.TickCount > Settings.RESEND_TIMEOUT) + { + // Client.Log("Resending " + packet.Type.ToString() + " packet, " + + // (now - packet.TickCount) + "ms have passed", Helpers.LogLevel.Info); + + packet.Header.Resent = true; + SendPacket(packet, false,User_info); + } + } + } + } + } + /// + /// Callback handler for incomming data + /// + /// + private void OnReceivedData(IAsyncResult result) + { + + ipeSender = new IPEndPoint(IPAddress.Any, 0); + epSender = (EndPoint)ipeSender; + Packet packet = null; + int numBytes; + + // If we're receiving data the sim connection is open + connected = true; + + // Update the disconnect flag so this sim doesn't time out + DisconnectCandidate = false; + User_Agent_info User_info=null; + + lock (RecvBuffer) + { + // Retrieve the incoming packet + try + { + numBytes = Connection.EndReceiveFrom(result, ref epSender); + + //find user_agent_info + + int packetEnd = numBytes - 1; + packet = Packet.BuildPacket(RecvBuffer, ref packetEnd, ZeroBuffer); + + + //should check if login/useconnection packet first + if (packet.Type == PacketType.UseCircuitCode) + { + UseCircuitCodePacket cir_pack=(UseCircuitCodePacket)packet; + User_Agent_info new_user=new User_Agent_info(); + new_user.circuitCode=cir_pack.CircuitCode.Code; + new_user.AgentID=cir_pack.CircuitCode.ID; + new_user.SessionID=cir_pack.CircuitCode.SessionID; + new_user.endpoint=epSender; + new_user.Inbox = new Queue(Settings.INBOX_SIZE); + + this.callback_object.new_user(new_user); + this.User_agents.Add(new_user); + + } + + + User_Agent_info temp_agent=null; + IPEndPoint send_ip=(IPEndPoint)epSender; + // this.callback_object.error("incoming: address is "+send_ip.Address +"port number is: "+send_ip.Port.ToString()); + + for(int ii=0; ii Settings.MAX_PENDING_ACKS) + { + SendAcks(User_info); + } + + // Check if we already received this packet + if (User_info.Inbox.Contains(packet.Header.Sequence)) + { + //Client.Log("Received a duplicate " + packet.Type.ToString() + ", sequence=" + + // packet.Header.Sequence + ", resent=" + ((packet.Header.Resent) ? "Yes" : "No") + + // ", Inbox.Count=" + Inbox.Count + ", NeedAck.Count=" + NeedAck.Count, + // Helpers.LogLevel.Info); + + // Send an ACK for this packet immediately + //SendAck(packet.Header.Sequence); + + // TESTING: Try just queuing up ACKs for resent packets instead of immediately triggering an ACK + lock (User_info.PendingAcks) + { + uint sequence = (uint)packet.Header.Sequence; + if (!User_info.PendingAcks.ContainsKey(sequence)) { User_info.PendingAcks[sequence] = sequence; } + } + + // Avoid firing a callback twice for the same packet + // this.callback_object.error("avoiding callback"); + return; + } + else + { + lock (User_info.PendingAcks) + { + uint sequence = (uint)packet.Header.Sequence; + if (!User_info.PendingAcks.ContainsKey(sequence)) { User_info.PendingAcks[sequence] = sequence; } + } + } + } + + // Add this packet to our inbox + lock (User_info.Inbox) + { + while (User_info.Inbox.Count >= Settings.INBOX_SIZE) + { + User_info.Inbox.Dequeue(); + } + User_info.Inbox.Enqueue(packet.Header.Sequence); + } + + // Handle appended ACKs + if (packet.Header.AppendedAcks) + { + lock (User_info.NeedAck) + { + foreach (uint ack in packet.Header.AckList) + { + User_info.NeedAck.Remove(ack); + } + } + } + + // Handle PacketAck packets + if (packet.Type == PacketType.PacketAck) + { + PacketAckPacket ackPacket = (PacketAckPacket)packet; + + lock (User_info.NeedAck) + { + foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets) + { + User_info.NeedAck.Remove(block.ID); + } + } + } + + // this.callback_object.error("calling callback"); + this.callback_object.main_callback(packet,User_info); + // this.callback_object.error("finished"); + // Fire the registered packet events + #region FireCallbacks + /* if (Callbacks.ContainsKey(packet.Type)) + { + List callbackArray = Callbacks[packet.Type]; + + // Fire any registered callbacks + foreach (NetworkManager.PacketCallback callback in callbackArray) + { + if (callback != null) + { + try + { + callback(packet, this); + } + catch (Exception e) + { + Client.Log("Caught an exception in a packet callback: " + e.ToString(), + Helpers.LogLevel.Error); + } + } + } + } + + if (Callbacks.ContainsKey(PacketType.Default)) + { + List callbackArray = Callbacks[PacketType.Default]; + + // Fire any registered callbacks + foreach (NetworkManager.PacketCallback callback in callbackArray) + { + if (callback != null) + { + try + { + callback(packet, this); + } + catch (Exception e) + { + Client.Log("Caught an exception in a packet callback: " + e.ToString(), + Helpers.LogLevel.Error); + } + } + } + } + */ + #endregion FireCallbacks + } + + private void AckTimer_Elapsed(object sender, ElapsedEventArgs ea) + { + if (connected) + { + + //TODO for each user_agent_info + for(int i=0; iThe version of libsecondlife (not the SL protocol itself) + public string VERSION = "libsecondlife 0.0.9"; + /// XML-RPC login server to connect to + public string LOGIN_SERVER = "https://login.agni.lindenlab.com/cgi-bin/login.cgi"; + + /// Millisecond interval between ticks, where all ACKs are + /// sent out and the age of unACKed packets is checked + public readonly int NETWORK_TICK_LENGTH = 500; + /// The maximum value of a packet sequence number. After that + /// we assume the sequence number just rolls over? Or maybe the + /// protocol isn't able to sustain a connection past that + public readonly int MAX_SEQUENCE = 0xFFFFFF; + /// Number of milliseconds before a teleport attempt will time + /// out + public readonly int TELEPORT_TIMEOUT = 18 * 1000; + + /// Number of milliseconds before NetworkManager.Logout() will time out + public int LOGOUT_TIMEOUT = 5 * 1000; + /// Number of milliseconds for xml-rpc to timeout + public int LOGIN_TIMEOUT = 30 * 1000; + /// The maximum size of the sequence number inbox, used to + /// check for resent and/or duplicate packets + public int INBOX_SIZE = 100; + /// Milliseconds before a packet is assumed lost and resent + public int RESEND_TIMEOUT = 4000; + /// Milliseconds before the connection to a simulator is + /// assumed lost + public int SIMULATOR_TIMEOUT = 15000; + /// Maximum number of queued ACKs to be sent before SendAcks() + /// is forced + public int MAX_PENDING_ACKS = 10; + /// Maximum number of ACKs to append to a packet + public int MAX_APPENDED_ACKS = 10; + /// Cost of uploading an asset + public int UPLOAD_COST { get { return priceUpload; } } + + + private int priceUpload = 0; + + public Server_Settings() + { + + } + } + + public class User_Agent_info + { + public EndPoint endpoint; + public LLUUID AgentID; + public LLUUID SessionID; + public uint circuitCode; + public string name; + public uint localID; + public string first_name; + public string last_name; + + public Dictionary NeedAck = new Dictionary(); + // Sequence numbers of packets we've received from the simulator + public Queue Inbox; + // ACKs that are queued up to be sent to the simulator + public Dictionary PendingAcks = new Dictionary(); + + public User_Agent_info() + { + + } + } + +} -- cgit v1.1