From a23f41fdca42a749ba14e1e95f5ea55b2d31e7b3 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sun, 28 Sep 2008 21:28:10 +0000 Subject: [PATCH] Simian: * First pass at persistence support * Fixed a chat crashing bug * Remove avatars from the scene on logout * Sanity check before adding objects to the scene in ObjectAdd * Sanity check in CompleteAgentMovementHandler if the avatar is already in the scene * Added ContainsKey() to DoubleDictionary git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@2245 52acb1d6-8a22-11de-b505-999d5b087335 --- Programs/Simian/DoubleDictionary.cs | 10 +++ .../Simian/Extensions/ConnectionManagement.cs | 27 +++--- Programs/Simian/Extensions/Messaging.cs | 3 +- Programs/Simian/Extensions/ObjectManager.cs | 23 +++++ Programs/Simian/Extensions/SceneManager.cs | 56 +++++++----- Programs/Simian/Extensions/XMLPersistence.cs | 87 +++++++++++++++++++ .../Simian/Interfaces/IPersistenceProvider.cs | 9 ++ Programs/Simian/Simian.cs | 39 ++++++++- 8 files changed, 218 insertions(+), 36 deletions(-) create mode 100644 Programs/Simian/Extensions/XMLPersistence.cs create mode 100644 Programs/Simian/Interfaces/IPersistenceProvider.cs diff --git a/Programs/Simian/DoubleDictionary.cs b/Programs/Simian/DoubleDictionary.cs index 0beb6c3d..dc696e6a 100644 --- a/Programs/Simian/DoubleDictionary.cs +++ b/Programs/Simian/DoubleDictionary.cs @@ -53,6 +53,16 @@ namespace Simian get { return Dictionary1.Count; } } + public bool ContainsKey(TKey1 key) + { + return Dictionary1.ContainsKey(key); + } + + public bool ContainsKey(TKey2 key) + { + return Dictionary2.ContainsKey(key); + } + public bool TryGetValue(TKey1 key, out TValue value) { return Dictionary1.TryGetValue(key, out value); diff --git a/Programs/Simian/Extensions/ConnectionManagement.cs b/Programs/Simian/Extensions/ConnectionManagement.cs index 4e15a202..17b2b002 100644 --- a/Programs/Simian/Extensions/ConnectionManagement.cs +++ b/Programs/Simian/Extensions/ConnectionManagement.cs @@ -18,7 +18,6 @@ namespace Simian.Extensions server.UDP.RegisterPacketCallback(PacketType.UseCircuitCode, new PacketCallback(UseCircuitCodeHandler)); server.UDP.RegisterPacketCallback(PacketType.StartPingCheck, new PacketCallback(StartPingCheckHandler)); server.UDP.RegisterPacketCallback(PacketType.LogoutRequest, new PacketCallback(LogoutRequestHandler)); - } public void Stop() @@ -74,15 +73,6 @@ namespace Simian.Extensions { LogoutRequestPacket request = (LogoutRequestPacket)packet; - LogoutReplyPacket reply = new LogoutReplyPacket(); - reply.AgentData.AgentID = agent.AgentID; - reply.AgentData.SessionID = agent.SessionID; - reply.InventoryData = new LogoutReplyPacket.InventoryDataBlock[1]; - reply.InventoryData[0] = new LogoutReplyPacket.InventoryDataBlock(); - reply.InventoryData[0].ItemID = UUID.Zero; - - server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Transaction); - lock (server.Agents) { if (server.Agents.ContainsKey(agent.AgentID)) @@ -96,8 +86,25 @@ namespace Simian.Extensions } } + // Remove the avatar from the scene + SimulationObject obj; + if (server.Scene.TryGetObject(agent.AgentID, out obj)) + server.Scene.ObjectRemove(this, obj); + else + Logger.Log("Logout request from an agent that is not in the scene", Helpers.LogLevel.Warning); + + // Remove the UDP client server.UDP.RemoveClient(agent); + LogoutReplyPacket reply = new LogoutReplyPacket(); + reply.AgentData.AgentID = agent.AgentID; + reply.AgentData.SessionID = agent.SessionID; + reply.InventoryData = new LogoutReplyPacket.InventoryDataBlock[1]; + reply.InventoryData[0] = new LogoutReplyPacket.InventoryDataBlock(); + reply.InventoryData[0].ItemID = UUID.Zero; + + server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Transaction); + //HACK: Notify everyone when someone logs off OfflineNotificationPacket offline = new OfflineNotificationPacket(); offline.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[1]; diff --git a/Programs/Simian/Extensions/Messaging.cs b/Programs/Simian/Extensions/Messaging.cs index 46fd3206..d45ee7e4 100644 --- a/Programs/Simian/Extensions/Messaging.cs +++ b/Programs/Simian/Extensions/Messaging.cs @@ -30,7 +30,8 @@ namespace Simian.Extensions ChatFromViewerPacket viewerChat = (ChatFromViewerPacket)packet; string message = Utils.BytesToString(viewerChat.ChatData.Message); - if (viewerChat.ChatData.Channel != 0 || message.Substring(0, 1) == "/") return; //not public chat + if (viewerChat.ChatData.Channel != 0 || (message.Length > 0 && message.Substring(0, 1) == "/")) + return; //not public chat //TODO: add distance constraints to AudibleLevel and Message diff --git a/Programs/Simian/Extensions/ObjectManager.cs b/Programs/Simian/Extensions/ObjectManager.cs index 52b9b10f..60916dcc 100644 --- a/Programs/Simian/Extensions/ObjectManager.cs +++ b/Programs/Simian/Extensions/ObjectManager.cs @@ -27,6 +27,8 @@ namespace Simian.Extensions Server.UDP.RegisterPacketCallback(PacketType.ObjectDelink, new PacketCallback(ObjectDelinkHandler)); Server.UDP.RegisterPacketCallback(PacketType.ObjectShape, new PacketCallback(ObjectShapeHandler)); Server.UDP.RegisterPacketCallback(PacketType.ObjectFlagUpdate, new PacketCallback(ObjectFlagUpdateHandler)); + Server.UDP.RegisterPacketCallback(PacketType.ObjectExtraParams, new PacketCallback(ObjectExtraParamsHandler)); + Server.UDP.RegisterPacketCallback(PacketType.ObjectImage, new PacketCallback(ObjectImageHandler)); Server.UDP.RegisterPacketCallback(PacketType.DeRezObject, new PacketCallback(DeRezObjectHandler)); Server.UDP.RegisterPacketCallback(PacketType.MultipleObjectUpdate, new PacketCallback(MultipleObjectUpdateHandler)); Server.UDP.RegisterPacketCallback(PacketType.RequestObjectPropertiesFamily, new PacketCallback(RequestObjectPropertiesFamilyHandler)); @@ -481,6 +483,27 @@ namespace Simian.Extensions } } + void ObjectExtraParamsHandler(Packet packet, Agent agent) + { + ObjectExtraParamsPacket extra = (ObjectExtraParamsPacket)packet; + + for (int i = 0; i < extra.ObjectData.Length; i++) + { + ObjectExtraParamsPacket.ObjectDataBlock block = extra.ObjectData[i]; + + SimulationObject obj; + if (Server.Scene.TryGetObject(block.ObjectLocalID, out obj)) + { + ExtraParamType type = (ExtraParamType)block.ParamType; + } + + } + } + + void ObjectImageHandler(Packet packet, Agent agent) + { + } + void DeRezObjectHandler(Packet packet, Agent agent) { DeRezObjectPacket derez = (DeRezObjectPacket)packet; diff --git a/Programs/Simian/Extensions/SceneManager.cs b/Programs/Simian/Extensions/SceneManager.cs index 30f88afe..ea23c80e 100644 --- a/Programs/Simian/Extensions/SceneManager.cs +++ b/Programs/Simian/Extensions/SceneManager.cs @@ -55,6 +55,14 @@ namespace Simian.Extensions public bool ObjectAdd(object sender, Agent creator, SimulationObject obj, PrimFlags creatorFlags) { + // Check if the object already exists in the scene + if (sceneObjects.ContainsKey(obj.Prim.ID)) + { + Logger.Log(String.Format("Attempting to add duplicate object {0} to the scene", + obj.Prim.ID), Helpers.LogLevel.Warning); + return false; + } + // Assign a unique LocalID to this object obj.Prim.LocalID = (uint)Interlocked.Increment(ref currentLocalID); @@ -205,30 +213,36 @@ namespace Simian.Extensions // Give testers a provisionary balance of 1000L agent.Balance = 1000; - // Send a response back to the client - AgentMovementCompletePacket complete = new AgentMovementCompletePacket(); - complete.AgentData.AgentID = agent.AgentID; - complete.AgentData.SessionID = agent.SessionID; - complete.Data.LookAt = Vector3.UnitX; - complete.Data.Position = avatar.Position; - complete.Data.RegionHandle = server.RegionHandle; - complete.Data.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now); - complete.SimData.ChannelVersion = Utils.StringToBytes("Simian"); - - server.UDP.SendPacket(agent.AgentID, complete, PacketCategory.Transaction); - // Add this avatar as an object in the scene - ObjectAdd(this, agent, new SimulationObject(agent.Avatar, server), PrimFlags.None); + if (ObjectAdd(this, agent, new SimulationObject(agent.Avatar, server), PrimFlags.None)) + { + // Send a response back to the client + AgentMovementCompletePacket complete = new AgentMovementCompletePacket(); + complete.AgentData.AgentID = agent.AgentID; + complete.AgentData.SessionID = agent.SessionID; + complete.Data.LookAt = Vector3.UnitX; + complete.Data.Position = avatar.Position; + complete.Data.RegionHandle = server.RegionHandle; + complete.Data.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now); + complete.SimData.ChannelVersion = Utils.StringToBytes("Simian"); - // Send updates and appearances for every avatar to this new avatar - SynchronizeStateTo(agent); + server.UDP.SendPacket(agent.AgentID, complete, PacketCategory.Transaction); - //HACK: Notify everyone when someone logs on to the simulator - OnlineNotificationPacket online = new OnlineNotificationPacket(); - online.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[1]; - online.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock(); - online.AgentBlock[0].AgentID = agent.AgentID; - server.UDP.BroadcastPacket(online, PacketCategory.State); + // Send updates and appearances for every avatar to this new avatar + SynchronizeStateTo(agent); + + //HACK: Notify everyone when someone logs on to the simulator + OnlineNotificationPacket online = new OnlineNotificationPacket(); + online.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[1]; + online.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock(); + online.AgentBlock[0].AgentID = agent.AgentID; + server.UDP.BroadcastPacket(online, PacketCategory.State); + } + else + { + Logger.Log("Received a CompleteAgentMovement from an avatar already in the scene, " + + agent.FullName, Helpers.LogLevel.Warning); + } } // HACK: The reduction provider will deprecate this at some point diff --git a/Programs/Simian/Extensions/XMLPersistence.cs b/Programs/Simian/Extensions/XMLPersistence.cs new file mode 100644 index 00000000..bd878ced --- /dev/null +++ b/Programs/Simian/Extensions/XMLPersistence.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace Simian.Extensions +{ + public class XMLPersistence : ISimianExtension, IPersistenceProvider + { + Simian server; + + public XMLPersistence(Simian server) + { + this.server = server; + } + + public void Start() + { + LLSD llsd; + + try + { + XmlTextReader reader = new XmlTextReader(File.OpenRead(server.DataDir + "simiandata.xml")); + llsd = LLSDParser.DeserializeXml(reader); + reader.Close(); + } + catch (FileNotFoundException) + { + return; + } + catch (Exception ex) + { + Logger.Log("Failed to load saved data: " + ex.Message, Helpers.LogLevel.Error); + return; + } + + if (llsd is LLSDMap) + { + LLSDMap dictionary = (LLSDMap)llsd; + + for (int i = 0; i < server.PersistentExtensions.Count; i++) + { + IPersistable persistable = server.PersistentExtensions[i]; + + LLSD savedData; + if (dictionary.TryGetValue(persistable.ToString(), out savedData)) + { + Logger.DebugLog("Loading saved data for " + persistable.ToString()); + persistable.Deserialize(savedData); + } + else + { + Logger.DebugLog("No saved data found for " + persistable.ToString()); + } + } + } + } + + public void Stop() + { + LLSDMap dictionary = new LLSDMap(server.PersistentExtensions.Count); + + for (int i = 0; i < server.PersistentExtensions.Count; i++) + { + IPersistable persistable = server.PersistentExtensions[i]; + + Logger.DebugLog("Storing persistant data for " + persistable.ToString()); + dictionary.Add(persistable.ToString(), persistable.Serialize()); + } + + try + { + XmlTextWriter writer = new XmlTextWriter(server.DataDir + "simiandata.xml", System.Text.Encoding.UTF8); + writer.WriteStartElement("llsd"); + LLSDParser.SerializeXmlElement(writer, dictionary); + writer.WriteEndElement(); + writer.Close(); + } + catch (Exception ex) + { + Logger.Log("Failed to save persistance data: " + ex.Message, Helpers.LogLevel.Error); + } + } + } +} diff --git a/Programs/Simian/Interfaces/IPersistenceProvider.cs b/Programs/Simian/Interfaces/IPersistenceProvider.cs new file mode 100644 index 00000000..53e47ba7 --- /dev/null +++ b/Programs/Simian/Interfaces/IPersistenceProvider.cs @@ -0,0 +1,9 @@ +using System; +using OpenMetaverse; + +namespace Simian +{ + public interface IPersistenceProvider + { + } +} diff --git a/Programs/Simian/Simian.cs b/Programs/Simian/Simian.cs index afa2297f..9bf79540 100644 --- a/Programs/Simian/Simian.cs +++ b/Programs/Simian/Simian.cs @@ -35,6 +35,9 @@ namespace Simian public IParcelProvider Parcels; public IMeshingProvider Mesher; + // Persistent extensions + public List PersistentExtensions = new List(); + /// All of the agents currently connected to this UDP server public Dictionary Agents = new Dictionary(); @@ -62,9 +65,22 @@ namespace Simian foreach (ISimianExtension extension in ExtensionLoader.Extensions) { - // Start the interfaces - Logger.DebugLog("Loading extension " + extension.GetType().Name); - extension.Start(); + // Start persistance providers after all other extensions + if (!(extension is IPersistenceProvider)) + { + Logger.DebugLog("Loading extension " + extension.GetType().Name); + extension.Start(); + } + } + + foreach (ISimianExtension extension in ExtensionLoader.Extensions) + { + // Start the persistance provider(s) + if (extension is IPersistenceProvider) + { + Logger.DebugLog("Loading persistance provider " + extension.GetType().Name); + extension.Start(); + } } if (!CheckInterfaces()) @@ -82,7 +98,18 @@ namespace Simian public void Stop() { foreach (ISimianExtension extension in ExtensionLoader.Extensions) - extension.Stop(); + { + // Stop persistance providers first + if (extension is IPersistenceProvider) + extension.Stop(); + } + + foreach (ISimianExtension extension in ExtensionLoader.Extensions) + { + // Stop all other extensions + if (!(extension is IPersistenceProvider)) + extension.Stop(); + } HttpServer.Stop(); } @@ -107,6 +134,10 @@ namespace Simian Parcels = (IParcelProvider)extension; else if (extension is IMeshingProvider) Mesher = (IMeshingProvider)extension; + + // Track all of the extensions with persistence + if (extension is IPersistable) + PersistentExtensions.Add((IPersistable)extension); } bool CheckInterfaces()