From 02ebca8b023ad3f7b4f8736aa81ff4997ab659ab Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 1 Oct 2008 00:17:49 +0000 Subject: [PATCH] Simian: * OutgoingPacket fixes, needs more testing with slow/disconnected clients * Switch AvatarPropertiesRequestHandler to use actual profile data * Switch AvatarWearablesRequestHandler to use actual wearables data * Respond to AgentCachedTexture inquiries with empty cache data * Improve UUIDNameRequest performance git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@2252 52acb1d6-8a22-11de-b505-999d5b087335 --- Programs/Simian/Extensions/AvatarManager.cs | 324 ++++++++++++++------ Programs/Simian/Extensions/UDPServer.cs | 75 ++--- 2 files changed, 258 insertions(+), 141 deletions(-) diff --git a/Programs/Simian/Extensions/AvatarManager.cs b/Programs/Simian/Extensions/AvatarManager.cs index 882da01d..c7151bd1 100644 --- a/Programs/Simian/Extensions/AvatarManager.cs +++ b/Programs/Simian/Extensions/AvatarManager.cs @@ -24,6 +24,7 @@ namespace Simian.Extensions 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.AgentCachedTexture, new PacketCallback(AgentCachedTextureHandler)); Server.UDP.RegisterPacketCallback(PacketType.AgentAnimation, new PacketCallback(AgentAnimationHandler)); Server.UDP.RegisterPacketCallback(PacketType.SoundTrigger, new PacketCallback(SoundTriggerHandler)); Server.UDP.RegisterPacketCallback(PacketType.ViewerEffect, new PacketCallback(ViewerEffectHandler)); @@ -125,70 +126,184 @@ namespace Simian.Extensions { AvatarPropertiesRequestPacket request = (AvatarPropertiesRequestPacket)packet; - lock (Server.Agents) + Agent foundAgent; + if (Server.Agents.TryGetValue(request.AgentData.AvatarID, out foundAgent)) { - foreach (Agent agt in Server.Agents.Values) - { - if (agent.AgentID == request.AgentData.AvatarID) - { - AvatarPropertiesReplyPacket reply = new AvatarPropertiesReplyPacket(); - reply.AgentData.AgentID = agt.AgentID; - reply.AgentData.AvatarID = request.AgentData.AvatarID; - reply.PropertiesData.AboutText = Utils.StringToBytes("Profile info unavailable"); - reply.PropertiesData.BornOn = Utils.StringToBytes("Unknown"); - reply.PropertiesData.CharterMember = Utils.StringToBytes("Test User"); - reply.PropertiesData.FLAboutText = Utils.StringToBytes("First life info unavailable"); - reply.PropertiesData.Flags = 0; - //TODO: at least generate static image uuids based on name. - //this will prevent re-caching the default image for the same av name. - reply.PropertiesData.FLImageID = agent.AgentID; //temporary hack - reply.PropertiesData.ImageID = agent.AgentID; //temporary hack - reply.PropertiesData.PartnerID = UUID.Zero; - reply.PropertiesData.ProfileURL = Utils.StringToBytes(String.Empty); + AvatarPropertiesReplyPacket reply = new AvatarPropertiesReplyPacket(); + reply.AgentData.AgentID = agent.AgentID; + reply.AgentData.AvatarID = request.AgentData.AvatarID; + reply.PropertiesData.AboutText = Utils.StringToBytes(foundAgent.ProfileAboutText); + reply.PropertiesData.BornOn = Utils.StringToBytes(foundAgent.ProfileBornOn); + reply.PropertiesData.CharterMember = new byte[1]; + reply.PropertiesData.FLAboutText = Utils.StringToBytes(foundAgent.ProfileFirstText); + reply.PropertiesData.Flags = (uint)foundAgent.ProfileFlags; + reply.PropertiesData.FLImageID = foundAgent.ProfileFirstImage; + reply.PropertiesData.ImageID = foundAgent.ProfileImage; + reply.PropertiesData.PartnerID = foundAgent.PartnerID; + reply.PropertiesData.ProfileURL = Utils.StringToBytes(foundAgent.ProfileURL); - Server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Transaction); - break; - } - } + Server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Transaction); } + else + { + Logger.Log("AvatarPropertiesRequest for unknown agent " + request.AgentData.AvatarID.ToString(), + Helpers.LogLevel.Warning); + } + } + + int CountWearables(Agent agent) + { + int wearables = 0; + + if (agent.ShapeAsset != UUID.Zero) ++wearables; + if (agent.SkinAsset != UUID.Zero) ++wearables; + if (agent.HairAsset != UUID.Zero) ++wearables; + if (agent.EyesAsset != UUID.Zero) ++wearables; + if (agent.ShirtAsset != UUID.Zero) ++wearables; + if (agent.PantsAsset != UUID.Zero) ++wearables; + if (agent.ShoesAsset != UUID.Zero) ++wearables; + if (agent.SocksAsset != UUID.Zero) ++wearables; + if (agent.JacketAsset != UUID.Zero) ++wearables; + if (agent.GlovesAsset != UUID.Zero) ++wearables; + if (agent.UndershirtAsset != UUID.Zero) ++wearables; + if (agent.UnderpantsAsset != UUID.Zero) ++wearables; + if (agent.SkirtAsset != UUID.Zero) ++wearables; + + return wearables; + } + + void SendWearables(Agent agent) + { + AgentWearablesUpdatePacket update = new AgentWearablesUpdatePacket(); + update.AgentData.AgentID = agent.AgentID; + + // Count the number of WearableData blocks needed + int wearableCount = CountWearables(agent); + int i = 0; + + update.WearableData = new AgentWearablesUpdatePacket.WearableDataBlock[wearableCount]; + + #region WearableData Blocks + + if (agent.ShapeAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.ShapeAsset; + update.WearableData[i].ItemID = agent.ShapeItem; + update.WearableData[i].WearableType = (byte)WearableType.Shape; + ++i; + } + if (agent.SkinAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.SkinAsset; + update.WearableData[i].ItemID = agent.SkinItem; + update.WearableData[i].WearableType = (byte)WearableType.Skin; + ++i; + } + if (agent.HairAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.HairAsset; + update.WearableData[i].ItemID = agent.HairItem; + update.WearableData[i].WearableType = (byte)WearableType.Hair; + ++i; + } + if (agent.EyesAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.EyesAsset; + update.WearableData[i].ItemID = agent.EyesItem; + update.WearableData[i].WearableType = (byte)WearableType.Eyes; + ++i; + } + if (agent.ShirtAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.ShirtAsset; + update.WearableData[i].ItemID = agent.ShirtItem; + update.WearableData[i].WearableType = (byte)WearableType.Shirt; + ++i; + } + if (agent.PantsAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.PantsAsset; + update.WearableData[i].ItemID = agent.PantsItem; + update.WearableData[i].WearableType = (byte)WearableType.Pants; + ++i; + } + if (agent.ShoesAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.ShoesAsset; + update.WearableData[i].ItemID = agent.ShoesItem; + update.WearableData[i].WearableType = (byte)WearableType.Shoes; + ++i; + } + if (agent.SocksAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.SocksAsset; + update.WearableData[i].ItemID = agent.SocksItem; + update.WearableData[i].WearableType = (byte)WearableType.Socks; + ++i; + } + if (agent.JacketAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.JacketAsset; + update.WearableData[i].ItemID = agent.JacketItem; + update.WearableData[i].WearableType = (byte)WearableType.Jacket; + ++i; + } + if (agent.GlovesAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.GlovesAsset; + update.WearableData[i].ItemID = agent.GlovesItem; + update.WearableData[i].WearableType = (byte)WearableType.Gloves; + ++i; + } + if (agent.UndershirtAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.UndershirtAsset; + update.WearableData[i].ItemID = agent.UndershirtItem; + update.WearableData[i].WearableType = (byte)WearableType.Undershirt; + ++i; + } + if (agent.UnderpantsAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.UnderpantsAsset; + update.WearableData[i].ItemID = agent.UnderpantsItem; + update.WearableData[i].WearableType = (byte)WearableType.Underpants; + ++i; + } + if (agent.SkirtAsset != UUID.Zero) + { + update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); + update.WearableData[i].AssetID = agent.SkirtAsset; + update.WearableData[i].ItemID = agent.SkirtItem; + update.WearableData[i].WearableType = (byte)WearableType.Skirt; + ++i; + } + + #endregion WearableData Blocks + + // Technically this should be per-agent, but if the only requirement is that it + // increments this is easier + update.AgentData.SerialNum = (uint)Interlocked.Increment(ref currentWearablesSerialNum); + + Logger.DebugLog(String.Format("Sending info about {0} wearables", wearableCount)); + + Server.UDP.SendPacket(agent.AgentID, update, PacketCategory.Asset); } void AgentWearablesRequestHandler(Packet packet, Agent agent) { - AgentWearablesUpdatePacket update = new AgentWearablesUpdatePacket(); - update.AgentData.AgentID = agent.AgentID; - // Technically this should be per-agent, but if the only requirement is that it - // increments this is easier - update.AgentData.SerialNum = (uint)Interlocked.Increment(ref currentWearablesSerialNum); - update.WearableData = new AgentWearablesUpdatePacket.WearableDataBlock[5]; - - // TODO: These are hardcoded in for now, should change that - update.WearableData[0] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[0].AssetID = new UUID("dc675529-7ba5-4976-b91d-dcb9e5e36188"); - update.WearableData[0].ItemID = UUID.Random(); - update.WearableData[0].WearableType = (byte)WearableType.Hair; - - update.WearableData[1] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[1].AssetID = new UUID("3e8ee2d6-4f21-4a55-832d-77daa505edff"); - update.WearableData[1].ItemID = UUID.Random(); - update.WearableData[1].WearableType = (byte)WearableType.Pants; - - update.WearableData[2] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[2].AssetID = new UUID("530a2614-052e-49a2-af0e-534bb3c05af0"); - update.WearableData[2].ItemID = UUID.Random(); - update.WearableData[2].WearableType = (byte)WearableType.Shape; - - update.WearableData[3] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[3].AssetID = new UUID("6a714f37-fe53-4230-b46f-8db384465981"); - update.WearableData[3].ItemID = UUID.Random(); - update.WearableData[3].WearableType = (byte)WearableType.Shirt; - - update.WearableData[4] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[4].AssetID = new UUID("5f787f25-f761-4a35-9764-6418ee4774c4"); - update.WearableData[4].ItemID = UUID.Random(); - update.WearableData[4].WearableType = (byte)WearableType.Skin; - - Server.UDP.SendPacket(agent.AgentID, update, PacketCategory.Asset); + SendWearables(agent); } void ClearWearables(Agent agent) @@ -225,7 +340,6 @@ namespace Simian.Extensions { AgentIsNowWearingPacket wearing = (AgentIsNowWearingPacket)packet; - Logger.DebugLog("Updating agent wearables"); ClearWearables(agent); for (int i = 0; i < wearing.WearableData.Length; i++) @@ -237,6 +351,8 @@ namespace Simian.Extensions if (agent.Inventory.TryGetValue(itemID, out invObj) && invObj is InventoryItem) assetID = ((InventoryItem)invObj).AssetID; + #region Update Wearables + switch ((WearableType)wearing.WearableData[i].WearableType) { case WearableType.Shape: @@ -292,7 +408,11 @@ namespace Simian.Extensions agent.SkirtItem = itemID; break; } + + #endregion Update Wearables } + + Logger.DebugLog("Updated agent wearables, new count: " + CountWearables(agent)); } void AgentSetAppearanceHandler(Packet packet, Agent agent) @@ -311,7 +431,14 @@ namespace Simian.Extensions for (int i = 0; i < set.VisualParam.Length; i++) agent.VisualParams[i] = set.VisualParam[i].ParamValue; - //TODO: What is WearableData used for? + //TODO: Store this for cached bake responses + for (int i = 0; i < set.WearableData.Length; i++) + { + AppearanceManager.TextureIndex index = (AppearanceManager.TextureIndex)set.WearableData[i].TextureIndex; + UUID cacheID = set.WearableData[i].CacheID; + + Logger.DebugLog(String.Format("WearableData: {0} is now {1}", index, cacheID)); + } ObjectUpdatePacket update = SimulationObject.BuildFullUpdate(agent.Avatar, Server.RegionHandle, agent.State, agent.Flags | PrimFlags.ObjectYouOwner); @@ -319,7 +446,38 @@ namespace Simian.Extensions // Send out this appearance to all other connected avatars AvatarAppearancePacket appearance = BuildAppearancePacket(agent); - Server.UDP.BroadcastPacket(appearance, PacketCategory.State); + lock (Server.Agents) + { + foreach (Agent recipient in Server.Agents.Values) + { + if (recipient != agent) + Server.UDP.SendPacket(recipient.AgentID, appearance, PacketCategory.State); + } + } + } + + void AgentCachedTextureHandler(Packet packet, Agent agent) + { + AgentCachedTexturePacket cached = (AgentCachedTexturePacket)packet; + + AgentCachedTextureResponsePacket response = new AgentCachedTextureResponsePacket(); + response.AgentData.AgentID = agent.AgentID; + response.AgentData.SerialNum = cached.AgentData.SerialNum; + + response.WearableData = new AgentCachedTextureResponsePacket.WearableDataBlock[cached.WearableData.Length]; + + // TODO: Respond back with actual cache entries if we have them + for (int i = 0; i < cached.WearableData.Length; i++) + { + response.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); + response.WearableData[i].TextureIndex = cached.WearableData[i].TextureIndex; + response.WearableData[i].TextureID = UUID.Zero; + response.WearableData[i].HostName = new byte[0]; + } + + response.Header.Zerocoded = true; + + Server.UDP.SendPacket(agent.AgentID, response, PacketCategory.Transaction); } void SoundTriggerHandler(Packet packet, Agent agent) @@ -331,41 +489,31 @@ namespace Simian.Extensions void UUIDNameRequestHandler(Packet packet, Agent agent) { UUIDNameRequestPacket request = (UUIDNameRequestPacket)packet; - Dictionary replies = new Dictionary(request.UUIDNameBlock.Length); - // FIXME: This is messy/slow until we get a proper ISceneProvider + UUIDNameReplyPacket reply = new UUIDNameReplyPacket(); + reply.UUIDNameBlock = new UUIDNameReplyPacket.UUIDNameBlockBlock[request.UUIDNameBlock.Length]; + for (int i = 0; i < request.UUIDNameBlock.Length; i++) { - lock (Server.Agents) + UUID id = request.UUIDNameBlock[i].ID; + + reply.UUIDNameBlock[i] = new UUIDNameReplyPacket.UUIDNameBlockBlock(); + reply.UUIDNameBlock[i].ID = id; + + Agent foundAgent; + if (Server.Agents.TryGetValue(id, out foundAgent)) { - foreach (Agent curAgent in Server.Agents.Values) - { - if (curAgent.AgentID == request.UUIDNameBlock[i].ID) - { - replies[curAgent.AgentID] = curAgent; - break; - } - } + reply.UUIDNameBlock[i].FirstName = Utils.StringToBytes(foundAgent.FirstName); + reply.UUIDNameBlock[i].LastName = Utils.StringToBytes(foundAgent.LastName); + } + else + { + reply.UUIDNameBlock[i].FirstName = new byte[0]; + reply.UUIDNameBlock[i].LastName = new byte[0]; } } - if (replies.Count > 0) - { - UUIDNameReplyPacket reply = new UUIDNameReplyPacket(); - reply.UUIDNameBlock = new UUIDNameReplyPacket.UUIDNameBlockBlock[replies.Count]; - - int i = 0; - foreach (KeyValuePair kvp in replies) - { - reply.UUIDNameBlock[i] = new UUIDNameReplyPacket.UUIDNameBlockBlock(); - reply.UUIDNameBlock[i].ID = kvp.Key; - reply.UUIDNameBlock[i].FirstName = Utils.StringToBytes(kvp.Value.FirstName); - reply.UUIDNameBlock[i].LastName = Utils.StringToBytes(kvp.Value.LastName); - i++; - } - - Server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Transaction); - } + Server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Transaction); } public static AvatarAppearancePacket BuildAppearancePacket(Agent agent) diff --git a/Programs/Simian/Extensions/UDPServer.cs b/Programs/Simian/Extensions/UDPServer.cs index 05879897..34269e01 100644 --- a/Programs/Simian/Extensions/UDPServer.cs +++ b/Programs/Simian/Extensions/UDPServer.cs @@ -14,36 +14,17 @@ namespace Simian public Packet Packet; } - public struct OutgoingPacket + public class OutgoingPacket { - public UDPClient Client; public Packet Packet; /// Number of times this packet has been resent public int ResendCount; /// Environment.TickCount when this packet was last sent over the wire public int TickCount; - public OutgoingPacket(UDPClient client, Packet packet) + public OutgoingPacket(Packet packet) { - Client = client; Packet = packet; - ResendCount = 0; - TickCount = 0; - } - - public void IncrementResendCount() - { - ++ResendCount; - } - - public void SetTickCount() - { - TickCount = Environment.TickCount; - } - - public void ZeroTickCount() - { - TickCount = 0; } } @@ -250,16 +231,9 @@ namespace Simian if (packet.Header.Reliable) { - // Wrap this packet in a struct to track timeouts and resends - OutgoingPacket outgoing = new OutgoingPacket(null, packet); - // Keep track of when this packet was sent out (right now) - outgoing.TickCount = Environment.TickCount; + OutgoingPacket outgoing; - // Add this packet to the list of ACK responses we are waiting on from this client - lock (client.NeedAcks) - client.NeedAcks[sequence] = outgoing; - - if (packet.Header.Resent) + if (packet.Header.Resent && client.NeedAcks.TryGetValue(packet.Header.Sequence, out outgoing)) { // This packet has already been sent out once, strip any appended ACKs // off it and reinsert them into the outgoing ACK queue under the @@ -282,12 +256,16 @@ namespace Simian packet.Header.AppendedAcks = false; packet.Header.AckList = new uint[0]; } - - // Update the sent time for this packet - SetResentTime(client, packet.Header.Sequence); } else { + // Wrap this packet in a struct to track timeouts and resends + outgoing = new OutgoingPacket(packet); + + // Add this packet to the list of ACK responses we are waiting on from this client + lock (client.NeedAcks) + client.NeedAcks[sequence] = outgoing; + // This packet is not a resend, check if the conditions are favorable // to ACK appending if (packet.Type != PacketType.PacketAck) @@ -310,6 +288,9 @@ namespace Simian } } } + + // Update the sent time for this packet + outgoing.TickCount = Environment.TickCount; } else if (packet.Header.AckList.Length > 0) { @@ -401,13 +382,6 @@ namespace Simian SendPacket(client, acks, PacketCategory.Overhead, true); } - void SetResentTime(UDPClient client, uint sequence) - { - OutgoingPacket outgoing; - if (client.NeedAcks.TryGetValue(sequence, out outgoing)) - outgoing.SetTickCount(); - } - public void ResendUnacked(UDPClient client) { lock (client.NeedAcks) @@ -425,34 +399,29 @@ namespace Simian Logger.DebugLog(String.Format("Resending packet #{0} ({1}), {2}ms have passed", outgoing.Packet.Header.Sequence, outgoing.Packet.GetType(), now - outgoing.TickCount)); - outgoing.ZeroTickCount(); + outgoing.TickCount = 0; outgoing.Packet.Header.Resent = true; + ++outgoing.ResendCount; //++Stats.ResentPackets; - outgoing.IncrementResendCount(); SendPacket(client, outgoing.Packet, PacketCategory.Overhead, false); } else { Logger.Log(String.Format("Dropping packet #{0} ({1}) after {2} failed attempts", - outgoing.Packet.Header.Sequence, outgoing.Packet.GetType(), outgoing.ResendCount), Helpers.LogLevel.Warning); + outgoing.Packet.Header.Sequence, outgoing.Packet.GetType(), outgoing.ResendCount), + Helpers.LogLevel.Warning); dropAck.Add(outgoing.Packet.Header.Sequence); //Disconnect an agent if no packets are received for some time - //TODO: Send logout packet? Also, 60000 should be a setting somewhere. + //TODO: 60000 should be a setting somewhere. if (Environment.TickCount - client.Agent.TickLastPacketReceived > 60000) { - Logger.Log(String.Format("Ack timeout for {0}, disconnecting", client.Agent.Avatar.Name), Helpers.LogLevel.Warning); - server.UDP.RemoveClient(client.Agent); - - //HACK: Notify everyone when someone is disconnected - OfflineNotificationPacket offline = new OfflineNotificationPacket(); - offline.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[1]; - offline.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock(); - offline.AgentBlock[0].AgentID = client.Agent.AgentID; - server.UDP.BroadcastPacket(offline, PacketCategory.State); + Logger.Log(String.Format("Ack timeout for {0}, disconnecting", client.Agent.Avatar.Name), + Helpers.LogLevel.Warning); + server.DisconnectClient(client.Agent); return; } }