diff --git a/libsecondlife-cs/NetworkManager.cs b/libsecondlife-cs/NetworkManager.cs index 1707cd13..59a2e001 100644 --- a/libsecondlife-cs/NetworkManager.cs +++ b/libsecondlife-cs/NetworkManager.cs @@ -9,7 +9,7 @@ using System.Security.Cryptography; namespace libsecondlife { - public delegate void PacketCallback(Packet packet); + public delegate void PacketCallback(Packet packet, Circuit circuit); internal class AcceptAllCertificatePolicy : ICertificatePolicy { @@ -36,6 +36,8 @@ namespace libsecondlife private EndPoint endPoint; private ProtocolManager Protocol; private NetworkManager Network; + private Hashtable UserCallbacks; + private Hashtable InternalCallbacks; private byte[] Buffer; private Socket Connection; private AsyncCallback ReceivedData; @@ -48,10 +50,13 @@ namespace libsecondlife private Mutex NeedAckMutex; private int ResendTick; - public Circuit(ProtocolManager protocol, NetworkManager network, uint circuitCode) + public Circuit(ProtocolManager protocol, NetworkManager network, Hashtable userCallbacks, + Hashtable internalCallbacks, uint circuitCode) { Protocol = protocol; Network = network; + UserCallbacks = userCallbacks; + InternalCallbacks = internalCallbacks; CircuitCode = circuitCode; Sequence = 0; Buffer = new byte[4096]; @@ -177,6 +182,7 @@ namespace libsecondlife byte[] zeroBuffer = new byte[4096]; int zeroBytes; + // DEBUG //Console.WriteLine("Sending " + packet.Data.Length + " byte " + packet.Layout.Name); try @@ -241,6 +247,7 @@ namespace libsecondlife int numSent = Connection.Send(zeroBuffer, zeroBytes, SocketFlags.None); + // DEBUG //Console.WriteLine("Sent " + numSent + " bytes"); } catch (Exception e) @@ -275,6 +282,7 @@ namespace libsecondlife // Bypass SendPacket since we are taking care of the AckOutbox ourself int numSent = Connection.Send(packet.Data); + // DEBUG //Console.WriteLine("Sent " + numSent + " byte " + packet.Layout.Name); AckOutbox.Clear(); @@ -302,9 +310,6 @@ namespace libsecondlife // Retrieve the incoming packet int numBytes = Connection.EndReceiveFrom(result, ref endPoint); - // Start listening again immediately - Connection.BeginReceiveFrom(Buffer, 0, Buffer.Length, SocketFlags.None, ref endPoint, ReceivedData, null); - if ((Buffer[Buffer.Length - 1] & Helpers.MSG_APPENDED_ACKS) != 0) { // Grab the ACKs that are appended to this packet @@ -345,14 +350,22 @@ namespace libsecondlife // Allocate a temporary buffer for the zerodecoded packet byte[] zeroBuffer = new byte[4096]; int zeroBytes = Helpers.ZeroDecode(Buffer, numBytes, zeroBuffer); - Array.Copy(zeroBuffer, 0, Buffer, 0, zeroBytes); + packet = new Packet(zeroBuffer, zeroBytes, Protocol); numBytes = zeroBytes; } + else + { + // Create the packet object from our byte array + packet = new Packet(Buffer, numBytes, Protocol); + } - // Create the packet object from our byte array - packet = new Packet(Buffer, numBytes, Protocol); + // DEBUG + //Console.WriteLine("Received a " + numBytes + " byte " + packet.Layout.Name); - if ((Buffer[0] & Helpers.MSG_RELIABLE) != 0) + // Start listening again since we're done with Buffer + Connection.BeginReceiveFrom(Buffer, 0, Buffer.Length, SocketFlags.None, ref endPoint, ReceivedData, null); + + if ((packet.Data[0] & Helpers.MSG_RELIABLE) != 0) { if (!AckOutbox.Contains((uint)packet.Sequence)) { @@ -383,19 +396,13 @@ namespace libsecondlife Helpers.Log("Received an unrecognized packet", Helpers.LogLevel.Warning); return; } - else if (packet.Layout.Name == "StartPingCheck") - { - //TODO: Should we care about OldestUnacked? - - // Respond to the ping request - Packet pingPacket = PacketBuilder.CompletePingCheck(Protocol, packet.Data[5]); - SendPacket(pingPacket, true); - } else if (packet.Layout.Name == "PacketAck") { + // PacketAck is handled directly instead of using a callback to simplify access to + // the NeedAck hashtable and its mutex + ArrayList blocks = packet.Blocks(); - // Claim the NeedAck mutex NeedAckMutex.WaitOne(); // Remove each ACK in this packet from the NeedAck waiting list @@ -412,30 +419,39 @@ namespace libsecondlife if ((uint)reliablePacket.Sequence == (uint)field.Data) { NeedAck.Remove(reliablePacket); + // Restart the loop to avoid upsetting the enumerator goto Beginning; } } } } - // Release the mutex NeedAckMutex.ReleaseMutex(); } + + // Fire any internal callbacks registered with this packet type + PacketCallback callback = (PacketCallback)InternalCallbacks[packet.Layout.Name]; - // Fire any callbacks registered with this packet type - PacketCallback callback = (PacketCallback)Network.Callbacks[packet.Layout.Name]; + if (callback != null) + { + callback(packet, this); + } + + // Fire any user callbacks registered with this packet type + callback = (PacketCallback)UserCallbacks[packet.Layout.Name]; if (callback != null) { - callback(packet); + callback(packet, this); } else { - callback = (PacketCallback)Network.Callbacks["Default"]; + // Attempt to fire a default user callback + callback = (PacketCallback)UserCallbacks["Default"]; if (callback != null) { - callback(packet); + callback(packet, this); } } } @@ -537,20 +553,27 @@ namespace libsecondlife { public LoginReply LoginValues; public string LoginError; - public Hashtable Callbacks; + public Hashtable UserCallbacks; public Circuit CurrentCircuit; private ProtocolManager Protocol; private string LoginBuffer; private ArrayList Circuits; - //private Hashtable InternalCallbacks; + private Hashtable InternalCallbacks; public NetworkManager(ProtocolManager protocol) { Protocol = protocol; Circuits = new ArrayList(); - Callbacks = new Hashtable(); + UserCallbacks = new Hashtable(); + InternalCallbacks = new Hashtable(); CurrentCircuit = null; + + // Register the internal callbacks + PacketCallback callback = new PacketCallback(RegionHandshakeHandler); + InternalCallbacks["RegionHandshake"] = callback; + callback = new PacketCallback(StartPingCheckHandler); + InternalCallbacks["StartPingCheck"] = callback; } public void SendPacket(Packet packet) @@ -565,6 +588,11 @@ namespace libsecondlife } } + public void SendPacket(Packet packet, Circuit circuit) + { + circuit.SendPacket(packet, true); + } + public bool Login(string firstName, string lastName, string password, string mac, int major, int minor, int patch, int build, string platform, string viewerDigest, string userAgent, string author) @@ -654,7 +682,7 @@ namespace libsecondlife } // Connect to the sim given in the login reply - Circuit circuit = new Circuit(Protocol, this, LoginValues.CircuitCode); + Circuit circuit = new Circuit(Protocol, this, UserCallbacks, InternalCallbacks, LoginValues.CircuitCode); if (!circuit.Open(LoginValues.IP, LoginValues.Port)) { return false; @@ -784,5 +812,21 @@ namespace libsecondlife return Int32.Parse(rpc.Substring(0, pos2)); } + + private void StartPingCheckHandler(Packet packet, Circuit circuit) + { + //TODO: Should we care about OldestUnacked? + + // Respond to the ping request + Packet pingPacket = PacketBuilder.CompletePingCheck(Protocol, packet.Data[5]); + SendPacket(pingPacket, circuit); + } + + private void RegionHandshakeHandler(Packet packet, Circuit circuit) + { + // Send a RegionHandshakeReply + Packet replyPacket = new Packet("RegionHandshakeReply", Protocol, 12); + SendPacket(replyPacket, circuit); + } } } diff --git a/libsecondlife-cs/Packet.cs b/libsecondlife-cs/Packet.cs index 0eca7228..10e542d0 100644 --- a/libsecondlife-cs/Packet.cs +++ b/libsecondlife-cs/Packet.cs @@ -435,6 +435,41 @@ namespace libsecondlife return packet; } + public static Packet TeleportLocationRequest(ProtocolManager protocol, long regionHandle, LLVector3 lookAt, + LLVector3 position, LLUUID agentID, LLUUID sessionID) + { + Packet packet = new Packet("LogoutRequest", protocol, 72); + + Array.Copy(BitConverter.GetBytes(regionHandle), 0, packet.Data, 8, 8); + Array.Copy(lookAt.GetBytes(), 0, packet.Data, 16, 12); + Array.Copy(position.GetBytes(), 0, packet.Data, 28, 12); + Array.Copy(agentID.Data, 0, packet.Data, 40, 16); + Array.Copy(sessionID.Data, 0, packet.Data, 56, 16); + + packet.Data[0] = Helpers.MSG_RELIABLE; + + return packet; + } + + public static Packet DirLandQuery(ProtocolManager protocol, bool reservedNewbie, bool forSale, + LLUUID queryID, bool auction, uint queryFlags, LLUUID agentID, LLUUID sessionID) + { + Packet packet = new Packet("DirLandQuery", protocol, 63); + + packet.Data[8] = BitConverter.GetBytes(reservedNewbie)[0]; + packet.Data[9] = BitConverter.GetBytes(forSale)[0]; + Array.Copy(queryID.Data, 0, packet.Data, 10, 16); + packet.Data[26] = BitConverter.GetBytes(auction)[0]; + Array.Copy(BitConverter.GetBytes(queryFlags), 0, packet.Data, 27, 4); + Array.Copy(agentID.Data, 0, packet.Data, 31, 16); + Array.Copy(sessionID.Data, 0, packet.Data, 47, 16); + + // Set the packet flags + packet.Data[0] = Helpers.MSG_RELIABLE; + + return packet; + } + public static Packet DirFindQuery(ProtocolManager protocol, string queryText, LLUUID queryID, LLUUID agentID, LLUUID sessionID) { diff --git a/libsecondlife-cs/SecondLife.cs b/libsecondlife-cs/SecondLife.cs index 2040f4e0..304d1aac 100644 --- a/libsecondlife-cs/SecondLife.cs +++ b/libsecondlife-cs/SecondLife.cs @@ -28,40 +28,47 @@ namespace libsecondlife public static int ZeroDecode(byte[] src, int srclen, byte[] dest) { - int zerolen = 0; + uint zerolen = 0; - Array.Copy(src, 0, dest, 0, 4); - zerolen += 4; - - for (int i = zerolen; i < srclen; i++) + try { - if (src[i] == 0x00) - { - for (byte j = 0; j < src[i + 1]; j++) - { - dest[zerolen++] = 0x00; - } + Array.Copy(src, 0, dest, 0, 4); + zerolen += 4; - i++; - } - else + for (uint i = zerolen; i < srclen; i++) { - dest[zerolen++] = src[i]; + if (src[i] == 0x00) + { + for (byte j = 0; j < src[i + 1]; j++) + { + dest[zerolen++] = 0x00; + } + + i++; + } + else + { + dest[zerolen++] = src[i]; + } } } + catch (Exception e) + { + Helpers.Log(e.ToString(), Helpers.LogLevel.Error); + } - return zerolen; + return (int)zerolen; } public static int ZeroEncode(byte[] src, int srclen, byte[] dest) { - int zerolen = 0; + uint zerolen = 0; byte zerocount = 0; Array.Copy(src, 0, dest, 0, 4); zerolen += 4; - for (int i = zerolen; i < srclen; i++) + for (uint i = zerolen; i < srclen; i++) { if (src[i] == 0x00) { @@ -93,7 +100,7 @@ namespace libsecondlife dest[zerolen++] = (byte)zerocount; } - return zerolen; + return (int)zerolen; } } diff --git a/libsecondlife-cs/examples/name2key/name2key.cs b/libsecondlife-cs/examples/name2key/name2key.cs index cdd3705c..0f5ea94e 100644 --- a/libsecondlife-cs/examples/name2key/name2key.cs +++ b/libsecondlife-cs/examples/name2key/name2key.cs @@ -9,7 +9,7 @@ namespace name2key static bool waiting = true; // - public static void QueryHandler(Packet packet) + public static void QueryHandler(Packet packet, Circuit circuit) { if (packet.Layout.Name.IndexOf("Dir") > -1) { @@ -67,10 +67,10 @@ namespace name2key // Setup the callback PacketCallback queryCallback = new PacketCallback(QueryHandler); - client.Network.Callbacks["DirPeopleReply"] = queryCallback; + client.Network.UserCallbacks["DirPeopleReply"] = queryCallback; if (!client.Network.Login(args[0], args[1], args[2], "00:00:00:00:00:00", 1, 10, 2, 2, "Win", - "0", "sldump", "jhurliman@wsu.edu")) + "0", "name2key", "jhurliman@wsu.edu")) { // Login failed Console.WriteLine("ERROR: " + client.Network.LoginError); diff --git a/libsecondlife-cs/examples/sldump/sldump.cs b/libsecondlife-cs/examples/sldump/sldump.cs index eb28a6ef..df9dfd2c 100644 --- a/libsecondlife-cs/examples/sldump/sldump.cs +++ b/libsecondlife-cs/examples/sldump/sldump.cs @@ -7,10 +7,8 @@ namespace sldump class sldump { // - public static void DefaultHandler(Packet packet) - { - Console.WriteLine("Received " + packet.Layout.Name); - + public static void DefaultHandler(Packet packet, Circuit circuit) + { string output = ""; ArrayList blocks = packet.Blocks(); @@ -62,7 +60,7 @@ namespace sldump // Setup the callback PacketCallback defaultCallback = new PacketCallback(DefaultHandler); - client.Network.Callbacks["Default"] = defaultCallback; + client.Network.UserCallbacks["Default"] = defaultCallback; if (!client.Network.Login(args[0], args[1], args[2], "00:00:00:00:00:00", 1, 10, 2, 2, "Win", "0", "sldump", "jhurliman@wsu.edu"))