From f6a4f8844f01fb756bbc26d65c252fc07ab8c2bf Mon Sep 17 00:00:00 2001 From: Charles Krinke Date: Sat, 17 May 2008 15:47:08 +0000 Subject: [PATCH] Thank you very much KMeisthax for DataSnapshot 1.1 to enhance search capability on OpenSim sims using external search engines such as Metaversink.com and others. --- .../Region/DataSnapshot/DataRequestHandler.cs | 37 ++ .../DataSnapshot/DataSnapshotManager.cs | 451 +++++++----------- OpenSim/Region/DataSnapshot/EstateSnapshot.cs | 31 +- .../DataSnapshot/Interfaces/IDataSnapshot.cs | 13 + .../Interfaces/IDataSnapshotProvider.cs | 12 +- OpenSim/Region/DataSnapshot/LLSDDiscovery.cs | 46 ++ OpenSim/Region/DataSnapshot/LandSnapshot.cs | 71 ++- OpenSim/Region/DataSnapshot/ObjectSnapshot.cs | 136 ++++-- OpenSim/Region/DataSnapshot/SnapshotStore.cs | 316 ++++++++++++ 9 files changed, 786 insertions(+), 327 deletions(-) create mode 100644 OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshot.cs create mode 100644 OpenSim/Region/DataSnapshot/LLSDDiscovery.cs create mode 100644 OpenSim/Region/DataSnapshot/SnapshotStore.cs diff --git a/OpenSim/Region/DataSnapshot/DataRequestHandler.cs b/OpenSim/Region/DataSnapshot/DataRequestHandler.cs index e08934bf59..abf2c1ad33 100644 --- a/OpenSim/Region/DataSnapshot/DataRequestHandler.cs +++ b/OpenSim/Region/DataSnapshot/DataRequestHandler.cs @@ -26,11 +26,16 @@ * */ +using System; using System.Collections; using System.Reflection; using System.Xml; using log4net; using OpenSim.Region.Environment.Scenes; +using OpenSim.Framework.Communications.Capabilities; +using Caps = OpenSim.Framework.Communications.Capabilities.Caps; +using libsecondlife; +using OpenSim.Framework.Servers; namespace OpenSim.Region.DataSnapshot { @@ -40,18 +45,50 @@ namespace OpenSim.Region.DataSnapshot private DataSnapshotManager m_externalData = null; private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private readonly string m_discoveryPath = "DS0001/"; + public DataRequestHandler(Scene scene, DataSnapshotManager externalData) { m_scene = scene; m_externalData = externalData; + //Register HTTP handler if (m_scene.AddHTTPHandler("collector", OnGetSnapshot)) { m_log.Info("[DATASNAPSHOT]: Set up snapshot service"); } + + //Register CAPS handler event + m_scene.EventManager.OnRegisterCaps += OnRegisterCaps; + //harbl } + public void OnRegisterCaps(LLUUID agentID, Caps caps) + { + m_log.Info("[DATASNAPSHOT]: Registering service discovery capability for " + agentID); + string capsBase = "/CAPS/" + caps.CapsObjectPath; + caps.RegisterHandler("PublicSnapshotDataInfo", + new RestStreamHandler("POST", capsBase + m_discoveryPath, OnDiscoveryAttempt)); + } + + public string OnDiscoveryAttempt(string request, string path, string param) + { + //Very static for now, flexible enough to add new formats + LLSDDiscoveryResponse llsd_response = new LLSDDiscoveryResponse(); + llsd_response.snapshot_resources = new LLSDArray(); + + LLSDDiscoveryDataURL llsd_dataurl = new LLSDDiscoveryDataURL(); + llsd_dataurl.snapshot_format = "os-datasnapshot-v1"; + llsd_dataurl.snapshot_url = "http://" + m_externalData.m_hostname + ":" + m_externalData.m_listener_port + "/?method=collector"; + + llsd_response.snapshot_resources.Array.Add(llsd_dataurl); + + string response = LLSDHelpers.SerialiseLLSDReply(llsd_response); + + return response; + } + public Hashtable OnGetSnapshot(Hashtable keysvals) { m_log.Info("[DATASNAPSHOT] Received collection request"); diff --git a/OpenSim/Region/DataSnapshot/DataSnapshotManager.cs b/OpenSim/Region/DataSnapshot/DataSnapshotManager.cs index af3e547a4e..6fdedb4011 100644 --- a/OpenSim/Region/DataSnapshot/DataSnapshotManager.cs +++ b/OpenSim/Region/DataSnapshot/DataSnapshotManager.cs @@ -37,49 +37,62 @@ using System.Xml; using libsecondlife; using log4net; using Nini.Config; +using OpenSim.Framework; using OpenSim.Framework.Communications; using OpenSim.Region.DataSnapshot.Interfaces; using OpenSim.Region.Environment.Interfaces; using OpenSim.Region.Environment.Scenes; +using libsecondlife.Packets; namespace OpenSim.Region.DataSnapshot { - public class DataSnapshotManager : IRegionModule + public class DataSnapshotManager : IRegionModule, IDataSnapshot { #region Class members - private List m_scenes = new List(); - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + //Information from config private bool m_enabled = false; private bool m_configLoaded = false; - internal object m_syncInit = new object(); - private DataRequestHandler m_requests = null; - private Dictionary> m_dataproviders = new Dictionary>(); + private List m_disabledModules = new List(); private Dictionary m_gridinfo = new Dictionary(); - //private int m_oldestSnapshot = 0; - private int m_maxSnapshots = 500; - private int m_lastSnapshot = 0; private string m_snapsDir = "DataSnapshot"; + + //Lists of stuff we need + private List m_scenes = new List(); + private List m_dataproviders = new List(); + + //Various internal objects + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + internal object m_syncInit = new object(); + + //DataServices and networking private string m_dataServices = "noservices"; - private string m_listener_port = "9000"; //TODO: Set default port over 9000 - private string m_hostname = "127.0.0.1"; + public string m_listener_port = "9000"; //TODO: Set default port over 9000 + public string m_hostname = "127.0.0.1"; + + //Update timers private Timer m_periodic = null; - private int m_period = 60; // in seconds - private List m_disabledModules = new List(); + private int m_period = 20; // in seconds + private int m_maxStales = 500; + private int m_stales = 0; + private Timer m_passedCheck = null; + private bool m_periodPassed = false; + + //Program objects + private SnapshotStore m_snapStore = null; + private DataRequestHandler m_requests = null; + #endregion #region IRegionModule + public void Close() { - + m_log.Info("[DATASNAPSHOT]: Close called"); } public void Initialise(Scene scene, IConfigSource config) { - if (!m_scenes.Contains(scene)) - m_scenes.Add(scene); - - if (!m_configLoaded) - { + if (!m_configLoaded) { m_configLoaded = true; m_log.Info("[DATASNAPSHOT]: Loading configuration"); //Read from the config for options @@ -99,49 +112,59 @@ namespace OpenSim.Region.DataSnapshot //Non gridmode stuff } - m_gridinfo.Add("Name", config.Configs["DataSnapshot"].GetString("gridname", "harbl")); - m_maxSnapshots = config.Configs["DataSnapshot"].GetInt("max_snapshots", m_maxSnapshots); - m_period = config.Configs["DataSnapshot"].GetInt("default_snapshot_period", m_period); - m_snapsDir = config.Configs["DataSnapshot"].GetString("snapshot_cache_directory", m_snapsDir); - m_dataServices = config.Configs["DataSnapshot"].GetString("data_services", m_dataServices); - m_listener_port = config.Configs["Network"].GetString("http_listener_port", m_listener_port); - //BUG: Naming a search data module "DESUDESUDESU" will cause it to not get loaded by default. - //RESOLUTION: Wontfix, there are no Suiseiseki-loving developers - String[] annoying_string_array = config.Configs["DataSnapshot"].GetString("disable_modules", "DESUDESUDESU").Split(".".ToCharArray()); - foreach (String bloody_wanker in annoying_string_array) - { - m_disabledModules.Add(bloody_wanker); - } - } - catch (Exception) - { + m_gridinfo.Add("Name", config.Configs["DataSnapshot"].GetString("gridname", "harbl")); + m_period = config.Configs["DataSnapshot"].GetInt("default_snapshot_period", m_period); + m_maxStales = config.Configs["DataSnapshot"].GetInt("max_changes_before_update", m_maxStales); + m_snapsDir = config.Configs["DataSnapshot"].GetString("snapshot_cache_directory", m_snapsDir); + m_dataServices = config.Configs["DataSnapshot"].GetString("data_services", m_dataServices); + m_listener_port = config.Configs["Network"].GetString("http_listener_port", m_listener_port); + + String[] annoying_string_array = config.Configs["DataSnapshot"].GetString("disable_modules", "").Split(".".ToCharArray()); + foreach (String bloody_wanker in annoying_string_array) { + m_disabledModules.Add(bloody_wanker); + } + } catch (Exception) { m_log.Info("[DATASNAPSHOT]: Could not load configuration. DataSnapshot will be disabled."); m_enabled = false; return; } } - } - if (Directory.Exists(m_snapsDir)) - { - m_log.Info("[DATASNAPSHOT] DataSnapshot directory already exists."); - } - else - { - // Try to create the directory. - m_log.Info("[DATASNAPSHOT] Creating " + m_snapsDir + " directory."); - try - { - Directory.CreateDirectory(m_snapsDir); - } - catch (Exception) - { - m_log.Error("[DATASNAPSHOT] Failed to create " + m_snapsDir + " directory."); - } + + m_snapStore = new SnapshotStore(m_snapsDir, m_gridinfo, m_listener_port, m_hostname); } if (m_enabled) { m_log.Info("[DATASNAPSHOT]: Scene added to module."); + + m_snapStore.AddScene(scene); + m_scenes.Add(scene); + + Assembly currentasm = Assembly.GetExecutingAssembly(); + + foreach (Type pluginType in currentasm.GetTypes()) + { + if (pluginType.IsPublic) + { + if (!pluginType.IsAbstract) + { + if (pluginType.GetInterface("IDataSnapshotProvider") != null) + { + IDataSnapshotProvider module = (IDataSnapshotProvider)Activator.CreateInstance(pluginType); + module.Initialize(scene, this); + module.OnStale += MarkDataStale; + + m_dataproviders.Add(module); + m_snapStore.AddProvider(module); + + m_log.Info("[DATASNAPSHOT]: Added new data provider type: " + pluginType.Name); + } + } + } + } + + //scene.OnRestart += OnSimRestart; + scene.EventManager.OnShutdown += delegate() { OnSimRestart(scene.RegionInfo); }; } else { @@ -163,68 +186,35 @@ namespace OpenSim.Region.DataSnapshot { if (m_enabled) { - //Right now, only load ISearchData objects in the current assembly. - //Eventually allow it to load ISearchData objects from all assemblies. - Assembly currentasm = Assembly.GetExecutingAssembly(); - - //Stolen from ModuleLoader.cs - foreach (Type pluginType in currentasm.GetTypes()) - { - if (pluginType.IsPublic) - { - if (!pluginType.IsAbstract) - { - if (pluginType.GetInterface("IDataSnapshotProvider") != null) - { - foreach (Scene scene in m_scenes) - { - IDataSnapshotProvider module = (IDataSnapshotProvider)Activator.CreateInstance(pluginType); - module.Initialize(scene, this); - //module.PrepareData(); - List providerlist = null; - m_dataproviders.TryGetValue(scene, out providerlist); - if (providerlist == null) - { - providerlist = new List(); - m_dataproviders.Add(scene, providerlist); - } - providerlist.Add(module); - - } - m_log.Info("[DATASNAPSHOT]: Added new data provider type: " + pluginType.Name); - } - } - } - } - //Hand it the first scene, assuming that all scenes have the same BaseHTTPServer m_requests = new DataRequestHandler(m_scenes[0], this); - //Create timer + //Create update timer m_periodic = new Timer(); m_periodic.Interval = m_period * 1000; m_periodic.Elapsed += SnapshotTimerCallback; - m_periodic.Enabled = true; + + //Create update eligibility timer + m_passedCheck = new Timer(); + m_passedCheck.Interval = m_period * 1000; + m_passedCheck.Elapsed += UpdateEligibilityCallback; + m_passedCheck.Start(); m_hostname = m_scenes[0].RegionInfo.ExternalHostName; - MakeNewSnapshot(); //Make the initial snapshot + //m_snapStore = new SnapshotStore(m_snapsDir, m_dataproviders, m_gridinfo, m_listener_port, m_hostname); + MakeEverythingStale(); if (m_dataServices != "noservices") - NotifyDataServices(m_dataServices); + NotifyDataServices(m_dataServices); } } + #endregion #region Associated helper functions - string DataFileName(Scene scene) - { - return Path.Combine(m_snapsDir, Path.ChangeExtension(scene.RegionInfo.RegionName, "xml")); - //return (m_snapsDir + Path.DirectorySeparatorChar + scene.RegionInfo.RegionName + ".xml"); - } - - Scene SceneForName(string name) + public Scene SceneForName(string name) { foreach (Scene scene in m_scenes) if (scene.RegionInfo.RegionName == name) @@ -233,168 +223,19 @@ namespace OpenSim.Region.DataSnapshot return null; } - #endregion - - #region [Private] XML snapshot generator - - private XmlDocument Snapshot(Scene scene) + public Scene SceneForUUID(LLUUID id) { - XmlDocument basedoc = new XmlDocument(); - XmlNode regionElement = MakeRegionNode(scene, basedoc); + foreach (Scene scene in m_scenes) + if (scene.RegionInfo.RegionID == id) + return scene; - regionElement.AppendChild(GetGridSnapshotData(basedoc)); - XmlNode regionData = basedoc.CreateNode(XmlNodeType.Element, "data", ""); - - foreach (KeyValuePair> dataprovider in m_dataproviders) - { - if (dataprovider.Key == scene) - { - foreach (IDataSnapshotProvider provider in dataprovider.Value) - { - XmlNode data = provider.RequestSnapshotData(basedoc); - regionData.AppendChild(data); - } - } - } - - regionElement.AppendChild(regionData); - - basedoc.AppendChild(regionElement); - - return basedoc; + return null; } - private XmlNode MakeRegionNode(Scene scene, XmlDocument basedoc) - { - XmlNode docElement = basedoc.CreateNode(XmlNodeType.Element, "region", ""); - - XmlAttribute attr = basedoc.CreateAttribute("category"); - attr.Value = GetRegionCategory(scene); - docElement.Attributes.Append(attr); - - attr = basedoc.CreateAttribute("entities"); - attr.Value = scene.Entities.Count.ToString(); - docElement.Attributes.Append(attr); - - //attr = basedoc.CreateAttribute("parcels"); - //attr.Value = scene.LandManager.landList.Count.ToString(); - //docElement.Attributes.Append(attr); - - - XmlNode infoblock = basedoc.CreateNode(XmlNodeType.Element, "info", ""); - - XmlNode infopiece = basedoc.CreateNode(XmlNodeType.Element, "uuid", ""); - infopiece.InnerText = scene.RegionInfo.RegionID.ToString(); - infoblock.AppendChild(infopiece); - - infopiece = basedoc.CreateNode(XmlNodeType.Element, "url", ""); - infopiece.InnerText = "http://" + m_hostname + ":" + m_listener_port; - infoblock.AppendChild(infopiece); - - infopiece = basedoc.CreateNode(XmlNodeType.Element, "name", ""); - infopiece.InnerText = scene.RegionInfo.RegionName; - infoblock.AppendChild(infopiece); - - docElement.AppendChild(infoblock); - - return docElement; - } - - private XmlNode GetGridSnapshotData(XmlDocument factory) - { - XmlNode griddata = factory.CreateNode(XmlNodeType.Element, "grid", ""); - - foreach (KeyValuePair GridData in m_gridinfo) - { - //TODO: make it lowercase tag names for diva - XmlNode childnode = factory.CreateNode(XmlNodeType.Element, GridData.Key, ""); - childnode.InnerText = GridData.Value; - griddata.AppendChild(childnode); - } - - return griddata; - } - - private String GetRegionCategory(Scene scene) - { - - //Boolean choice between: - // "PG" - Mormontown - // "Mature" - Sodom and Gomorrah - // (Depreciated) "Patriotic Nigra Testing Sandbox" - Abandon Hope All Ye Who Enter Here - if ((scene.RegionInfo.EstateSettings.simAccess & Simulator.SimAccess.Mature) == Simulator.SimAccess.Mature) - { - return "Mature"; - } - else if ((scene.RegionInfo.EstateSettings.simAccess & Simulator.SimAccess.PG) == Simulator.SimAccess.PG) - { - return "PG"; - } - else - { - return "Unknown"; - } - } - - /* Code's closed due to AIDS, See EstateSnapshot.cs for CURE - private XmlNode GetEstateSnapshotData(Scene scene, XmlDocument factory) - { - //Estate data section - contains who owns a set of sims and the name of the set. - //In Opensim all the estate names are the same as the Master Avatar (owner of the sim) - XmlNode estatedata = factory.CreateNode(XmlNodeType.Element, "estate", ""); - - LLUUID ownerid = scene.RegionInfo.MasterAvatarAssignedUUID; - String firstname = scene.RegionInfo.MasterAvatarFirstName; - String lastname = scene.RegionInfo.MasterAvatarLastName; - String hostname = scene.RegionInfo.ExternalHostName; - - XmlNode user = factory.CreateNode(XmlNodeType.Element, "owner", ""); - - XmlNode username = factory.CreateNode(XmlNodeType.Element, "name", ""); - username.InnerText = firstname + " " + lastname; - user.AppendChild(username); - - XmlNode useruuid = factory.CreateNode(XmlNodeType.Element, "uuid", ""); - useruuid.InnerText = ownerid.ToString(); - user.AppendChild(useruuid); - - estatedata.AppendChild(user); - - return estatedata; - } */ - #endregion #region [Public] Snapshot storage functions - public void MakeNewSnapshot() - { - foreach (Scene scene in m_scenes) - { - XmlDocument snapshot = Snapshot(scene); - - string path = DataFileName(scene); - - try - { - using (XmlTextWriter snapXWriter = new XmlTextWriter(path, Encoding.Default)) - { - snapXWriter.Formatting = Formatting.Indented; - snapXWriter.WriteStartDocument(); - snapshot.WriteTo(snapXWriter); - snapXWriter.WriteEndDocument(); - - m_lastSnapshot++; - } - } - catch (Exception e) - { - m_log.Warn("[DATASNAPSHOT]: Caught unknown exception while trying to save snapshot: " + path + "\n" + e.ToString()); - } - m_log.Info("[DATASNAPSHOT]: Made external data snapshot " + path); - } - } - /** * Reply to the http request */ @@ -410,30 +251,16 @@ namespace OpenSim.Region.DataSnapshot { foreach (Scene scene in m_scenes) { - string path = DataFileName(scene); - XmlDocument regionSnap = new XmlDocument(); - regionSnap.PreserveWhitespace = true; - - regionSnap.Load(path); - XmlNode nodeOrig = regionSnap["region"]; - XmlNode nodeDest = requestedSnap.ImportNode(nodeOrig, true); - //requestedSnap.AppendChild(nodeDest); - - regiondata.AppendChild(requestedSnap.CreateWhitespace("\r\n")); - regiondata.AppendChild(nodeDest); + regiondata.AppendChild(m_snapStore.GetScene(scene, requestedSnap)); } } else { Scene scene = SceneForName(regionName); - requestedSnap.Load(DataFileName(scene)); + regiondata.AppendChild(m_snapStore.GetScene(scene, requestedSnap)); } - // requestedSnap.InsertBefore(requestedSnap.CreateXmlDeclaration("1.0", null, null), -// requestedSnap.DocumentElement); requestedSnap.AppendChild(regiondata); regiondata.AppendChild(requestedSnap.CreateWhitespace("\r\n")); - - } catch (XmlException e) { @@ -469,16 +296,6 @@ namespace OpenSim.Region.DataSnapshot #endregion - #region Event callbacks - - private void SnapshotTimerCallback(object timer, ElapsedEventArgs args) - { - MakeNewSnapshot(); - //Add extra calls here - } - - #endregion - #region External data services private void NotifyDataServices(string servicesStr) { @@ -524,5 +341,85 @@ namespace OpenSim.Region.DataSnapshot } #endregion + + #region Latency-based update functions + + public void MarkDataStale(IDataSnapshotProvider provider) + { + //Behavior here: Wait m_period seconds, then update if there has not been a request in m_period seconds + //or m_maxStales has been exceeded + m_stales++; + + if ((m_stales >= m_maxStales) && m_periodPassed) + SnapshotTimerCallback(m_periodic, null); + else if (m_periodic.Enabled == false) + m_periodic.Start(); + else + { + m_periodic.Stop(); + m_periodic.Start(); + } + } + + private void SnapshotTimerCallback(object timer, ElapsedEventArgs args) + { + m_log.Debug("[DATASNAPSHOT]: Marking scenes for snapshot updates."); + + //Finally generate those snapshot updates + MakeEverythingStale(); + + //Stop the update delay timer + m_periodic.Stop(); + + //Reset the eligibility flag and timer + m_periodPassed = false; + m_passedCheck.Stop(); + m_passedCheck.Start(); + } + + private void UpdateEligibilityCallback(object timer, ElapsedEventArgs args) + { + //Set eligibility, so we can start making updates + m_periodPassed = true; + } + + public void MakeEverythingStale() + { + m_log.Debug("[DATASNAPSHOT]: Marking all scenes as stale."); + foreach (Scene scene in m_scenes) + { + m_snapStore.ForceSceneStale(scene); + } + } + + #endregion + + public void OnSimRestart(RegionInfo thisRegion) + { + m_log.Info("[DATASNAPSHOT]: Region " + thisRegion.RegionName + " is restarting, removing from indexing"); + Scene restartedScene = SceneForUUID(thisRegion.RegionID); + + m_scenes.Remove(restartedScene); + m_snapStore.RemoveScene(restartedScene); + + //Getting around the fact that we can't remove objects from a collection we are enumerating over + List providersToRemove = new List(); + + foreach (IDataSnapshotProvider provider in m_dataproviders) + { + if (provider.GetParentScene == restartedScene) + { + providersToRemove.Add(provider); + } + } + + foreach (IDataSnapshotProvider provider in providersToRemove) + { + m_dataproviders.Remove(provider); + m_snapStore.RemoveProvider(provider); + } + + m_snapStore.RemoveScene(restartedScene); + } } } diff --git a/OpenSim/Region/DataSnapshot/EstateSnapshot.cs b/OpenSim/Region/DataSnapshot/EstateSnapshot.cs index 80207139c5..40b96d5905 100644 --- a/OpenSim/Region/DataSnapshot/EstateSnapshot.cs +++ b/OpenSim/Region/DataSnapshot/EstateSnapshot.cs @@ -31,12 +31,19 @@ using libsecondlife; using OpenSim.Region.DataSnapshot.Interfaces; using OpenSim.Region.Environment.Scenes; -namespace OpenSim.Region.DataSnapshot +namespace OpenSim.Region.DataSnapshot.Providers { public class EstateSnapshot : IDataSnapshotProvider { + /* This module doesn't check for changes, since it's *assumed* there are none. + * Nevertheless, it's possible to have changes, since all the fields are public. + * There's no event to subscribe to. :/ + * + * I don't think anything changes the fields beyond RegionModule PostInit, however. + */ private Scene m_scene = null; private DataSnapshotManager m_parent = null; + private bool m_stale = true; #region IDataSnapshotProvider Members @@ -70,6 +77,7 @@ namespace OpenSim.Region.DataSnapshot estatedata.AppendChild(user); + this.Stale = false; return estatedata; } @@ -84,6 +92,25 @@ namespace OpenSim.Region.DataSnapshot get { return m_scene; } } + public String Name { + get { return "EstateSnapshot"; } + } + + public bool Stale + { + get { + return m_stale; + } + set { + m_stale = value; + + if (m_stale) + OnStale(this); + } + } + + public event ProviderStale OnStale; + #endregion } -} +} \ No newline at end of file diff --git a/OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshot.cs b/OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshot.cs new file mode 100644 index 0000000000..697d31326a --- /dev/null +++ b/OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshot.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace OpenSim.Region.DataSnapshot.Interfaces +{ + public interface IDataSnapshot + { + XmlDocument GetSnapshot(string regionName); + void MakeEverythingStale(); + } +} diff --git a/OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshotProvider.cs b/OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshotProvider.cs index fb9c51a1d9..1519355e7d 100644 --- a/OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshotProvider.cs +++ b/OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshotProvider.cs @@ -26,19 +26,21 @@ * */ +using System; using System.Xml; using OpenSim.Region.Environment.Scenes; namespace OpenSim.Region.DataSnapshot.Interfaces { + public delegate void ProviderStale(IDataSnapshotProvider provider); + public interface IDataSnapshotProvider { XmlNode RequestSnapshotData(XmlDocument document); - - //void PrepareData(); - void Initialize(Scene scene, DataSnapshotManager parent); - Scene GetParentScene { get; } + String Name { get; } + bool Stale { get; set; } + event ProviderStale OnStale; } -} \ No newline at end of file +} diff --git a/OpenSim/Region/DataSnapshot/LLSDDiscovery.cs b/OpenSim/Region/DataSnapshot/LLSDDiscovery.cs new file mode 100644 index 0000000000..7b4f4ec154 --- /dev/null +++ b/OpenSim/Region/DataSnapshot/LLSDDiscovery.cs @@ -0,0 +1,46 @@ +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + +using System; +using OpenSim.Framework.Communications.Capabilities; + +namespace OpenSim.Region.DataSnapshot +{ + [LLSDMap] + public class LLSDDiscoveryResponse + { + public LLSDArray snapshot_resources; + } + + [LLSDMap] + public class LLSDDiscoveryDataURL + { + public string snapshot_format; + public string snapshot_url; + } +} diff --git a/OpenSim/Region/DataSnapshot/LandSnapshot.cs b/OpenSim/Region/DataSnapshot/LandSnapshot.cs index 183c2a31c0..4bfa87ab0e 100644 --- a/OpenSim/Region/DataSnapshot/LandSnapshot.cs +++ b/OpenSim/Region/DataSnapshot/LandSnapshot.cs @@ -23,7 +23,7 @@ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* +* */ using System; @@ -37,8 +37,9 @@ using OpenSim.Region.DataSnapshot.Interfaces; using OpenSim.Region.Environment.Interfaces; using OpenSim.Region.Environment.Modules.World.Land; using OpenSim.Region.Environment.Scenes; +using libsecondlife.Packets; -namespace OpenSim.Region.DataSnapshot +namespace OpenSim.Region.DataSnapshot.Providers { public class LandSnapshot : IDataSnapshotProvider { @@ -46,14 +47,15 @@ namespace OpenSim.Region.DataSnapshot private DataSnapshotManager m_parent = null; //private Dictionary m_landIndexed = new Dictionary(); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private bool m_stale = true; #region Dead code /* * David, I don't think we need this at all. When we do the snapshot, we can - * simply look into the parcels that are marked for ShowDirectory -- see + * simply look into the parcels that are marked for ShowDirectory -- see * conditional in RequestSnapshotData - * + * //Revise this, look for more direct way of checking for change in land #region Client hooks @@ -106,7 +108,9 @@ namespace OpenSim.Region.DataSnapshot { m_scene = scene; m_parent = parent; - //m_scene.EventManager.OnNewClient += OnNewClient; + + //Brought back from the dead for staleness checks. + m_scene.EventManager.OnNewClient += OnNewClient; } public Scene GetParentScene @@ -115,7 +119,7 @@ namespace OpenSim.Region.DataSnapshot } public XmlNode RequestSnapshotData(XmlDocument nodeFactory) - { + { ILandChannel landChannel = (LandChannel)m_scene.LandChannel; Dictionary landList = null; try @@ -129,7 +133,7 @@ namespace OpenSim.Region.DataSnapshot } catch (Exception e) { - Console.WriteLine("[DATASNAPSHOT] couldn't access field reflectively\n" + e.ToString()); + m_log.Error("[DATASNAPSHOT] couldn't access field reflectively\n" + e.ToString()); } XmlNode parent = nodeFactory.CreateNode(XmlNodeType.Element, "parceldata", ""); if (landList != null) @@ -243,9 +247,32 @@ namespace OpenSim.Region.DataSnapshot //snap.AppendChild(parent); } + this.Stale = false; return parent; } + public String Name + { + get { return "LandSnapshot"; } + } + + public bool Stale + { + get + { + return m_stale; + } + set + { + m_stale = value; + + if (m_stale) + OnStale(this); + } + } + + public event ProviderStale OnStale; + #endregion #region Helper functions @@ -260,5 +287,33 @@ namespace OpenSim.Region.DataSnapshot } #endregion + + #region Change detection hooks + + public void OnNewClient(IClientAPI client) + { + //Land hooks + client.OnParcelDivideRequest += delegate (int west, int south, int east, int north, + IClientAPI remote_client) { this.Stale = true; }; + client.OnParcelJoinRequest += delegate (int west, int south, int east, int north, + IClientAPI remote_client) { this.Stale = true; }; + client.OnParcelPropertiesUpdateRequest += delegate(LandUpdateArgs args, int local_id, + IClientAPI remote_client) { this.Stale = true; }; + client.OnParcelBuy += delegate (LLUUID agentId, LLUUID groupId, bool final, bool groupOwned, + bool removeContribution, int parcelLocalID, int parcelArea, int parcelPrice, bool authenticated) + { this.Stale = true; }; + } + + public void ParcelSplitHook(int west, int south, int east, int north, IClientAPI remote_client) + { + this.Stale = true; + } + + public void ParcelPropsHook(LandUpdateArgs args, int local_id, IClientAPI remote_client) + { + this.Stale = true; + } + + #endregion } -} +} \ No newline at end of file diff --git a/OpenSim/Region/DataSnapshot/ObjectSnapshot.cs b/OpenSim/Region/DataSnapshot/ObjectSnapshot.cs index 3270c3ab2a..af73ba04e4 100644 --- a/OpenSim/Region/DataSnapshot/ObjectSnapshot.cs +++ b/OpenSim/Region/DataSnapshot/ObjectSnapshot.cs @@ -25,24 +25,63 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using System; +using System.Collections.Generic; using System.Reflection; using System.Xml; using log4net; using OpenSim.Region.DataSnapshot.Interfaces; using OpenSim.Region.Environment.Scenes; +using OpenSim.Framework; +using libsecondlife; -namespace OpenSim.Region.DataSnapshot +namespace OpenSim.Region.DataSnapshot.Providers { public class ObjectSnapshot : IDataSnapshotProvider { private Scene m_scene = null; private DataSnapshotManager m_parent = null; private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private bool m_stale = true; public void Initialize(Scene scene, DataSnapshotManager parent) { m_scene = scene; m_parent = parent; + + //To check for staleness, we must catch all incoming client packets. + m_scene.EventManager.OnNewClient += OnNewClient; + m_scene.EventManager.OnParcelPrimCountAdd += delegate(SceneObjectGroup obj) { this.Stale = true; }; + } + + public void OnNewClient(IClientAPI client) + { + //Detect object data changes by hooking into the IClientAPI. + //Very dirty, and breaks whenever someone changes the client API. + + client.OnAddPrim += delegate (LLUUID ownerID, LLVector3 RayEnd, LLQuaternion rot, + PrimitiveBaseShape shape, byte bypassRaycast, LLVector3 RayStart, LLUUID RayTargetID, + byte RayEndIsIntersection) { this.Stale = true; }; + client.OnLinkObjects += delegate (IClientAPI remoteClient, uint parent, List children) + { this.Stale = true; }; + client.OnDelinkObjects += delegate(List primIds) { this.Stale = true; }; + client.OnGrabUpdate += delegate(LLUUID objectID, LLVector3 offset, LLVector3 grapPos, + IClientAPI remoteClient) { this.Stale = true; }; + client.OnObjectAttach += delegate(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, + LLQuaternion rot) { this.Stale = true; }; + client.OnObjectDuplicate += delegate(uint localID, LLVector3 offset, uint dupeFlags, LLUUID AgentID, + LLUUID GroupID) { this.Stale = true; }; + client.OnObjectDuplicateOnRay += delegate(uint localID, uint dupeFlags, LLUUID AgentID, LLUUID GroupID, + LLUUID RayTargetObj, LLVector3 RayEnd, LLVector3 RayStart, bool BypassRaycast, + bool RayEndIsIntersection, bool CopyCenters, bool CopyRotates) { this.Stale = true; }; + client.OnObjectIncludeInSearch += delegate(IClientAPI remoteClient, bool IncludeInSearch, uint localID) + { this.Stale = true; }; + client.OnObjectPermissions += delegate(IClientAPI controller, LLUUID agentID, LLUUID sessionID, + byte field, uint localId, uint mask, byte set) { this.Stale = true; }; + client.OnRezObject += delegate(IClientAPI remoteClient, LLUUID itemID, LLVector3 RayEnd, + LLVector3 RayStart, LLUUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, + uint EveryoneMask, uint GroupMask, uint NextOwnerMask, uint ItemFlags, bool RezSelected, + bool RemoveItem, LLUUID fromTaskID) { this.Stale = true; }; } public Scene GetParentScene @@ -52,56 +91,83 @@ namespace OpenSim.Region.DataSnapshot public XmlNode RequestSnapshotData(XmlDocument nodeFactory) { + m_log.Debug("[DATASNAPSHOT]: Generating object data for scene " + m_scene.RegionInfo.RegionName); + XmlNode parent = nodeFactory.CreateNode(XmlNodeType.Element, "objectdata", ""); XmlNode node; -#if LIBSL_IS_FIXED + foreach (EntityBase entity in m_scene.Entities.Values) { // only objects, not avatars - if (entity is SceneObjectGroup) + if (entity is SceneObjectGroup) { SceneObjectGroup obj = (SceneObjectGroup)entity; - XmlNode xmlobject = nodeFactory.CreateNode(XmlNodeType.Element, "object", ""); + m_log.Debug("[DATASNAPSHOT]: Found object " + obj.Name + " in scene"); - node = nodeFactory.CreateNode(XmlNodeType.Element, "uuid", ""); - node.InnerText = obj.UUID.ToString(); - xmlobject.AppendChild(node); + if ((obj.RootPart.Flags & LLObject.ObjectFlags.JointWheel) == LLObject.ObjectFlags.JointWheel) { + XmlNode xmlobject = nodeFactory.CreateNode(XmlNodeType.Element, "object", ""); - SceneObjectPart m_rootPart = null; - try - { - Type sog = typeof(SceneObjectGroup); - FieldInfo rootField = sog.GetField("m_rootPart", BindingFlags.NonPublic | BindingFlags.Instance); - if (rootField != null) + node = nodeFactory.CreateNode(XmlNodeType.Element, "uuid", ""); + node.InnerText = obj.UUID.ToString(); + xmlobject.AppendChild(node); + + SceneObjectPart m_rootPart = null; + try { - m_rootPart = (SceneObjectPart)rootField.GetValue(obj); + Type sog = typeof(SceneObjectGroup); + FieldInfo rootField = sog.GetField("m_rootPart", BindingFlags.NonPublic | BindingFlags.Instance); + if (rootField != null) + { + m_rootPart = (SceneObjectPart)rootField.GetValue(obj); + } } - } - catch (Exception e) - { - Console.WriteLine("[DATASNAPSHOT] couldn't access field reflectively\n" + e.ToString()); - } - if (m_rootPart != null) - { - node = nodeFactory.CreateNode(XmlNodeType.Element, "title", ""); - node.InnerText = m_rootPart.Name; - xmlobject.AppendChild(node); + catch (Exception e) + { + Console.WriteLine("[DATASNAPSHOT] couldn't access field reflectively\n" + e.ToString()); + } + if (m_rootPart != null) + { + node = nodeFactory.CreateNode(XmlNodeType.Element, "title", ""); + node.InnerText = m_rootPart.Name; + xmlobject.AppendChild(node); - node = nodeFactory.CreateNode(XmlNodeType.Element, "description", ""); - node.InnerText = m_rootPart.Description; - xmlobject.AppendChild(node); - - node = nodeFactory.CreateNode(XmlNodeType.Element, "flags", ""); - node.InnerText = String.Format("{0:x}", m_rootPart.ObjectFlags); - xmlobject.AppendChild(node); + node = nodeFactory.CreateNode(XmlNodeType.Element, "description", ""); + node.InnerText = m_rootPart.Description; + xmlobject.AppendChild(node); + + node = nodeFactory.CreateNode(XmlNodeType.Element, "flags", ""); + node.InnerText = String.Format("{0:x}", m_rootPart.ObjectFlags); + xmlobject.AppendChild(node); + } + parent.AppendChild(xmlobject); } - parent.AppendChild(xmlobject); } } -#endif + this.Stale = false; return parent; - } + + public String Name + { + get { return "ObjectSnapshot"; } + } + + public bool Stale + { + get + { + return m_stale; + } + set + { + m_stale = value; + + if (m_stale) + OnStale(this); + } + } + + public event ProviderStale OnStale; } -} +} \ No newline at end of file diff --git a/OpenSim/Region/DataSnapshot/SnapshotStore.cs b/OpenSim/Region/DataSnapshot/SnapshotStore.cs new file mode 100644 index 0000000000..05d564048b --- /dev/null +++ b/OpenSim/Region/DataSnapshot/SnapshotStore.cs @@ -0,0 +1,316 @@ +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; +using System.IO; +using OpenSim.Region.Environment.Scenes; +using OpenSim.Region.DataSnapshot.Interfaces; +using libsecondlife; + +namespace OpenSim.Region.DataSnapshot +{ + public class SnapshotStore + { + #region Class Members + private String m_directory = "unyuu"; //not an attempt at adding RM references to core SVN, honest + private Dictionary m_scenes = null; + private List m_providers = null; + private log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private Dictionary m_gridinfo = null; + private bool m_cacheEnabled = true; + private string m_listener_port = "9000"; //TODO: Set default port over 9000 + private string m_hostname = "127.0.0.1"; + #endregion + + public SnapshotStore(string directory, Dictionary gridinfo, string port, string hostname) { + m_directory = directory; + m_scenes = new Dictionary(); + m_providers = new List(); + m_gridinfo = gridinfo; + m_listener_port = port; + m_hostname = hostname; + + if (Directory.Exists(m_directory)) + { + m_log.Info("[DATASNAPSHOT]: Repsonse and fragment cache directory already exists."); + } + else + { + // Try to create the directory. + m_log.Info("[DATASNAPSHOT]: Creating directory " + m_directory); + try + { + Directory.CreateDirectory(m_directory); + } + catch (Exception e) + { + m_log.Error("[DATASNAPSHOT]: Failed to create directory " + m_directory, e); + + //This isn't a horrible problem, just disable cacheing. + m_cacheEnabled = false; + m_log.Error("[DATASNAPSHOT]: Could not create directory, response cache has been disabled."); + } + } + } + + public void ForceSceneStale(Scene scene) { + m_scenes[scene] = true; + } + + #region Fragment storage + public XmlNode GetFragment(IDataSnapshotProvider provider, XmlDocument factory) + { + XmlNode data = null; + + if (provider.Stale || !m_cacheEnabled) + { + data = provider.RequestSnapshotData(factory); + + if (m_cacheEnabled) + { + String path = DataFileNameFragment(provider.GetParentScene, provider.Name); + + using (XmlTextWriter snapXWriter = new XmlTextWriter(path, Encoding.Default)) + { + snapXWriter.Formatting = Formatting.Indented; + snapXWriter.WriteStartDocument(); + data.WriteTo(snapXWriter); + snapXWriter.WriteEndDocument(); + } + } + + //mark provider as not stale, parent scene as stale + provider.Stale = false; + m_scenes[provider.GetParentScene] = true; + + m_log.Info("[DATASNAPSHOT]: Generated fragment response for provider type " + provider.Name); + } + else + { + String path = DataFileNameFragment(provider.GetParentScene, provider.Name); + + XmlDocument fragDocument = new XmlDocument(); + fragDocument.PreserveWhitespace = true; + fragDocument.Load(path); + foreach (XmlNode node in fragDocument) + { + data = factory.ImportNode(node, true); + } + + m_log.Info("[DATASNAPSHOT]: Retrieved fragment response for provider type " + provider.Name); + } + + return data; + } + #endregion + + #region Response storage + public XmlNode GetScene(Scene scene, XmlDocument factory) + { + m_log.Debug("[DATASNAPSHOT]: Data requested for scene " + scene.RegionInfo.RegionName); + + if (!m_scenes.ContainsKey(scene)) { + m_scenes.Add(scene, true); //stale by default + } + + XmlNode regionElement = null; + + if (!m_scenes[scene]) + { + m_log.Info("[DATASNAPSHOT]: Attempting to retrieve snapshot from cache."); + //get snapshot from cache + String path = DataFileNameScene(scene); + + XmlDocument fragDocument = new XmlDocument(); + fragDocument.PreserveWhitespace = true; + + fragDocument.Load(path); + + foreach (XmlNode node in fragDocument) + { + regionElement = factory.ImportNode(node, true); + } + + m_log.Info("[DATASNAPSHOT]: Obtained snapshot from cache for " + scene.RegionInfo.RegionName); + } + else + { + m_log.Info("[DATASNAPSHOT]: Attempting to generate snapshot."); + //make snapshot + regionElement = MakeRegionNode(scene, factory); + + regionElement.AppendChild(GetGridSnapshotData(factory)); + XmlNode regionData = factory.CreateNode(XmlNodeType.Element, "data", ""); + + foreach (IDataSnapshotProvider dataprovider in m_providers) + { + if (dataprovider.GetParentScene == scene) + { + regionData.AppendChild(GetFragment(dataprovider, factory)); + } + } + + regionElement.AppendChild(regionData); + + factory.AppendChild(regionElement); + + //save snapshot + String path = DataFileNameScene(scene); + + using (XmlTextWriter snapXWriter = new XmlTextWriter(path, Encoding.Default)) + { + snapXWriter.Formatting = Formatting.Indented; + snapXWriter.WriteStartDocument(); + regionElement.WriteTo(snapXWriter); + snapXWriter.WriteEndDocument(); + } + + m_scenes[scene] = false; + + m_log.Info("[DATASNAPSHOT]: Generated new snapshot for " + scene.RegionInfo.RegionName); + } + + return regionElement; + } + + #endregion + + #region Helpers + private string DataFileNameFragment(Scene scene, String fragmentName) + { + return Path.Combine(m_directory, Path.ChangeExtension(scene.RegionInfo.RegionName + "_" + fragmentName, "xml")); + } + + private string DataFileNameScene(Scene scene) + { + return Path.Combine(m_directory, Path.ChangeExtension(scene.RegionInfo.RegionName, "xml")); + //return (m_snapsDir + Path.DirectorySeparatorChar + scene.RegionInfo.RegionName + ".xml"); + } + + private XmlNode MakeRegionNode(Scene scene, XmlDocument basedoc) + { + XmlNode docElement = basedoc.CreateNode(XmlNodeType.Element, "region", ""); + + XmlAttribute attr = basedoc.CreateAttribute("category"); + attr.Value = GetRegionCategory(scene); + docElement.Attributes.Append(attr); + + attr = basedoc.CreateAttribute("entities"); + attr.Value = scene.Entities.Count.ToString(); + docElement.Attributes.Append(attr); + + //attr = basedoc.CreateAttribute("parcels"); + //attr.Value = scene.LandManager.landList.Count.ToString(); + //docElement.Attributes.Append(attr); + + + XmlNode infoblock = basedoc.CreateNode(XmlNodeType.Element, "info", ""); + + XmlNode infopiece = basedoc.CreateNode(XmlNodeType.Element, "uuid", ""); + infopiece.InnerText = scene.RegionInfo.RegionID.ToString(); + infoblock.AppendChild(infopiece); + + infopiece = basedoc.CreateNode(XmlNodeType.Element, "url", ""); + infopiece.InnerText = "http://" + m_hostname + ":" + m_listener_port; + infoblock.AppendChild(infopiece); + + infopiece = basedoc.CreateNode(XmlNodeType.Element, "name", ""); + infopiece.InnerText = scene.RegionInfo.RegionName; + infoblock.AppendChild(infopiece); + + docElement.AppendChild(infoblock); + + m_log.Debug("[DATASNAPSHOT]: Generated region node"); + return docElement; + } + + private String GetRegionCategory(Scene scene) + { + + //Boolean choice between: + // "PG" - Mormontown + // "Mature" - Sodom and Gomorrah + // (Depreciated) "Patriotic Nigra Testing Sandbox" - Abandon Hope All Ye Who Enter Here + if ((scene.RegionInfo.EstateSettings.simAccess & Simulator.SimAccess.Mature) == Simulator.SimAccess.Mature) + { + return "Mature"; + } + else if ((scene.RegionInfo.EstateSettings.simAccess & Simulator.SimAccess.PG) == Simulator.SimAccess.PG) + { + return "PG"; + } + else + { + return "Unknown"; + } + } + + private XmlNode GetGridSnapshotData(XmlDocument factory) + { + XmlNode griddata = factory.CreateNode(XmlNodeType.Element, "grid", ""); + + foreach (KeyValuePair GridData in m_gridinfo) + { + //TODO: make it lowercase tag names for diva + XmlNode childnode = factory.CreateNode(XmlNodeType.Element, GridData.Key, ""); + childnode.InnerText = GridData.Value; + griddata.AppendChild(childnode); + } + + m_log.Debug("[DATASNAPSHOT]: Got grid snapshot data"); + + return griddata; + } + #endregion + + #region Manage internal collections + public void AddScene(Scene newScene) + { + m_scenes.Add(newScene, true); + } + + public void RemoveScene(Scene deadScene) + { + m_scenes.Remove(deadScene); + } + + public void AddProvider(IDataSnapshotProvider newProvider) + { + m_providers.Add(newProvider); + } + + public void RemoveProvider(IDataSnapshotProvider deadProvider) + { + m_providers.Remove(deadProvider); + } + #endregion + } +}