* Internal callback mechanism, user callback hashtable is now UserCallbacks

* Moved some of the packet receiving logic in to internal callbacks
* Fixed HORRIBLE threading race condition with Circuit.Buffer[]

git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@22 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
John Hurliman
2006-06-13 07:15:38 +00:00
parent a25be7e467
commit 9c32a490a0
5 changed files with 139 additions and 55 deletions

View File

@@ -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);
}
}
}

View File

@@ -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)
{

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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"))