diff --git a/Programs/Simian/Agent.cs b/Programs/Simian/Agent.cs
index b576c30e..2330ada7 100644
--- a/Programs/Simian/Agent.cs
+++ b/Programs/Simian/Agent.cs
@@ -7,7 +7,7 @@ using OpenMetaverse.Packets;
namespace Simian
{
- public class Agent : IDisposable
+ public class Agent
{
public UUID AgentID;
public UUID SessionID;
@@ -31,262 +31,5 @@ namespace Simian
public PrimFlags Flags;
public UUID InventoryRoot;
public UUID InventoryLibRoot;
-
- public bool Disposed { get { return disposed; } }
- bool disposed = false;
-
- /// Sequence numbers of packets we've received (for duplicate checking)
- internal Queue packetArchive = new Queue();
- /// Packets we have sent that need to be ACKed by the client
- internal Dictionary needAcks = new Dictionary();
-
- UDPServer udpServer;
- IPEndPoint address;
- /// ACKs that are queued up, waiting to be sent to the client
- SortedList pendingAcks = new SortedList();
- int currentSequence = 0;
- Timer ackTimer;
-
- public IPEndPoint Address
- {
- get { return address; }
- set { address = value; }
- }
-
- public Agent(UDPServer udpServer)
- {
- this.udpServer = udpServer;
- }
-
- public void Initialize(IPEndPoint address)
- {
- this.address = address;
-
- ackTimer = new Timer(new TimerCallback(AckTimer_Elapsed), null, Settings.NETWORK_TICK_INTERVAL,
- Settings.NETWORK_TICK_INTERVAL);
- }
-
- public void Dispose()
- {
- disposed = true;
- ackTimer.Dispose();
- packetArchive.Clear();
- needAcks.Clear();
- }
-
- public void SendPacket(Packet packet)
- {
- SendPacket(packet, true);
- }
-
- public void SendPacket(Packet packet, bool setSequence)
- {
- byte[] buffer;
- int bytes;
-
- // Keep track of when this packet was sent out
- packet.TickCount = Environment.TickCount;
-
- if (setSequence)
- {
- // Reset to zero if we've hit the upper sequence number limit
- Interlocked.CompareExchange(ref currentSequence, 0, 0xFFFFFF);
- // Increment and fetch the current sequence number
- uint sequence = (uint)Interlocked.Increment(ref currentSequence);
- packet.Header.Sequence = sequence;
-
- if (packet.Header.Reliable)
- {
- // Add this packet to the list of ACK responses we are waiting on from the client
- lock (needAcks)
- needAcks[sequence] = packet;
-
- if (packet.Header.Resent)
- {
- // This packet has already been sent out once, strip any appended ACKs
- // off it and reinsert them into the outgoing ACK queue under the
- // assumption that this packet will continually be rejected from the
- // client or that the appended ACKs are possibly making the delivery fail
- if (packet.Header.AckList.Length > 0)
- {
- Logger.DebugLog(String.Format("Purging ACKs from packet #{0} ({1}) which will be resent.",
- packet.Header.Sequence, packet.GetType()));
-
- lock (pendingAcks)
- {
- foreach (uint ack in packet.Header.AckList)
- {
- if (!pendingAcks.ContainsKey(ack))
- pendingAcks[ack] = ack;
- }
- }
-
- packet.Header.AppendedAcks = false;
- packet.Header.AckList = new uint[0];
- }
- }
- else
- {
- // This packet is not a resend, check if the conditions are favorable
- // to ACK appending
- if (packet.Type != PacketType.PacketAck)
- {
- lock (pendingAcks)
- {
- if (pendingAcks.Count > 0 &&
- pendingAcks.Count < 10)
- {
- // Append all of the queued up outgoing ACKs to this packet
- packet.Header.AckList = new uint[pendingAcks.Count];
-
- for (int i = 0; i < pendingAcks.Count; i++)
- packet.Header.AckList[i] = pendingAcks.Values[i];
-
- pendingAcks.Clear();
- packet.Header.AppendedAcks = true;
- }
- }
- }
- }
- }
- else if (packet.Header.AckList.Length > 0)
- {
- // Sanity check for ACKS appended on an unreliable packet, this is bad form
- Logger.Log("Sending appended ACKs on an unreliable packet", Helpers.LogLevel.Warning);
- }
- }
-
- // Serialize the packet
- buffer = packet.ToBytes();
- bytes = buffer.Length;
- //Stats.SentBytes += (ulong)bytes;
- //++Stats.SentPackets;
-
- UDPPacketBuffer buf = new UDPPacketBuffer(address);
-
- // Zerocode if needed
- if (packet.Header.Zerocoded)
- bytes = Helpers.ZeroEncode(buffer, bytes, buf.Data);
- else
- Buffer.BlockCopy(buffer, 0, buf.Data, 0, bytes);
-
- buf.DataLength = bytes;
-
- udpServer.AsyncBeginSend(buf);
- }
-
- public void QueueAck(uint ack)
- {
- // Add this packet to the list of ACKs that need to be sent out
- lock (pendingAcks)
- pendingAcks[ack] = ack;
-
- // Send out ACKs if we have a lot of them
- if (pendingAcks.Count >= 10)
- SendAcks();
- }
-
- public void ProcessAcks(List acks)
- {
- lock (needAcks)
- {
- foreach (uint ack in acks)
- needAcks.Remove(ack);
- }
- }
-
- public void SendAck(uint ack)
- {
- PacketAckPacket acks = new PacketAckPacket();
- acks.Header.Reliable = false;
- acks.Packets = new PacketAckPacket.PacketsBlock[1];
- acks.Packets[0] = new PacketAckPacket.PacketsBlock();
- acks.Packets[0].ID = ack;
-
- SendPacket(acks, true);
- }
-
- void SendAcks()
- {
- PacketAckPacket acks = null;
-
- lock (pendingAcks)
- {
- if (pendingAcks.Count > 0)
- {
- if (pendingAcks.Count > 250)
- {
- Logger.Log("Too many ACKs queued up!", Helpers.LogLevel.Error);
- return;
- }
-
- acks = new PacketAckPacket();
- acks.Header.Reliable = false;
- acks.Packets = new PacketAckPacket.PacketsBlock[pendingAcks.Count];
-
- for (int i = 0; i < pendingAcks.Count; i++)
- {
- acks.Packets[i] = new PacketAckPacket.PacketsBlock();
- acks.Packets[i].ID = pendingAcks.Values[i];
- }
-
- pendingAcks.Clear();
- }
- }
-
- if (acks != null)
- SendPacket(acks, true);
- }
-
- void ResendUnacked()
- {
- lock (needAcks)
- {
- List dropAck = new List();
- int now = Environment.TickCount;
-
- // Resend packets
- foreach (Packet packet in needAcks.Values)
- {
- if (packet.TickCount != 0 && now - packet.TickCount > 4000)
- {
- if (packet.ResendCount < 3)
- {
- Logger.DebugLog(String.Format("Resending packet #{0} ({1}), {2}ms have passed",
- packet.Header.Sequence, packet.GetType(), now - packet.TickCount));
-
- packet.TickCount = 0;
- packet.Header.Resent = true;
- //++Stats.ResentPackets;
- ++packet.ResendCount;
-
- SendPacket(packet, false);
- }
- else
- {
- Logger.Log(String.Format("Dropping packet #{0} ({1}) after {2} failed attempts",
- packet.Header.Sequence, packet.GetType(), packet.ResendCount), Helpers.LogLevel.Warning);
-
- dropAck.Add(packet.Header.Sequence);
- }
- }
- }
-
- if (dropAck.Count != 0)
- {
- foreach (uint seq in dropAck)
- needAcks.Remove(seq);
- }
- }
- }
-
- private void AckTimer_Elapsed(object obj)
- {
- if (!this.Disposed)
- {
- SendAcks();
- ResendUnacked();
- }
- }
}
}
diff --git a/Programs/Simian/EventDictionary.cs b/Programs/Simian/EventDictionary.cs
index 6941e5b4..898cadf1 100644
--- a/Programs/Simian/EventDictionary.cs
+++ b/Programs/Simian/EventDictionary.cs
@@ -18,14 +18,14 @@ namespace Simian
private struct PacketCallbackWrapper
{
/// Callback to fire for this packet
- public UDPServer.PacketCallback Callback;
+ public PacketCallback Callback;
/// Reference to the agent that this packet came from
public Agent Agent;
/// The packet that needs to be processed
public Packet Packet;
}
- private Dictionary _EventTable = new Dictionary();
+ private Dictionary _EventTable = new Dictionary();
private WaitCallback _ThreadPoolCallback;
///
@@ -43,7 +43,7 @@ namespace Simian
/// incoming packet
/// Packet type to register the handler for
/// Callback to be fired
- public void RegisterEvent(PacketType packetType, UDPServer.PacketCallback eventHandler)
+ public void RegisterEvent(PacketType packetType, PacketCallback eventHandler)
{
lock (_EventTable)
{
@@ -59,7 +59,7 @@ namespace Simian
///
/// Packet type to unregister the handler for
/// Callback to be unregistered
- public void UnregisterEvent(PacketType packetType, UDPServer.PacketCallback eventHandler)
+ public void UnregisterEvent(PacketType packetType, PacketCallback eventHandler)
{
lock (_EventTable)
{
@@ -76,7 +76,7 @@ namespace Simian
/// Agent this packet was received from
internal void BeginRaiseEvent(PacketType packetType, Packet packet, Agent agent)
{
- UDPServer.PacketCallback callback;
+ PacketCallback callback;
PacketCallbackWrapper wrapper;
// Default handler first, if one exists
diff --git a/Programs/Simian/Extensions/AssetManager.cs b/Programs/Simian/Extensions/AssetManager.cs
index 1c8f5565..1315fa8b 100644
--- a/Programs/Simian/Extensions/AssetManager.cs
+++ b/Programs/Simian/Extensions/AssetManager.cs
@@ -21,10 +21,10 @@ namespace Simian.Extensions
{
LoadDefaultAssets(Server.DataDir);
- Server.UDPServer.RegisterPacketCallback(PacketType.AssetUploadRequest, new UDPServer.PacketCallback(AssetUploadRequestHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.SendXferPacket, new UDPServer.PacketCallback(SendXferPacketHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.AbortXfer, new UDPServer.PacketCallback(AbortXferHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.TransferRequest, new UDPServer.PacketCallback(TransferRequestHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, new PacketCallback(AssetUploadRequestHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.SendXferPacket, new PacketCallback(SendXferPacketHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.AbortXfer, new PacketCallback(AbortXferHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.TransferRequest, new PacketCallback(TransferRequestHandler));
}
public void Stop()
@@ -59,7 +59,7 @@ namespace Simian.Extensions
complete.AssetBlock.Success = true;
complete.AssetBlock.Type = request.AssetBlock.Type;
complete.AssetBlock.UUID = request.AssetBlock.TransactionID;
- agent.SendPacket(complete);
+ Server.UDP.SendPacket(agent.AgentID, complete, PacketCategory.Inventory);
}
else
{
@@ -85,7 +85,7 @@ namespace Simian.Extensions
lock (CurrentUploads)
CurrentUploads[xfer.XferID.ID] = asset;
- agent.SendPacket(xfer);
+ Server.UDP.SendPacket(agent.AgentID, xfer, PacketCategory.Inventory);
}
}
@@ -114,7 +114,7 @@ namespace Simian.Extensions
ConfirmXferPacketPacket confirm = new ConfirmXferPacketPacket();
confirm.XferID.ID = xfer.XferID.ID;
confirm.XferID.Packet = xfer.XferID.Packet;
- agent.SendPacket(confirm);
+ Server.UDP.SendPacket(agent.AgentID, confirm, PacketCategory.Asset);
}
else
{
@@ -125,7 +125,7 @@ namespace Simian.Extensions
ConfirmXferPacketPacket confirm = new ConfirmXferPacketPacket();
confirm.XferID.ID = xfer.XferID.ID;
confirm.XferID.Packet = xfer.XferID.Packet;
- agent.SendPacket(confirm);
+ Server.UDP.SendPacket(agent.AgentID, confirm, PacketCategory.Asset);
if ((xfer.XferID.Packet & (uint)0x80000000) != 0)
{
@@ -142,7 +142,7 @@ namespace Simian.Extensions
complete.AssetBlock.Success = true;
complete.AssetBlock.Type = (sbyte)asset.AssetType;
complete.AssetBlock.UUID = asset.AssetID;
- agent.SendPacket(complete);
+ Server.UDP.SendPacket(agent.AgentID, complete, PacketCategory.Asset);
}
}
}
@@ -218,7 +218,7 @@ namespace Simian.Extensions
response.TransferInfo.Status = (int)StatusCode.OK;
response.TransferInfo.TargetType = (int)TargetType.Unknown; // Doesn't seem to be used by the client
- agent.SendPacket(response);
+ Server.UDP.SendPacket(agent.AgentID, response, PacketCategory.Asset);
// Transfer system does not wait for ACKs, just sends all of the
// packets for this transfer out
@@ -242,7 +242,7 @@ namespace Simian.Extensions
else
transfer.TransferData.Status = (int)StatusCode.OK;
- agent.SendPacket(transfer);
+ Server.UDP.SendPacket(agent.AgentID, transfer, PacketCategory.Asset);
}
}
else
@@ -259,7 +259,7 @@ namespace Simian.Extensions
response.TransferInfo.Status = (int)StatusCode.UnknownSource;
response.TransferInfo.TargetType = (int)TargetType.Unknown;
- agent.SendPacket(response);
+ Server.UDP.SendPacket(agent.AgentID, response, PacketCategory.Asset);
}
}
else if (source == SourceType.SimEstate)
diff --git a/Programs/Simian/Extensions/AvatarManager.cs b/Programs/Simian/Extensions/AvatarManager.cs
index 3af54e07..de92e1fc 100644
--- a/Programs/Simian/Extensions/AvatarManager.cs
+++ b/Programs/Simian/Extensions/AvatarManager.cs
@@ -19,13 +19,13 @@ namespace Simian.Extensions
public void Start()
{
- Server.UDPServer.RegisterPacketCallback(PacketType.AvatarPropertiesRequest, new UDPServer.PacketCallback(AvatarPropertiesRequestHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.AgentWearablesRequest, new UDPServer.PacketCallback(AgentWearablesRequestHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.AgentIsNowWearing, new UDPServer.PacketCallback(AgentIsNowWearingHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.AgentSetAppearance, new UDPServer.PacketCallback(AgentSetAppearanceHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.AgentAnimation, new UDPServer.PacketCallback(AgentAnimationHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.ViewerEffect, new UDPServer.PacketCallback(ViewerEffectHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.UUIDNameRequest, new UDPServer.PacketCallback(UUIDNameRequestHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.AvatarPropertiesRequest, new PacketCallback(AvatarPropertiesRequestHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.AgentWearablesRequest, new PacketCallback(AgentWearablesRequestHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.AgentIsNowWearing, new PacketCallback(AgentIsNowWearingHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.AgentSetAppearance, new PacketCallback(AgentSetAppearanceHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.AgentAnimation, new PacketCallback(AgentAnimationHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.ViewerEffect, new PacketCallback(ViewerEffectHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.UUIDNameRequest, new PacketCallback(UUIDNameRequestHandler));
}
public void Stop()
@@ -67,11 +67,7 @@ namespace Simian.Extensions
sendAnim.AnimationList[i].AnimSequenceID = sequenceNums[i];
}
- lock (Server.Agents)
- {
- foreach (Agent recipient in Server.Agents.Values)
- recipient.SendPacket(sendAnim);
- }
+ Server.UDP.BroadcastPacket(sendAnim, PacketCategory.State);
}
void AgentAnimationHandler(Packet packet, Agent agent)
@@ -106,12 +102,7 @@ namespace Simian.Extensions
effect.AgentData.AgentID = UUID.Zero;
effect.AgentData.SessionID = UUID.Zero;
- // Broadcast this to everyone
- lock (Server.Agents)
- {
- foreach (Agent recipient in Server.Agents.Values)
- recipient.SendPacket(effect);
- }
+ Server.UDP.BroadcastPacket(effect, PacketCategory.State);
}
void AvatarPropertiesRequestHandler(Packet packet, Agent agent)
@@ -139,8 +130,7 @@ namespace Simian.Extensions
reply.PropertiesData.PartnerID = UUID.Zero;
reply.PropertiesData.ProfileURL = Utils.StringToBytes(String.Empty);
- agent.SendPacket(reply);
-
+ Server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Transaction);
break;
}
}
@@ -182,7 +172,7 @@ namespace Simian.Extensions
update.WearableData[4].ItemID = UUID.Random();
update.WearableData[4].WearableType = (byte)WearableType.Skin;
- agent.SendPacket(update);
+ Server.UDP.SendPacket(agent.AgentID, update, PacketCategory.Asset);
}
void AgentIsNowWearingHandler(Packet packet, Agent agent)
@@ -218,19 +208,11 @@ namespace Simian.Extensions
ObjectUpdatePacket update = Movement.BuildFullUpdate(agent.Avatar,
NameValue.NameValuesToString(agent.Avatar.NameValues), Server.RegionHandle,
agent.State, agent.Flags | PrimFlags.ObjectYouOwner);
- agent.SendPacket(update);
+ Server.UDP.SendPacket(agent.AgentID, update, PacketCategory.State);
// Send out this appearance to all other connected avatars
AvatarAppearancePacket appearance = BuildAppearancePacket(agent);
-
- lock (Server.Agents)
- {
- foreach (Agent recipient in Server.Agents.Values)
- {
- if (recipient != agent)
- recipient.SendPacket(appearance);
- }
- }
+ Server.UDP.BroadcastPacket(appearance, PacketCategory.State);
}
void UUIDNameRequestHandler(Packet packet, Agent agent)
@@ -268,7 +250,7 @@ namespace Simian.Extensions
reply.UUIDNameBlock[i].LastName = Utils.StringToBytes(kvp.Value.LastName);
}
- agent.SendPacket(reply);
+ Server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Transaction);
}
}
diff --git a/Programs/Simian/Extensions/CoarseLocationUpdates.cs b/Programs/Simian/Extensions/CoarseLocationUpdates.cs
index 4439fb04..f883125a 100644
--- a/Programs/Simian/Extensions/CoarseLocationUpdates.cs
+++ b/Programs/Simian/Extensions/CoarseLocationUpdates.cs
@@ -33,33 +33,42 @@ namespace Simian.Extensions
{
lock (Server.Agents)
{
- List avatarPositions = new List();
-
- CoarseLocationUpdatePacket update = new CoarseLocationUpdatePacket();
- update.AgentData = new CoarseLocationUpdatePacket.AgentDataBlock[Server.Agents.Count];
- update.Location = new CoarseLocationUpdatePacket.LocationBlock[Server.Agents.Count];
-
- short i = 0;
- foreach (Agent agent in Server.Agents.Values)
- {
- update.AgentData[i] = new CoarseLocationUpdatePacket.AgentDataBlock();
- update.AgentData[i].AgentID = agent.AgentID;
- update.Location[i] = new CoarseLocationUpdatePacket.LocationBlock();
- update.Location[i].X = (byte)((int)agent.Avatar.Position.X);
- update.Location[i].Y = (byte)((int)agent.Avatar.Position.Y);
- update.Location[i].Z = (byte)((int)agent.Avatar.Position.Z / 4);
- update.Index.Prey = -1;
- i++;
- }
-
- i = 0;
foreach (Agent recipient in Server.Agents.Values)
{
- update.Index.You = i;
- recipient.SendPacket(update);
- i++;
- }
+ int i = 0;
+ CoarseLocationUpdatePacket update = new CoarseLocationUpdatePacket();
+ update.Index.Prey = -1;
+ update.Index.You = 0;
+
+ update.AgentData = new CoarseLocationUpdatePacket.AgentDataBlock[Server.Agents.Count];
+ update.Location = new CoarseLocationUpdatePacket.LocationBlock[Server.Agents.Count];
+
+ // Fill in this avatar
+ update.AgentData[0] = new CoarseLocationUpdatePacket.AgentDataBlock();
+ update.AgentData[0].AgentID = recipient.AgentID;
+ update.Location[0] = new CoarseLocationUpdatePacket.LocationBlock();
+ update.Location[0].X = (byte)((int)recipient.Avatar.Position.X);
+ update.Location[0].Y = (byte)((int)recipient.Avatar.Position.Y);
+ update.Location[0].Z = (byte)((int)recipient.Avatar.Position.Z / 4);
+ ++i;
+
+ foreach (Agent agent in Server.Agents.Values)
+ {
+ if (agent != recipient)
+ {
+ update.AgentData[i] = new CoarseLocationUpdatePacket.AgentDataBlock();
+ update.AgentData[i].AgentID = agent.AgentID;
+ update.Location[i] = new CoarseLocationUpdatePacket.LocationBlock();
+ update.Location[i].X = (byte)((int)agent.Avatar.Position.X);
+ update.Location[i].Y = (byte)((int)agent.Avatar.Position.Y);
+ update.Location[i].Z = (byte)((int)agent.Avatar.Position.Z / 4);
+ ++i;
+ }
+ }
+
+ Server.UDP.SendPacket(recipient.AgentID, update, PacketCategory.State);
+ }
}
}
}
diff --git a/Programs/Simian/Extensions/ConnectionManagement.cs b/Programs/Simian/Extensions/ConnectionManagement.cs
index 9182ae9f..e41e28d4 100644
--- a/Programs/Simian/Extensions/ConnectionManagement.cs
+++ b/Programs/Simian/Extensions/ConnectionManagement.cs
@@ -15,9 +15,9 @@ namespace Simian.Extensions
public void Start()
{
- server.UDPServer.RegisterPacketCallback(PacketType.UseCircuitCode, new UDPServer.PacketCallback(UseCircuitCodeHandler));
- server.UDPServer.RegisterPacketCallback(PacketType.StartPingCheck, new UDPServer.PacketCallback(StartPingCheckHandler));
- server.UDPServer.RegisterPacketCallback(PacketType.LogoutRequest, new UDPServer.PacketCallback(LogoutRequestHandler));
+ server.UDP.RegisterPacketCallback(PacketType.UseCircuitCode, new PacketCallback(UseCircuitCodeHandler));
+ server.UDP.RegisterPacketCallback(PacketType.StartPingCheck, new PacketCallback(StartPingCheckHandler));
+ server.UDP.RegisterPacketCallback(PacketType.LogoutRequest, new PacketCallback(LogoutRequestHandler));
}
@@ -54,7 +54,7 @@ namespace Simian.Extensions
handshake.RegionInfo.TerrainStartHeight11 = 40f;
handshake.RegionInfo2.RegionID = UUID.Random();
- agent.SendPacket(handshake);
+ server.UDP.SendPacket(agent.AgentID, handshake, PacketCategory.Transaction);
}
void StartPingCheckHandler(Packet packet, Agent agent)
@@ -65,7 +65,7 @@ namespace Simian.Extensions
complete.Header.Reliable = false;
complete.PingID.PingID = start.PingID.PingID;
- agent.SendPacket(complete);
+ server.UDP.SendPacket(agent.AgentID, complete, PacketCategory.Overhead);
}
void LogoutRequestHandler(Packet packet, Agent agent)
@@ -79,24 +79,24 @@ namespace Simian.Extensions
reply.InventoryData[0] = new LogoutReplyPacket.InventoryDataBlock();
reply.InventoryData[0].ItemID = UUID.Zero;
- agent.SendPacket(reply);
+ server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Transaction);
lock (server.Agents)
{
- if (server.Agents.ContainsKey(agent.Address))
+ if (server.Agents.ContainsKey(agent.AgentID))
{
KillObjectPacket kill = new KillObjectPacket();
kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
kill.ObjectData[0].ID = agent.Avatar.LocalID;
- agent.Dispose();
- server.Agents.Remove(agent.Address);
+ server.UDP.BroadcastPacket(kill, PacketCategory.State);
- foreach (Agent recipient in server.Agents.Values)
- recipient.SendPacket(kill);
+ server.Agents.Remove(agent.AgentID);
}
}
+
+ server.UDP.RemoveClient(agent);
}
}
}
diff --git a/Programs/Simian/Extensions/ImageDelivery.cs b/Programs/Simian/Extensions/ImageDelivery.cs
index 3fcb6e03..0cd5b364 100644
--- a/Programs/Simian/Extensions/ImageDelivery.cs
+++ b/Programs/Simian/Extensions/ImageDelivery.cs
@@ -20,7 +20,7 @@ namespace Simian.Extensions
public void Start()
{
- Server.UDPServer.RegisterPacketCallback(PacketType.RequestImage, new UDPServer.PacketCallback(RequestImageHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.RequestImage, new PacketCallback(RequestImageHandler));
Bitmap defaultImage = new Bitmap(32, 32);
Graphics gfx = Graphics.FromImage(defaultImage);
@@ -69,7 +69,7 @@ namespace Simian.Extensions
}
imageData.ImageID.Size = (uint)imageData.ImageData.Data.Length;
- agent.SendPacket(imageData);
+ Server.UDP.SendPacket(agent.AgentID, imageData, PacketCategory.Texture);
}
}
}
diff --git a/Programs/Simian/Extensions/InventoryManager.cs b/Programs/Simian/Extensions/InventoryManager.cs
index 7d2504a8..835a08bc 100644
--- a/Programs/Simian/Extensions/InventoryManager.cs
+++ b/Programs/Simian/Extensions/InventoryManager.cs
@@ -16,10 +16,10 @@ namespace Simian.Extensions
public void Start()
{
- Server.UDPServer.RegisterPacketCallback(PacketType.CreateInventoryItem, new UDPServer.PacketCallback(CreateInventoryItemHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.CreateInventoryFolder, new UDPServer.PacketCallback(CreateInventoryFolderHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.UpdateInventoryItem, new UDPServer.PacketCallback(UpdateInventoryItemHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.FetchInventoryDescendents, new UDPServer.PacketCallback(FetchInventoryDescendentsHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.CreateInventoryItem, new PacketCallback(CreateInventoryItemHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.CreateInventoryFolder, new PacketCallback(CreateInventoryFolderHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.UpdateInventoryItem, new PacketCallback(UpdateInventoryItemHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.FetchInventoryDescendents, new PacketCallback(FetchInventoryDescendentsHandler));
}
public void Stop()
@@ -275,7 +275,7 @@ namespace Simian.Extensions
}
}
- agent.SendPacket(descendents);
+ Server.UDP.SendPacket(agent.AgentID, descendents, PacketCategory.Inventory);
}
}
else
@@ -358,8 +358,7 @@ namespace Simian.Extensions
update.InventoryData[0].SaleType = (byte)item.SaleType;
update.InventoryData[0].Type = (sbyte)item.AssetType;
- agent.SendPacket(update);
-
+ Server.UDP.SendPacket(agent.AgentID, update, PacketCategory.Inventory);
return item.ID;
}
else
diff --git a/Programs/Simian/Extensions/Messaging.cs b/Programs/Simian/Extensions/Messaging.cs
index d03f09cd..f45ff251 100644
--- a/Programs/Simian/Extensions/Messaging.cs
+++ b/Programs/Simian/Extensions/Messaging.cs
@@ -18,8 +18,8 @@ namespace Simian.Extensions
public void Start()
{
- Server.UDPServer.RegisterPacketCallback(PacketType.ChatFromViewer, new UDPServer.PacketCallback(ChatFromViewerHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.ImprovedInstantMessage, new UDPServer.PacketCallback(ImprovedInstantMessageHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.ChatFromViewer, new PacketCallback(ChatFromViewerHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.ImprovedInstantMessage, new PacketCallback(ImprovedInstantMessageHandler));
}
public void Stop()
@@ -44,11 +44,7 @@ namespace Simian.Extensions
chat.ChatData.FromName = Utils.StringToBytes(agent.Avatar.Name);
chat.ChatData.Message = viewerChat.ChatData.Message;
- lock (Server.Agents)
- {
- foreach(Agent recipient in Server.Agents.Values)
- recipient.SendPacket(chat);
- }
+ Server.UDP.BroadcastPacket(chat, PacketCategory.Transaction);
}
void ImprovedInstantMessageHandler(Packet packet, Agent agent)
@@ -76,8 +72,9 @@ namespace Simian.Extensions
sendIM.MessageBlock.Message = im.MessageBlock.Message;
sendIM.MessageBlock.BinaryBucket = new byte[0];
sendIM.MessageBlock.Timestamp = 0;
- sendIM.MessageBlock.Position = agent.Avatar.Position;
- recipient.SendPacket(sendIM);
+ sendIM.MessageBlock.Position = agent.Avatar.Position;
+
+ Server.UDP.SendPacket(recipient.AgentID, sendIM, PacketCategory.Transaction);
break;
}
diff --git a/Programs/Simian/Extensions/Money.cs b/Programs/Simian/Extensions/Money.cs
index 30c1c5c3..9f6381c3 100644
--- a/Programs/Simian/Extensions/Money.cs
+++ b/Programs/Simian/Extensions/Money.cs
@@ -17,8 +17,8 @@ namespace Simian.Extensions
public void Start()
{
- Server.UDPServer.RegisterPacketCallback(PacketType.MoneyBalanceRequest, new UDPServer.PacketCallback(MoneyBalanceRequestHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.MoneyTransferRequest, new UDPServer.PacketCallback(MoneyTransferRequestHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.MoneyBalanceRequest, new PacketCallback(MoneyBalanceRequestHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.MoneyTransferRequest, new PacketCallback(MoneyTransferRequestHandler));
}
public void Stop()
@@ -33,7 +33,7 @@ namespace Simian.Extensions
reply.MoneyData.TransactionID = transactionID;
reply.MoneyData.Description = Utils.StringToBytes(message);
- agent.SendPacket(reply);
+ Server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Transaction);
}
void MoneyBalanceRequestHandler(Packet packet, Agent agent)
diff --git a/Programs/Simian/Extensions/Movement.cs b/Programs/Simian/Extensions/Movement.cs
index f8af1319..e437065c 100644
--- a/Programs/Simian/Extensions/Movement.cs
+++ b/Programs/Simian/Extensions/Movement.cs
@@ -41,9 +41,9 @@ namespace Simian.Extensions
public void Start()
{
- server.UDPServer.RegisterPacketCallback(PacketType.AgentUpdate, new UDPServer.PacketCallback(AgentUpdateHandler));
- server.UDPServer.RegisterPacketCallback(PacketType.AgentHeightWidth, new UDPServer.PacketCallback(AgentHeightWidthHandler));
- server.UDPServer.RegisterPacketCallback(PacketType.SetAlwaysRun, new UDPServer.PacketCallback(SetAlwaysRunHandler));
+ server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, new PacketCallback(AgentUpdateHandler));
+ server.UDP.RegisterPacketCallback(PacketType.AgentHeightWidth, new PacketCallback(AgentHeightWidthHandler));
+ server.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, new PacketCallback(SetAlwaysRunHandler));
updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed));
LastTick = Environment.TickCount;
@@ -324,11 +324,7 @@ namespace Simian.Extensions
NameValue.NameValuesToString(agent.Avatar.NameValues), server.RegionHandle,
agent.State, agent.Flags);
- lock (server.Agents)
- {
- foreach (Agent recipient in server.Agents.Values)
- recipient.SendPacket(fullUpdate);
- }
+ server.UDP.BroadcastPacket(fullUpdate, PacketCategory.State);
}
void SetAlwaysRunHandler(Packet packet, Agent agent)
diff --git a/Programs/Simian/Extensions/ObjectManager.cs b/Programs/Simian/Extensions/ObjectManager.cs
index 8e8d041d..2d77f166 100644
--- a/Programs/Simian/Extensions/ObjectManager.cs
+++ b/Programs/Simian/Extensions/ObjectManager.cs
@@ -21,13 +21,13 @@ namespace Simian.Extensions
public void Start()
{
- Server.UDPServer.RegisterPacketCallback(PacketType.ObjectAdd, new UDPServer.PacketCallback(ObjectAddHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.ObjectSelect, new UDPServer.PacketCallback(ObjectSelectHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.ObjectDeselect, new UDPServer.PacketCallback(ObjectDeselectHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.ObjectShape, new UDPServer.PacketCallback(ObjectShapeHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.DeRezObject, new UDPServer.PacketCallback(DeRezObjectHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.MultipleObjectUpdate, new UDPServer.PacketCallback(MultipleObjectUpdateHandler));
- Server.UDPServer.RegisterPacketCallback(PacketType.RequestObjectPropertiesFamily, new UDPServer.PacketCallback(RequestObjectPropertiesFamilyHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.ObjectAdd, new PacketCallback(ObjectAddHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.ObjectSelect, new PacketCallback(ObjectSelectHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.ObjectDeselect, new PacketCallback(ObjectDeselectHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.ObjectShape, new PacketCallback(ObjectShapeHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.DeRezObject, new PacketCallback(DeRezObjectHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.MultipleObjectUpdate, new PacketCallback(MultipleObjectUpdateHandler));
+ Server.UDP.RegisterPacketCallback(PacketType.RequestObjectPropertiesFamily, new PacketCallback(RequestObjectPropertiesFamilyHandler));
}
public void Stop()
@@ -172,7 +172,7 @@ namespace Simian.Extensions
// Send an update out to the creator
ObjectUpdatePacket updateToOwner = Movement.BuildFullUpdate(prim, String.Empty, prim.RegionHandle, 0,
prim.Flags | PrimFlags.CreateSelected | PrimFlags.ObjectYouOwner);
- agent.SendPacket(updateToOwner);
+ Server.UDP.SendPacket(agent.AgentID, updateToOwner, PacketCategory.State);
// Send an update out to everyone else
ObjectUpdatePacket updateToOthers = Movement.BuildFullUpdate(prim, String.Empty, prim.RegionHandle, 0,
@@ -182,7 +182,7 @@ namespace Simian.Extensions
foreach (Agent recipient in Server.Agents.Values)
{
if (recipient != agent)
- recipient.SendPacket(updateToOthers);
+ Server.UDP.SendPacket(recipient.AgentID, updateToOthers, PacketCategory.State);
}
}
}
@@ -223,7 +223,7 @@ namespace Simian.Extensions
}
}
- agent.SendPacket(properties);
+ Server.UDP.SendPacket(agent.AgentID, properties, PacketCategory.Transaction);
}
void ObjectDeselectHandler(Packet packet, Agent agent)
@@ -266,11 +266,7 @@ namespace Simian.Extensions
// Send the update out to everyone
ObjectUpdatePacket editedobj = Movement.BuildFullUpdate(obj.Prim, String.Empty, obj.Prim.RegionHandle, 0,
obj.Prim.Flags);
- lock (Server.Agents)
- {
- foreach (Agent recipient in Server.Agents.Values)
- recipient.SendPacket(editedobj);
- }
+ Server.UDP.BroadcastPacket(editedobj, PacketCategory.State);
}
else
{
@@ -408,11 +404,7 @@ namespace Simian.Extensions
// Send the update out to everyone
ObjectUpdatePacket editedobj = Movement.BuildFullUpdate(obj.Prim, String.Empty, obj.Prim.RegionHandle, 0,
obj.Prim.Flags);
- lock (Server.Agents)
- {
- foreach (Agent recipient in Server.Agents.Values)
- recipient.SendPacket(editedobj);
- }
+ Server.UDP.BroadcastPacket(editedobj, PacketCategory.State);
}
else
{
@@ -422,7 +414,7 @@ namespace Simian.Extensions
kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
kill.ObjectData[0].ID = block.ObjectLocalID;
- agent.SendPacket(kill);
+ Server.UDP.SendPacket(agent.AgentID, kill, PacketCategory.State);
}
}
}
@@ -453,7 +445,7 @@ namespace Simian.Extensions
props.ObjectData.SalePrice = obj.Prim.Properties.SalePrice;
props.ObjectData.SaleType = (byte)obj.Prim.Properties.SaleType;
- agent.SendPacket(props);
+ Server.UDP.SendPacket(agent.AgentID, props, PacketCategory.Transaction);
}
else
{
@@ -474,11 +466,7 @@ namespace Simian.Extensions
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
kill.ObjectData[0].ID = obj.Prim.LocalID;
- lock (Server.Agents)
- {
- foreach (Agent recipient in Server.Agents.Values)
- recipient.SendPacket(kill);
- }
+ Server.UDP.BroadcastPacket(kill, PacketCategory.State);
}
Vector3 FullSceneCollisionTest(Vector3 rayStart, Vector3 rayEnd)
diff --git a/Programs/Simian/Extensions/SceneManager.cs b/Programs/Simian/Extensions/SceneManager.cs
index 28ee8c5d..045e89c3 100644
--- a/Programs/Simian/Extensions/SceneManager.cs
+++ b/Programs/Simian/Extensions/SceneManager.cs
@@ -23,7 +23,7 @@ namespace Simian.Extensions
public void Start()
{
- server.UDPServer.RegisterPacketCallback(PacketType.CompleteAgentMovement, new UDPServer.PacketCallback(CompleteAgentMovementHandler));
+ server.UDP.RegisterPacketCallback(PacketType.CompleteAgentMovement, new PacketCallback(CompleteAgentMovementHandler));
LoadTerrain(server.DataDir + "heightmap.tga");
}
@@ -72,7 +72,7 @@ namespace Simian.Extensions
complete.Data.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now);
complete.SimData.ChannelVersion = Utils.StringToBytes("Simian");
- agent.SendPacket(complete);
+ server.UDP.SendPacket(agent.AgentID, complete, PacketCategory.Transaction);
lock (server.Agents)
{
@@ -82,11 +82,11 @@ namespace Simian.Extensions
ObjectUpdatePacket update = Movement.BuildFullUpdate(otherAgent.Avatar,
NameValue.NameValuesToString(otherAgent.Avatar.NameValues),
server.RegionHandle, otherAgent.State, otherAgent.Flags);
- agent.SendPacket(update);
+ server.UDP.SendPacket(agent.AgentID, update, PacketCategory.State);
// Send appearances for this avatar
AvatarAppearancePacket appearance = AvatarManager.BuildAppearancePacket(otherAgent);
- agent.SendPacket(appearance);
+ server.UDP.SendPacket(agent.AgentID, appearance, PacketCategory.State);
}
}
@@ -136,11 +136,10 @@ namespace Simian.Extensions
int[] patches = new int[1];
patches[0] = (y * 16) + x;
LayerDataPacket layer = TerrainCompressor.CreateLandPacket(server.Heightmap, patches);
- agent.SendPacket(layer);
+ server.UDP.SendPacket(agent.AgentID, layer, PacketCategory.Terrain);
}
}
}
}
-
}
}
diff --git a/Programs/Simian/Extensions/UDPServer.cs b/Programs/Simian/Extensions/UDPServer.cs
new file mode 100644
index 00000000..ccc90dd1
--- /dev/null
+++ b/Programs/Simian/Extensions/UDPServer.cs
@@ -0,0 +1,551 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Net;
+using System.Threading;
+using OpenMetaverse;
+using OpenMetaverse.Packets;
+
+namespace Simian
+{
+ public class UDPClient
+ {
+ ///
+ public Agent Agent;
+ ///
+ public IPEndPoint Address;
+ /// Sequence numbers of packets we've received (for duplicate checking)
+ public Queue PacketArchive = new Queue();
+ /// Packets we have sent that need to be ACKed by the client
+ public Dictionary NeedAcks = new Dictionary();
+ /// ACKs that are queued up, waiting to be sent to the client
+ public SortedList PendingAcks = new SortedList();
+ /// Current packet sequence number
+ public int CurrentSequence = 0;
+
+ Timer ackTimer;
+ UDPServer udpServer;
+
+ public UDPClient(UDPServer server, Agent agent, IPEndPoint address)
+ {
+ udpServer = server;
+ Agent = agent;
+ Address = address;
+ ackTimer = new Timer(new TimerCallback(AckTimer_Elapsed), null, Settings.NETWORK_TICK_INTERVAL,
+ Settings.NETWORK_TICK_INTERVAL);
+ }
+
+ public void Dispose()
+ {
+ ackTimer.Dispose();
+ }
+
+ private void AckTimer_Elapsed(object obj)
+ {
+ udpServer.SendAcks(this);
+ udpServer.ResendUnacked(this);
+ }
+ }
+
+ public struct IncomingPacket
+ {
+ public UDPClient Client;
+ public Packet Packet;
+ }
+
+ public class UDPManager : ISimianExtension, IUDPProvider
+ {
+ Simian Server;
+ UDPServer udpServer;
+
+ public UDPManager(Simian server)
+ {
+ Server = server;
+ // Have to do this in the constructor, because we don't know that the
+ // UDP extension will be started before other extensions
+ udpServer = new UDPServer(Server.UDPPort, Server);
+ }
+
+ public void Start()
+ {
+ }
+
+ public void Stop()
+ {
+ udpServer.Stop();
+ }
+
+ public void AddClient(Agent agent, IPEndPoint endpoint)
+ {
+ udpServer.AddClient(agent, endpoint);
+ }
+
+ public bool RemoveClient(Agent agent)
+ {
+ return udpServer.RemoveClient(agent);
+ }
+
+ public bool RemoveClient(Agent agent, IPEndPoint endpoint)
+ {
+ return udpServer.RemoveClient(agent, endpoint);
+ }
+
+ public void SendPacket(UUID agentID, Packet packet, PacketCategory category)
+ {
+ udpServer.SendPacket(agentID, packet, category);
+ }
+
+ public void BroadcastPacket(Packet packet, PacketCategory category)
+ {
+ udpServer.BroadcastPacket(packet, category);
+ }
+
+ public void RegisterPacketCallback(PacketType type, PacketCallback callback)
+ {
+ udpServer.RegisterPacketCallback(type, callback);
+ }
+ }
+
+ public class UDPServer : UDPBase
+ {
+ /// This is only used to fetch unassociated agents, which will
+ /// be exposed through a login interface at some point
+ Simian server;
+ /// Handlers for incoming packets
+ PacketEventDictionary packetEvents = new PacketEventDictionary();
+ /// Incoming packets that are awaiting handling
+ BlockingQueue packetInbox = new BlockingQueue(Settings.PACKET_INBOX_SIZE);
+ ///
+ DoubleDictionary clients = new DoubleDictionary();
+
+ public UDPServer(int port, Simian server)
+ : base(port)
+ {
+ this.server = server;
+
+ Start();
+
+ // Start the incoming packet processing thread
+ Thread incomingThread = new Thread(new ThreadStart(IncomingPacketHandler));
+ incomingThread.Start();
+ }
+
+ public void RegisterPacketCallback(PacketType type, PacketCallback callback)
+ {
+ packetEvents.RegisterEvent(type, callback);
+ }
+
+ public void AddClient(Agent agent, IPEndPoint endpoint)
+ {
+ UDPClient client = new UDPClient(this, agent, endpoint);
+ clients.Add(agent.AgentID, endpoint, client);
+ }
+
+ public bool RemoveClient(Agent agent)
+ {
+ UDPClient client;
+ if (clients.TryGetValue(agent.AgentID, out client))
+ return clients.Remove(agent.AgentID, client.Address);
+ else
+ return false;
+ }
+
+ public bool RemoveClient(Agent agent, IPEndPoint endpoint)
+ {
+ return clients.Remove(agent.AgentID, endpoint);
+ }
+
+ public void BroadcastPacket(Packet packet, PacketCategory category)
+ {
+ clients.ForEach(
+ delegate(UDPClient client) { SendPacket(client, packet, category, true); });
+ }
+
+ public void SendPacket(UUID agentID, Packet packet, PacketCategory category)
+ {
+ // Look up the UDPClient this is going to
+ UDPClient client;
+ if (!clients.TryGetValue(agentID, out client))
+ {
+ Logger.Log("Attempted to send a packet to unknown UDP client " +
+ agentID.ToString(), Helpers.LogLevel.Warning);
+ return;
+ }
+
+ SendPacket(client, packet, category, true);
+ }
+
+ void SendPacket(UDPClient client, Packet packet, PacketCategory category, bool setSequence)
+ {
+ byte[] buffer;
+ int bytes;
+
+ // Keep track of when this packet was sent out
+ packet.TickCount = Environment.TickCount;
+
+ if (setSequence)
+ {
+ // Reset to zero if we've hit the upper sequence number limit
+ Interlocked.CompareExchange(ref client.CurrentSequence, 0, 0xFFFFFF);
+ // Increment and fetch the current sequence number
+ uint sequence = (uint)Interlocked.Increment(ref client.CurrentSequence);
+ packet.Header.Sequence = sequence;
+
+ if (packet.Header.Reliable)
+ {
+ // Add this packet to the list of ACK responses we are waiting on from this client
+ lock (client.NeedAcks)
+ client.NeedAcks[sequence] = packet;
+
+ if (packet.Header.Resent)
+ {
+ // This packet has already been sent out once, strip any appended ACKs
+ // off it and reinsert them into the outgoing ACK queue under the
+ // assumption that this packet will continually be rejected from the
+ // client or that the appended ACKs are possibly making the delivery fail
+ if (packet.Header.AckList.Length > 0)
+ {
+ Logger.DebugLog(String.Format("Purging ACKs from packet #{0} ({1}) which will be resent.",
+ packet.Header.Sequence, packet.GetType()));
+
+ lock (client.PendingAcks)
+ {
+ foreach (uint ack in packet.Header.AckList)
+ {
+ if (!client.PendingAcks.ContainsKey(ack))
+ client.PendingAcks[ack] = ack;
+ }
+ }
+
+ packet.Header.AppendedAcks = false;
+ packet.Header.AckList = new uint[0];
+ }
+ }
+ else
+ {
+ // This packet is not a resend, check if the conditions are favorable
+ // to ACK appending
+ if (packet.Type != PacketType.PacketAck)
+ {
+ lock (client.PendingAcks)
+ {
+ int count = client.PendingAcks.Count;
+
+ if (count > 0 && count < 10)
+ {
+ // Append all of the queued up outgoing ACKs to this packet
+ packet.Header.AckList = new uint[count];
+
+ for (int i = 0; i < count; i++)
+ packet.Header.AckList[i] = client.PendingAcks.Values[i];
+
+ client.PendingAcks.Clear();
+ packet.Header.AppendedAcks = true;
+ }
+ }
+ }
+ }
+ }
+ else if (packet.Header.AckList.Length > 0)
+ {
+ // Sanity check for ACKS appended on an unreliable packet, this is bad form
+ Logger.Log("Sending appended ACKs on an unreliable packet", Helpers.LogLevel.Warning);
+ }
+ }
+
+ // Serialize the packet
+ buffer = packet.ToBytes();
+ bytes = buffer.Length;
+ //Stats.SentBytes += (ulong)bytes;
+ //++Stats.SentPackets;
+
+ UDPPacketBuffer buf = new UDPPacketBuffer(client.Address);
+
+ // Zerocode if needed
+ if (packet.Header.Zerocoded)
+ bytes = Helpers.ZeroEncode(buffer, bytes, buf.Data);
+ else
+ Buffer.BlockCopy(buffer, 0, buf.Data, 0, bytes);
+
+ buf.DataLength = bytes;
+
+ AsyncBeginSend(buf);
+ }
+
+ void QueueAck(UDPClient client, uint ack)
+ {
+ // Add this packet to the list of ACKs that need to be sent out
+ lock (client.PendingAcks)
+ client.PendingAcks[ack] = ack;
+
+ // Send out ACKs if we have a lot of them
+ if (client.PendingAcks.Count >= 10)
+ SendAcks(client);
+ }
+
+ void ProcessAcks(UDPClient client, List acks)
+ {
+ lock (client.NeedAcks)
+ {
+ foreach (uint ack in acks)
+ client.NeedAcks.Remove(ack);
+ }
+ }
+
+ void SendAck(UDPClient client, uint ack)
+ {
+ PacketAckPacket acks = new PacketAckPacket();
+ acks.Header.Reliable = false;
+ acks.Packets = new PacketAckPacket.PacketsBlock[1];
+ acks.Packets[0] = new PacketAckPacket.PacketsBlock();
+ acks.Packets[0].ID = ack;
+
+ SendPacket(client, acks, PacketCategory.Overhead, true);
+ }
+
+ public void SendAcks(UDPClient client)
+ {
+ PacketAckPacket acks = null;
+
+ lock (client.PendingAcks)
+ {
+ int count = client.PendingAcks.Count;
+
+ if (count > 250)
+ {
+ Logger.Log("Too many ACKs queued up!", Helpers.LogLevel.Error);
+ return;
+ }
+ else if (count > 0)
+ {
+ acks = new PacketAckPacket();
+ acks.Header.Reliable = false;
+ acks.Packets = new PacketAckPacket.PacketsBlock[count];
+
+ for (int i = 0; i < count; i++)
+ {
+ acks.Packets[i] = new PacketAckPacket.PacketsBlock();
+ acks.Packets[i].ID = client.PendingAcks.Values[i];
+ }
+
+ client.PendingAcks.Clear();
+ }
+ }
+
+ if (acks != null)
+ SendPacket(client, acks, PacketCategory.Overhead, true);
+ }
+
+ public void ResendUnacked(UDPClient client)
+ {
+ lock (client.NeedAcks)
+ {
+ List dropAck = new List();
+ int now = Environment.TickCount;
+
+ // Resend packets
+ foreach (Packet packet in client.NeedAcks.Values)
+ {
+ if (packet.TickCount != 0 && now - packet.TickCount > 4000)
+ {
+ if (packet.ResendCount < 3)
+ {
+ Logger.DebugLog(String.Format("Resending packet #{0} ({1}), {2}ms have passed",
+ packet.Header.Sequence, packet.GetType(), now - packet.TickCount));
+
+ packet.TickCount = 0;
+ packet.Header.Resent = true;
+ //++Stats.ResentPackets;
+ ++packet.ResendCount;
+
+ SendPacket(client, packet, PacketCategory.Overhead, false);
+ }
+ else
+ {
+ Logger.Log(String.Format("Dropping packet #{0} ({1}) after {2} failed attempts",
+ packet.Header.Sequence, packet.GetType(), packet.ResendCount), Helpers.LogLevel.Warning);
+
+ dropAck.Add(packet.Header.Sequence);
+ }
+ }
+ }
+
+ if (dropAck.Count != 0)
+ {
+ for (int i = 0; i < dropAck.Count; i++)
+ client.NeedAcks.Remove(dropAck[i]);
+ }
+ }
+ }
+
+ protected override void PacketReceived(UDPPacketBuffer buffer)
+ {
+ UDPClient client = null;
+ Packet packet = null;
+ int packetEnd = buffer.DataLength - 1;
+ IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint;
+
+ // Decoding
+ try
+ {
+ packet = Packet.BuildPacket(buffer.Data, ref packetEnd, buffer.ZeroData);
+ }
+ catch (MalformedDataException)
+ {
+ Logger.Log(String.Format("Malformed data, cannot parse packet:\n{0}",
+ Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)), Helpers.LogLevel.Error);
+ }
+
+ // Fail-safe check
+ if (packet == null)
+ {
+ Logger.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning);
+ return;
+ }
+
+ //Stats.RecvBytes += (ulong)buffer.DataLength;
+ //++Stats.RecvPackets;
+
+ if (packet.Type == PacketType.UseCircuitCode)
+ {
+ UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet;
+
+ Agent agent;
+ if (server.CompleteAgentConnection(useCircuitCode.CircuitCode.Code, out agent))
+ {
+ AddClient(agent, address);
+ if (clients.TryGetValue(agent.AgentID, out client))
+ {
+ Logger.Log("Activated UDP circuit " + useCircuitCode.CircuitCode.Code, Helpers.LogLevel.Info);
+ }
+ else
+ {
+ Logger.Log("Failed to locate newly created UDPClient", Helpers.LogLevel.Error);
+ return;
+ }
+ }
+ else
+ {
+ Logger.Log("Received a UseCircuitCode packet for an unrecognized circuit: " +
+ useCircuitCode.CircuitCode.Code.ToString(), Helpers.LogLevel.Warning);
+ return;
+ }
+ }
+ else
+ {
+ // Determine which agent this packet came from
+ if (!clients.TryGetValue(address, out client))
+ {
+ Logger.Log("Received UDP packet from an unrecognized source: " + address.ToString(),
+ Helpers.LogLevel.Warning);
+ return;
+ }
+ }
+
+ // Reliable handling
+ if (packet.Header.Reliable)
+ {
+ // Queue up this sequence number for acknowledgement
+ QueueAck(client, (uint)packet.Header.Sequence);
+ //if (packet.Header.Resent) ++Stats.ReceivedResends;
+ }
+
+ // Inbox insertion
+ IncomingPacket incomingPacket;
+ incomingPacket.Client = client;
+ incomingPacket.Packet = packet;
+
+ // TODO: Prioritize the queue
+ packetInbox.Enqueue(incomingPacket);
+ }
+
+ protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent)
+ {
+ }
+
+ private void IncomingPacketHandler()
+ {
+ IncomingPacket incomingPacket = new IncomingPacket();
+ Packet packet = null;
+ UDPClient client = null;
+
+ while (IsRunning)
+ {
+ // Reset packet to null for the check below
+ packet = null;
+
+ if (packetInbox.Dequeue(100, ref incomingPacket))
+ {
+ packet = incomingPacket.Packet;
+ client = incomingPacket.Client;
+
+ if (packet != null)
+ {
+ #region ACK accounting
+
+ // Check the archives to see whether we already received this packet
+ lock (client.PacketArchive)
+ {
+ if (client.PacketArchive.Contains(packet.Header.Sequence))
+ {
+ if (packet.Header.Resent)
+ {
+ Logger.DebugLog("Received resent packet #" + packet.Header.Sequence);
+ }
+ else
+ {
+ Logger.Log(String.Format("Received a duplicate of packet #{0}, current type: {1}",
+ packet.Header.Sequence, packet.Type), Helpers.LogLevel.Warning);
+ }
+
+ // Avoid firing a callback twice for the same packet
+ continue;
+ }
+ else
+ {
+ // Keep the PacketArchive size within a certain capacity
+ while (client.PacketArchive.Count >= Settings.PACKET_ARCHIVE_SIZE)
+ {
+ client.PacketArchive.Dequeue(); client.PacketArchive.Dequeue();
+ client.PacketArchive.Dequeue(); client.PacketArchive.Dequeue();
+ }
+
+ client.PacketArchive.Enqueue(packet.Header.Sequence);
+ }
+ }
+
+ #endregion ACK accounting
+
+ #region ACK handling
+
+ // Handle appended ACKs
+ if (packet.Header.AppendedAcks)
+ {
+ lock (client.NeedAcks)
+ {
+ for (int i = 0; i < packet.Header.AckList.Length; i++)
+ client.NeedAcks.Remove(packet.Header.AckList[i]);
+ }
+ }
+
+ // Handle PacketAck packets
+ if (packet.Type == PacketType.PacketAck)
+ {
+ PacketAckPacket ackPacket = (PacketAckPacket)packet;
+
+ lock (client.NeedAcks)
+ {
+ for (int i = 0; i < ackPacket.Packets.Length; i++)
+ client.NeedAcks.Remove(ackPacket.Packets[i].ID);
+ }
+ }
+
+ #endregion ACK handling
+
+ packetEvents.BeginRaiseEvent(packet.Type, packet, client.Agent);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Programs/Simian/Interfaces/IUDPProvider.cs b/Programs/Simian/Interfaces/IUDPProvider.cs
new file mode 100644
index 00000000..8ae50439
--- /dev/null
+++ b/Programs/Simian/Interfaces/IUDPProvider.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Net;
+using OpenMetaverse;
+using OpenMetaverse.Packets;
+
+namespace Simian
+{
+ public enum PacketCategory
+ {
+ /// Any sort of transactional message, such as
+ /// AgentMovementComplete
+ Transaction = 0,
+ /// State synchronization, such as animations or
+ /// object updates
+ State,
+ /// State synchronization of inventory
+ Inventory,
+ /// State synchronization of terrain, LayerData
+ /// packets
+ Terrain,
+ /// Asset transfer packets
+ Asset,
+ /// Texture transfer packets
+ Texture,
+ /// Protocol overhead such as PacketAck
+ Overhead,
+ }
+
+ ///
+ /// Coupled with RegisterCallback(), this is triggered whenever a packet
+ /// of a registered type is received
+ ///
+ public delegate void PacketCallback(Packet packet, Agent agent);
+
+ public interface IUDPProvider
+ {
+ void AddClient(Agent agent, IPEndPoint endpoint);
+ bool RemoveClient(Agent agent);
+ bool RemoveClient(Agent agent, IPEndPoint endpoint);
+
+ void SendPacket(UUID agentID, Packet packet, PacketCategory category);
+
+ void BroadcastPacket(Packet packet, PacketCategory category);
+
+ void RegisterPacketCallback(PacketType type, PacketCallback callback);
+ }
+}
diff --git a/Programs/Simian/Simian.cs b/Programs/Simian/Simian.cs
index 70684126..25f2d6a5 100644
--- a/Programs/Simian/Simian.cs
+++ b/Programs/Simian/Simian.cs
@@ -13,30 +13,30 @@ namespace Simian
{
public partial class Simian
{
+ public int UDPPort = 9000;
+ public int HttpPort = 9000;
public string DataDir = "SimianData/";
public HttpServer HttpServer;
- public UDPServer UDPServer;
public ulong RegionHandle;
public float[] Heightmap = new float[65536];
public float WaterHeight = 35.0f;
public Dictionary AssetStore = new Dictionary();
// Interfaces
+ public IUDPProvider UDP;
public IAvatarProvider Avatars;
public IInventoryProvider Inventory;
public IMeshingProvider Mesher;
/// All of the agents currently connected to this UDP server
- public Dictionary Agents = new Dictionary();
+ public Dictionary Agents = new Dictionary();
const uint regionX = 256000;
const uint regionY = 256000;
Dictionary unassociatedAgents;
int currentCircuitCode;
- int tcpPort;
- int udpPort;
public Simian()
{
@@ -46,12 +46,10 @@ namespace Simian
public void Start(int port, bool ssl)
{
- // Put UDP listening on the same port number as the HTTP server for simplicity
- tcpPort = port;
- udpPort = port;
+ HttpPort = port;
+ UDPPort = port;
- InitUDPServer(udpPort);
- InitHttpServer(tcpPort, ssl);
+ InitHttpServer(HttpPort, ssl);
RegionHandle = Helpers.UIntsToLong(regionX, regionY);
@@ -60,13 +58,17 @@ namespace Simian
foreach (ISimianExtension extension in ExtensionLoader.Extensions)
{
- Logger.DebugLog("Loading extension " + extension.GetType().Name);
- extension.Start();
-
// Assign to an interface if possible
TryAssignToInterface(extension);
}
+ foreach (ISimianExtension extension in ExtensionLoader.Extensions)
+ {
+ // Start the interfaces
+ Logger.DebugLog("Loading extension " + extension.GetType().Name);
+ extension.Start();
+ }
+
if (!CheckInterfaces())
{
Logger.Log("Missing interfaces, shutting down", Helpers.LogLevel.Error);
@@ -79,10 +81,26 @@ namespace Simian
foreach (ISimianExtension extension in ExtensionLoader.Extensions)
extension.Stop();
- UDPServer.Stop();
HttpServer.Stop();
}
+ public bool CompleteAgentConnection(uint circuitCode, out Agent agent)
+ {
+ if (unassociatedAgents.TryGetValue(circuitCode, out agent))
+ {
+ lock (Agents)
+ Agents[agent.AgentID] = agent;
+ lock (unassociatedAgents)
+ unassociatedAgents.Remove(circuitCode);
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
public bool TryGetUnassociatedAgent(uint circuitCode, out Agent agent)
{
if (unassociatedAgents.TryGetValue(circuitCode, out agent))
@@ -100,7 +118,11 @@ namespace Simian
void TryAssignToInterface(ISimianExtension extension)
{
- if (extension is IAvatarProvider)
+ if (extension is IUDPProvider)
+ {
+ UDP = (IUDPProvider)extension;
+ }
+ else if (extension is IAvatarProvider)
{
Avatars = (IAvatarProvider)extension;
}
@@ -116,7 +138,12 @@ namespace Simian
bool CheckInterfaces()
{
- if (Avatars == null)
+ if (UDP == null)
+ {
+ Logger.Log("No IUDPProvider interface loaded", Helpers.LogLevel.Error);
+ return false;
+ }
+ else if (Avatars == null)
{
Logger.Log("No IAvatarProvider interface loaded", Helpers.LogLevel.Error);
return false;
@@ -135,14 +162,9 @@ namespace Simian
return true;
}
- void InitUDPServer(int port)
- {
- UDPServer = new UDPServer(port, this);
- }
-
void InitHttpServer(int port, bool ssl)
{
- HttpServer = new HttpServer(tcpPort, ssl);
+ HttpServer = new HttpServer(HttpPort, ssl);
// Login webpage HEAD request, used to check if the login webpage is alive
HttpRequestSignature signature = new HttpRequestSignature();
@@ -301,7 +323,7 @@ namespace Simian
uint regionX = 256000;
uint regionY = 256000;
- Agent agent = new Agent(UDPServer);
+ Agent agent = new Agent();
agent.AgentID = UUID.Random();
agent.FirstName = firstName;
agent.LastName = lastName;
@@ -361,9 +383,9 @@ namespace Simian
response.RegionY = regionY;
response.SecondsSinceEpoch = DateTime.Now;
// FIXME: Actually generate a seed capability
- response.SeedCapability = String.Format("http://{0}:{1}/seed_caps", simIP, tcpPort);
+ response.SeedCapability = String.Format("http://{0}:{1}/seed_caps", simIP, HttpPort);
response.SimIP = simIP;
- response.SimPort = (ushort)udpPort;
+ response.SimPort = (ushort)UDPPort;
response.StartLocation = "last";
response.Success = true;
diff --git a/Programs/Simian/UDPServer.cs b/Programs/Simian/UDPServer.cs
deleted file mode 100644
index c2a6a8a6..00000000
--- a/Programs/Simian/UDPServer.cs
+++ /dev/null
@@ -1,217 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Net;
-using System.Threading;
-using OpenMetaverse;
-using OpenMetaverse.Packets;
-
-namespace Simian
-{
- public struct IncomingPacket
- {
- public Agent Agent;
- public Packet Packet;
- }
-
- public class UDPServer : UDPBase
- {
- ///
- /// Coupled with RegisterCallback(), this is triggered whenever a packet
- /// of a registered type is received
- ///
- public delegate void PacketCallback(Packet packet, Agent agent);
-
- /// This is only used to fetch unassociated agents, which will
- /// be exposed through a login interface at some point
- Simian server;
- /// Handlers for incoming packets
- PacketEventDictionary packetEvents = new PacketEventDictionary();
- /// Incoming packets that are awaiting handling
- BlockingQueue packetInbox = new BlockingQueue(Settings.PACKET_INBOX_SIZE);
-
- public UDPServer(int port, Simian server)
- : base(port)
- {
- this.server = server;
-
- Start();
-
- // Start the incoming packet processing thread
- Thread incomingThread = new Thread(new ThreadStart(IncomingPacketHandler));
- incomingThread.Start();
- }
-
- public void RegisterPacketCallback(PacketType type, PacketCallback callback)
- {
- packetEvents.RegisterEvent(type, callback);
- }
-
- protected override void PacketReceived(UDPPacketBuffer buffer)
- {
- Agent agent = null;
- Packet packet = null;
- int packetEnd = buffer.DataLength - 1;
-
- // Decoding
- try
- {
- packet = Packet.BuildPacket(buffer.Data, ref packetEnd, buffer.ZeroData);
- }
- catch (MalformedDataException)
- {
- Logger.Log(String.Format("Malformed data, cannot parse packet:\n{0}",
- Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)), Helpers.LogLevel.Error);
- }
-
- // Fail-safe check
- if (packet == null)
- {
- Logger.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning);
- return;
- }
-
- //Stats.RecvBytes += (ulong)buffer.DataLength;
- //++Stats.RecvPackets;
-
- if (packet.Type == PacketType.UseCircuitCode)
- {
- UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet;
-
- if (server.TryGetUnassociatedAgent(useCircuitCode.CircuitCode.Code, out agent))
- {
- agent.Initialize((IPEndPoint)buffer.RemoteEndPoint);
-
- lock (server.Agents)
- server.Agents[(IPEndPoint)buffer.RemoteEndPoint] = agent;
-
- Logger.Log("Activated UDP circuit " + useCircuitCode.CircuitCode.Code, Helpers.LogLevel.Info);
-
- //agent.SendAck(useCircuitCode.Header.Sequence);
- }
- else
- {
- Logger.Log("Received a UseCircuitCode packet for an unrecognized circuit: " + useCircuitCode.CircuitCode.Code.ToString(),
- Helpers.LogLevel.Warning);
- return;
- }
- }
- else
- {
- // Determine which agent this packet came from
- if (!server.Agents.TryGetValue((IPEndPoint)buffer.RemoteEndPoint, out agent))
- {
- Logger.Log("Received UDP packet from an unrecognized source: " + ((IPEndPoint)buffer.RemoteEndPoint).ToString(),
- Helpers.LogLevel.Warning);
- return;
- }
- }
-
- // Reliable handling
- if (packet.Header.Reliable)
- {
- // Queue up this sequence number for acknowledgement
- agent.QueueAck((uint)packet.Header.Sequence);
-
- //if (packet.Header.Resent) ++Stats.ReceivedResends;
- }
-
- // Inbox insertion
- IncomingPacket incomingPacket;
- incomingPacket.Agent = agent;
- incomingPacket.Packet = packet;
-
- // TODO: Prioritize the queue
- packetInbox.Enqueue(incomingPacket);
- }
-
- protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent)
- {
- }
-
- private void IncomingPacketHandler()
- {
- IncomingPacket incomingPacket = new IncomingPacket();
- Packet packet = null;
- Agent agent = null;
-
- while (IsRunning)
- {
- // Reset packet to null for the check below
- packet = null;
-
- if (packetInbox.Dequeue(100, ref incomingPacket))
- {
- packet = incomingPacket.Packet;
- agent = incomingPacket.Agent;
-
- if (packet != null)
- {
- #region ACK accounting
-
- // Check the archives to see whether we already received this packet
- lock (agent.packetArchive)
- {
- if (agent.packetArchive.Contains(packet.Header.Sequence))
- {
- if (packet.Header.Resent)
- {
- Logger.DebugLog("Received resent packet #" + packet.Header.Sequence);
- }
- else
- {
- Logger.Log(String.Format("Received a duplicate of packet #{0}, current type: {1}",
- packet.Header.Sequence, packet.Type), Helpers.LogLevel.Warning);
- }
-
- // Avoid firing a callback twice for the same packet
- continue;
- }
- else
- {
- // Keep the PacketArchive size within a certain capacity
- while (agent.packetArchive.Count >= Settings.PACKET_ARCHIVE_SIZE)
- {
- agent.packetArchive.Dequeue(); agent.packetArchive.Dequeue();
- agent.packetArchive.Dequeue(); agent.packetArchive.Dequeue();
- }
-
- agent.packetArchive.Enqueue(packet.Header.Sequence);
- }
- }
-
- #endregion ACK accounting
-
- #region ACK handling
-
- // Handle appended ACKs
- if (packet.Header.AppendedAcks)
- {
- lock (agent.needAcks)
- {
- for (int i = 0; i < packet.Header.AckList.Length; i++)
- agent.needAcks.Remove(packet.Header.AckList[i]);
- }
- }
-
- // Handle PacketAck packets
- if (packet.Type == PacketType.PacketAck)
- {
- PacketAckPacket ackPacket = (PacketAckPacket)packet;
-
- lock (agent.needAcks)
- {
- for (int i = 0; i < ackPacket.Packets.Length; i++)
- agent.needAcks.Remove(ackPacket.Packets[i].ID);
- }
- }
-
- #endregion ACK handling
-
- packetEvents.BeginRaiseEvent(packet.Type, packet, agent);
- }
- }
- }
- }
- }
-}