aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/Region/DataSnapshot/DataRequestHandler.cs37
-rw-r--r--OpenSim/Region/DataSnapshot/DataSnapshotManager.cs451
-rw-r--r--OpenSim/Region/DataSnapshot/EstateSnapshot.cs31
-rw-r--r--OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshot.cs13
-rw-r--r--OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshotProvider.cs12
-rw-r--r--OpenSim/Region/DataSnapshot/LLSDDiscovery.cs46
-rw-r--r--OpenSim/Region/DataSnapshot/LandSnapshot.cs71
-rw-r--r--OpenSim/Region/DataSnapshot/ObjectSnapshot.cs134
-rw-r--r--OpenSim/Region/DataSnapshot/SnapshotStore.cs316
9 files changed, 785 insertions, 326 deletions
diff --git a/OpenSim/Region/DataSnapshot/DataRequestHandler.cs b/OpenSim/Region/DataSnapshot/DataRequestHandler.cs
index e08934b..abf2c1a 100644
--- a/OpenSim/Region/DataSnapshot/DataRequestHandler.cs
+++ b/OpenSim/Region/DataSnapshot/DataRequestHandler.cs
@@ -26,11 +26,16 @@
26* 26*
27*/ 27*/
28 28
29using System;
29using System.Collections; 30using System.Collections;
30using System.Reflection; 31using System.Reflection;
31using System.Xml; 32using System.Xml;
32using log4net; 33using log4net;
33using OpenSim.Region.Environment.Scenes; 34using OpenSim.Region.Environment.Scenes;
35using OpenSim.Framework.Communications.Capabilities;
36using Caps = OpenSim.Framework.Communications.Capabilities.Caps;
37using libsecondlife;
38using OpenSim.Framework.Servers;
34 39
35namespace OpenSim.Region.DataSnapshot 40namespace OpenSim.Region.DataSnapshot
36{ 41{
@@ -40,18 +45,50 @@ namespace OpenSim.Region.DataSnapshot
40 private DataSnapshotManager m_externalData = null; 45 private DataSnapshotManager m_externalData = null;
41 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 46 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
42 47
48 private readonly string m_discoveryPath = "DS0001/";
49
43 public DataRequestHandler(Scene scene, DataSnapshotManager externalData) 50 public DataRequestHandler(Scene scene, DataSnapshotManager externalData)
44 { 51 {
45 m_scene = scene; 52 m_scene = scene;
46 m_externalData = externalData; 53 m_externalData = externalData;
47 54
55 //Register HTTP handler
48 if (m_scene.AddHTTPHandler("collector", OnGetSnapshot)) 56 if (m_scene.AddHTTPHandler("collector", OnGetSnapshot))
49 { 57 {
50 m_log.Info("[DATASNAPSHOT]: Set up snapshot service"); 58 m_log.Info("[DATASNAPSHOT]: Set up snapshot service");
51 } 59 }
60
61 //Register CAPS handler event
62 m_scene.EventManager.OnRegisterCaps += OnRegisterCaps;
63
52 //harbl 64 //harbl
53 } 65 }
54 66
67 public void OnRegisterCaps(LLUUID agentID, Caps caps)
68 {
69 m_log.Info("[DATASNAPSHOT]: Registering service discovery capability for " + agentID);
70 string capsBase = "/CAPS/" + caps.CapsObjectPath;
71 caps.RegisterHandler("PublicSnapshotDataInfo",
72 new RestStreamHandler("POST", capsBase + m_discoveryPath, OnDiscoveryAttempt));
73 }
74
75 public string OnDiscoveryAttempt(string request, string path, string param)
76 {
77 //Very static for now, flexible enough to add new formats
78 LLSDDiscoveryResponse llsd_response = new LLSDDiscoveryResponse();
79 llsd_response.snapshot_resources = new LLSDArray();
80
81 LLSDDiscoveryDataURL llsd_dataurl = new LLSDDiscoveryDataURL();
82 llsd_dataurl.snapshot_format = "os-datasnapshot-v1";
83 llsd_dataurl.snapshot_url = "http://" + m_externalData.m_hostname + ":" + m_externalData.m_listener_port + "/?method=collector";
84
85 llsd_response.snapshot_resources.Array.Add(llsd_dataurl);
86
87 string response = LLSDHelpers.SerialiseLLSDReply(llsd_response);
88
89 return response;
90 }
91
55 public Hashtable OnGetSnapshot(Hashtable keysvals) 92 public Hashtable OnGetSnapshot(Hashtable keysvals)
56 { 93 {
57 m_log.Info("[DATASNAPSHOT] Received collection request"); 94 m_log.Info("[DATASNAPSHOT] Received collection request");
diff --git a/OpenSim/Region/DataSnapshot/DataSnapshotManager.cs b/OpenSim/Region/DataSnapshot/DataSnapshotManager.cs
index af3e547..6fdedb4 100644
--- a/OpenSim/Region/DataSnapshot/DataSnapshotManager.cs
+++ b/OpenSim/Region/DataSnapshot/DataSnapshotManager.cs
@@ -37,49 +37,62 @@ using System.Xml;
37using libsecondlife; 37using libsecondlife;
38using log4net; 38using log4net;
39using Nini.Config; 39using Nini.Config;
40using OpenSim.Framework;
40using OpenSim.Framework.Communications; 41using OpenSim.Framework.Communications;
41using OpenSim.Region.DataSnapshot.Interfaces; 42using OpenSim.Region.DataSnapshot.Interfaces;
42using OpenSim.Region.Environment.Interfaces; 43using OpenSim.Region.Environment.Interfaces;
43using OpenSim.Region.Environment.Scenes; 44using OpenSim.Region.Environment.Scenes;
45using libsecondlife.Packets;
44 46
45namespace OpenSim.Region.DataSnapshot 47namespace OpenSim.Region.DataSnapshot
46{ 48{
47 public class DataSnapshotManager : IRegionModule 49 public class DataSnapshotManager : IRegionModule, IDataSnapshot
48 { 50 {
49 #region Class members 51 #region Class members
50 private List<Scene> m_scenes = new List<Scene>(); 52 //Information from config
51 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
52 private bool m_enabled = false; 53 private bool m_enabled = false;
53 private bool m_configLoaded = false; 54 private bool m_configLoaded = false;
54 internal object m_syncInit = new object(); 55 private List<String> m_disabledModules = new List<String>();
55 private DataRequestHandler m_requests = null;
56 private Dictionary<Scene, List<IDataSnapshotProvider>> m_dataproviders = new Dictionary<Scene, List<IDataSnapshotProvider>>();
57 private Dictionary<string, string> m_gridinfo = new Dictionary<string, string>(); 56 private Dictionary<string, string> m_gridinfo = new Dictionary<string, string>();
58 //private int m_oldestSnapshot = 0;
59 private int m_maxSnapshots = 500;
60 private int m_lastSnapshot = 0;
61 private string m_snapsDir = "DataSnapshot"; 57 private string m_snapsDir = "DataSnapshot";
58
59 //Lists of stuff we need
60 private List<Scene> m_scenes = new List<Scene>();
61 private List<IDataSnapshotProvider> m_dataproviders = new List<IDataSnapshotProvider>();
62
63 //Various internal objects
64 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
65 internal object m_syncInit = new object();
66
67 //DataServices and networking
62 private string m_dataServices = "noservices"; 68 private string m_dataServices = "noservices";
63 private string m_listener_port = "9000"; //TODO: Set default port over 9000 69 public string m_listener_port = "9000"; //TODO: Set default port over 9000
64 private string m_hostname = "127.0.0.1"; 70 public string m_hostname = "127.0.0.1";
71
72 //Update timers
65 private Timer m_periodic = null; 73 private Timer m_periodic = null;
66 private int m_period = 60; // in seconds 74 private int m_period = 20; // in seconds
67 private List<string> m_disabledModules = new List<string>(); 75 private int m_maxStales = 500;
76 private int m_stales = 0;
77 private Timer m_passedCheck = null;
78 private bool m_periodPassed = false;
79
80 //Program objects
81 private SnapshotStore m_snapStore = null;
82 private DataRequestHandler m_requests = null;
83
68 #endregion 84 #endregion
69 85
70 #region IRegionModule 86 #region IRegionModule
87
71 public void Close() 88 public void Close()
72 { 89 {
73 90 m_log.Info("[DATASNAPSHOT]: Close called");
74 } 91 }
75 92
76 public void Initialise(Scene scene, IConfigSource config) 93 public void Initialise(Scene scene, IConfigSource config)
77 { 94 {
78 if (!m_scenes.Contains(scene)) 95 if (!m_configLoaded) {
79 m_scenes.Add(scene);
80
81 if (!m_configLoaded)
82 {
83 m_configLoaded = true; 96 m_configLoaded = true;
84 m_log.Info("[DATASNAPSHOT]: Loading configuration"); 97 m_log.Info("[DATASNAPSHOT]: Loading configuration");
85 //Read from the config for options 98 //Read from the config for options
@@ -99,49 +112,59 @@ namespace OpenSim.Region.DataSnapshot
99 //Non gridmode stuff 112 //Non gridmode stuff
100 } 113 }
101 114
102 m_gridinfo.Add("Name", config.Configs["DataSnapshot"].GetString("gridname", "harbl")); 115 m_gridinfo.Add("Name", config.Configs["DataSnapshot"].GetString("gridname", "harbl"));
103 m_maxSnapshots = config.Configs["DataSnapshot"].GetInt("max_snapshots", m_maxSnapshots); 116 m_period = config.Configs["DataSnapshot"].GetInt("default_snapshot_period", m_period);
104 m_period = config.Configs["DataSnapshot"].GetInt("default_snapshot_period", m_period); 117 m_maxStales = config.Configs["DataSnapshot"].GetInt("max_changes_before_update", m_maxStales);
105 m_snapsDir = config.Configs["DataSnapshot"].GetString("snapshot_cache_directory", m_snapsDir); 118 m_snapsDir = config.Configs["DataSnapshot"].GetString("snapshot_cache_directory", m_snapsDir);
106 m_dataServices = config.Configs["DataSnapshot"].GetString("data_services", m_dataServices); 119 m_dataServices = config.Configs["DataSnapshot"].GetString("data_services", m_dataServices);
107 m_listener_port = config.Configs["Network"].GetString("http_listener_port", m_listener_port); 120 m_listener_port = config.Configs["Network"].GetString("http_listener_port", m_listener_port);
108 //BUG: Naming a search data module "DESUDESUDESU" will cause it to not get loaded by default. 121
109 //RESOLUTION: Wontfix, there are no Suiseiseki-loving developers 122 String[] annoying_string_array = config.Configs["DataSnapshot"].GetString("disable_modules", "").Split(".".ToCharArray());
110 String[] annoying_string_array = config.Configs["DataSnapshot"].GetString("disable_modules", "DESUDESUDESU").Split(".".ToCharArray()); 123 foreach (String bloody_wanker in annoying_string_array) {
111 foreach (String bloody_wanker in annoying_string_array) 124 m_disabledModules.Add(bloody_wanker);
112 { 125 }
113 m_disabledModules.Add(bloody_wanker); 126 } catch (Exception) {
114 }
115 }
116 catch (Exception)
117 {
118 m_log.Info("[DATASNAPSHOT]: Could not load configuration. DataSnapshot will be disabled."); 127 m_log.Info("[DATASNAPSHOT]: Could not load configuration. DataSnapshot will be disabled.");
119 m_enabled = false; 128 m_enabled = false;
120 return; 129 return;
121 } 130 }
122 } 131 }
123 } 132
124 if (Directory.Exists(m_snapsDir)) 133 m_snapStore = new SnapshotStore(m_snapsDir, m_gridinfo, m_listener_port, m_hostname);
125 {
126 m_log.Info("[DATASNAPSHOT] DataSnapshot directory already exists.");
127 }
128 else
129 {
130 // Try to create the directory.
131 m_log.Info("[DATASNAPSHOT] Creating " + m_snapsDir + " directory.");
132 try
133 {
134 Directory.CreateDirectory(m_snapsDir);
135 }
136 catch (Exception)
137 {
138 m_log.Error("[DATASNAPSHOT] Failed to create " + m_snapsDir + " directory.");
139 }
140 } 134 }
141 135
142 if (m_enabled) 136 if (m_enabled)
143 { 137 {
144 m_log.Info("[DATASNAPSHOT]: Scene added to module."); 138 m_log.Info("[DATASNAPSHOT]: Scene added to module.");
139
140 m_snapStore.AddScene(scene);
141 m_scenes.Add(scene);
142
143 Assembly currentasm = Assembly.GetExecutingAssembly();
144
145 foreach (Type pluginType in currentasm.GetTypes())
146 {
147 if (pluginType.IsPublic)
148 {
149 if (!pluginType.IsAbstract)
150 {
151 if (pluginType.GetInterface("IDataSnapshotProvider") != null)
152 {
153 IDataSnapshotProvider module = (IDataSnapshotProvider)Activator.CreateInstance(pluginType);
154 module.Initialize(scene, this);
155 module.OnStale += MarkDataStale;
156
157 m_dataproviders.Add(module);
158 m_snapStore.AddProvider(module);
159
160 m_log.Info("[DATASNAPSHOT]: Added new data provider type: " + pluginType.Name);
161 }
162 }
163 }
164 }
165
166 //scene.OnRestart += OnSimRestart;
167 scene.EventManager.OnShutdown += delegate() { OnSimRestart(scene.RegionInfo); };
145 } 168 }
146 else 169 else
147 { 170 {
@@ -163,68 +186,35 @@ namespace OpenSim.Region.DataSnapshot
163 { 186 {
164 if (m_enabled) 187 if (m_enabled)
165 { 188 {
166 //Right now, only load ISearchData objects in the current assembly.
167 //Eventually allow it to load ISearchData objects from all assemblies.
168 Assembly currentasm = Assembly.GetExecutingAssembly();
169
170 //Stolen from ModuleLoader.cs
171 foreach (Type pluginType in currentasm.GetTypes())
172 {
173 if (pluginType.IsPublic)
174 {
175 if (!pluginType.IsAbstract)
176 {
177 if (pluginType.GetInterface("IDataSnapshotProvider") != null)
178 {
179 foreach (Scene scene in m_scenes)
180 {
181 IDataSnapshotProvider module = (IDataSnapshotProvider)Activator.CreateInstance(pluginType);
182 module.Initialize(scene, this);
183 //module.PrepareData();
184 List<IDataSnapshotProvider> providerlist = null;
185 m_dataproviders.TryGetValue(scene, out providerlist);
186 if (providerlist == null)
187 {
188 providerlist = new List<IDataSnapshotProvider>();
189 m_dataproviders.Add(scene, providerlist);
190 }
191 providerlist.Add(module);
192
193 }
194 m_log.Info("[DATASNAPSHOT]: Added new data provider type: " + pluginType.Name);
195 }
196 }
197 }
198 }
199
200 //Hand it the first scene, assuming that all scenes have the same BaseHTTPServer 189 //Hand it the first scene, assuming that all scenes have the same BaseHTTPServer
201 m_requests = new DataRequestHandler(m_scenes[0], this); 190 m_requests = new DataRequestHandler(m_scenes[0], this);
202 191
203 //Create timer 192 //Create update timer
204 m_periodic = new Timer(); 193 m_periodic = new Timer();
205 m_periodic.Interval = m_period * 1000; 194 m_periodic.Interval = m_period * 1000;
206 m_periodic.Elapsed += SnapshotTimerCallback; 195 m_periodic.Elapsed += SnapshotTimerCallback;
207 m_periodic.Enabled = true; 196
197 //Create update eligibility timer
198 m_passedCheck = new Timer();
199 m_passedCheck.Interval = m_period * 1000;
200 m_passedCheck.Elapsed += UpdateEligibilityCallback;
201 m_passedCheck.Start();
208 202
209 m_hostname = m_scenes[0].RegionInfo.ExternalHostName; 203 m_hostname = m_scenes[0].RegionInfo.ExternalHostName;
210 204
211 MakeNewSnapshot(); //Make the initial snapshot 205 //m_snapStore = new SnapshotStore(m_snapsDir, m_dataproviders, m_gridinfo, m_listener_port, m_hostname);
206 MakeEverythingStale();
212 207
213 if (m_dataServices != "noservices") 208 if (m_dataServices != "noservices")
214 NotifyDataServices(m_dataServices); 209 NotifyDataServices(m_dataServices);
215 } 210 }
216 } 211 }
212
217 #endregion 213 #endregion
218 214
219 #region Associated helper functions 215 #region Associated helper functions
220 216
221 string DataFileName(Scene scene) 217 public Scene SceneForName(string name)
222 {
223 return Path.Combine(m_snapsDir, Path.ChangeExtension(scene.RegionInfo.RegionName, "xml"));
224 //return (m_snapsDir + Path.DirectorySeparatorChar + scene.RegionInfo.RegionName + ".xml");
225 }
226
227 Scene SceneForName(string name)
228 { 218 {
229 foreach (Scene scene in m_scenes) 219 foreach (Scene scene in m_scenes)
230 if (scene.RegionInfo.RegionName == name) 220 if (scene.RegionInfo.RegionName == name)
@@ -233,168 +223,19 @@ namespace OpenSim.Region.DataSnapshot
233 return null; 223 return null;
234 } 224 }
235 225
236 #endregion 226 public Scene SceneForUUID(LLUUID id)
237
238 #region [Private] XML snapshot generator
239
240 private XmlDocument Snapshot(Scene scene)
241 {
242 XmlDocument basedoc = new XmlDocument();
243 XmlNode regionElement = MakeRegionNode(scene, basedoc);
244
245 regionElement.AppendChild(GetGridSnapshotData(basedoc));
246 XmlNode regionData = basedoc.CreateNode(XmlNodeType.Element, "data", "");
247
248 foreach (KeyValuePair<Scene, List<IDataSnapshotProvider>> dataprovider in m_dataproviders)
249 {
250 if (dataprovider.Key == scene)
251 {
252 foreach (IDataSnapshotProvider provider in dataprovider.Value)
253 {
254 XmlNode data = provider.RequestSnapshotData(basedoc);
255 regionData.AppendChild(data);
256 }
257 }
258 }
259
260 regionElement.AppendChild(regionData);
261
262 basedoc.AppendChild(regionElement);
263
264 return basedoc;
265 }
266
267 private XmlNode MakeRegionNode(Scene scene, XmlDocument basedoc)
268 {
269 XmlNode docElement = basedoc.CreateNode(XmlNodeType.Element, "region", "");
270
271 XmlAttribute attr = basedoc.CreateAttribute("category");
272 attr.Value = GetRegionCategory(scene);
273 docElement.Attributes.Append(attr);
274
275 attr = basedoc.CreateAttribute("entities");
276 attr.Value = scene.Entities.Count.ToString();
277 docElement.Attributes.Append(attr);
278
279 //attr = basedoc.CreateAttribute("parcels");
280 //attr.Value = scene.LandManager.landList.Count.ToString();
281 //docElement.Attributes.Append(attr);
282
283
284 XmlNode infoblock = basedoc.CreateNode(XmlNodeType.Element, "info", "");
285
286 XmlNode infopiece = basedoc.CreateNode(XmlNodeType.Element, "uuid", "");
287 infopiece.InnerText = scene.RegionInfo.RegionID.ToString();
288 infoblock.AppendChild(infopiece);
289
290 infopiece = basedoc.CreateNode(XmlNodeType.Element, "url", "");
291 infopiece.InnerText = "http://" + m_hostname + ":" + m_listener_port;
292 infoblock.AppendChild(infopiece);
293
294 infopiece = basedoc.CreateNode(XmlNodeType.Element, "name", "");
295 infopiece.InnerText = scene.RegionInfo.RegionName;
296 infoblock.AppendChild(infopiece);
297
298 docElement.AppendChild(infoblock);
299
300 return docElement;
301 }
302
303 private XmlNode GetGridSnapshotData(XmlDocument factory)
304 {
305 XmlNode griddata = factory.CreateNode(XmlNodeType.Element, "grid", "");
306
307 foreach (KeyValuePair<String, String> GridData in m_gridinfo)
308 {
309 //TODO: make it lowercase tag names for diva
310 XmlNode childnode = factory.CreateNode(XmlNodeType.Element, GridData.Key, "");
311 childnode.InnerText = GridData.Value;
312 griddata.AppendChild(childnode);
313 }
314
315 return griddata;
316 }
317
318 private String GetRegionCategory(Scene scene)
319 { 227 {
228 foreach (Scene scene in m_scenes)
229 if (scene.RegionInfo.RegionID == id)
230 return scene;
320 231
321 //Boolean choice between: 232 return null;
322 // "PG" - Mormontown
323 // "Mature" - Sodom and Gomorrah
324 // (Depreciated) "Patriotic Nigra Testing Sandbox" - Abandon Hope All Ye Who Enter Here
325 if ((scene.RegionInfo.EstateSettings.simAccess & Simulator.SimAccess.Mature) == Simulator.SimAccess.Mature)
326 {
327 return "Mature";
328 }
329 else if ((scene.RegionInfo.EstateSettings.simAccess & Simulator.SimAccess.PG) == Simulator.SimAccess.PG)
330 {
331 return "PG";
332 }
333 else
334 {
335 return "Unknown";
336 }
337 } 233 }
338 234
339 /* Code's closed due to AIDS, See EstateSnapshot.cs for CURE
340 private XmlNode GetEstateSnapshotData(Scene scene, XmlDocument factory)
341 {
342 //Estate data section - contains who owns a set of sims and the name of the set.
343 //In Opensim all the estate names are the same as the Master Avatar (owner of the sim)
344 XmlNode estatedata = factory.CreateNode(XmlNodeType.Element, "estate", "");
345
346 LLUUID ownerid = scene.RegionInfo.MasterAvatarAssignedUUID;
347 String firstname = scene.RegionInfo.MasterAvatarFirstName;
348 String lastname = scene.RegionInfo.MasterAvatarLastName;
349 String hostname = scene.RegionInfo.ExternalHostName;
350
351 XmlNode user = factory.CreateNode(XmlNodeType.Element, "owner", "");
352
353 XmlNode username = factory.CreateNode(XmlNodeType.Element, "name", "");
354 username.InnerText = firstname + " " + lastname;
355 user.AppendChild(username);
356
357 XmlNode useruuid = factory.CreateNode(XmlNodeType.Element, "uuid", "");
358 useruuid.InnerText = ownerid.ToString();
359 user.AppendChild(useruuid);
360
361 estatedata.AppendChild(user);
362
363 return estatedata;
364 } */
365
366 #endregion 235 #endregion
367 236
368 #region [Public] Snapshot storage functions 237 #region [Public] Snapshot storage functions
369 238
370 public void MakeNewSnapshot()
371 {
372 foreach (Scene scene in m_scenes)
373 {
374 XmlDocument snapshot = Snapshot(scene);
375
376 string path = DataFileName(scene);
377
378 try
379 {
380 using (XmlTextWriter snapXWriter = new XmlTextWriter(path, Encoding.Default))
381 {
382 snapXWriter.Formatting = Formatting.Indented;
383 snapXWriter.WriteStartDocument();
384 snapshot.WriteTo(snapXWriter);
385 snapXWriter.WriteEndDocument();
386
387 m_lastSnapshot++;
388 }
389 }
390 catch (Exception e)
391 {
392 m_log.Warn("[DATASNAPSHOT]: Caught unknown exception while trying to save snapshot: " + path + "\n" + e.ToString());
393 }
394 m_log.Info("[DATASNAPSHOT]: Made external data snapshot " + path);
395 }
396 }
397
398 /** 239 /**
399 * Reply to the http request 240 * Reply to the http request
400 */ 241 */
@@ -410,30 +251,16 @@ namespace OpenSim.Region.DataSnapshot
410 { 251 {
411 foreach (Scene scene in m_scenes) 252 foreach (Scene scene in m_scenes)
412 { 253 {
413 string path = DataFileName(scene); 254 regiondata.AppendChild(m_snapStore.GetScene(scene, requestedSnap));
414 XmlDocument regionSnap = new XmlDocument();
415 regionSnap.PreserveWhitespace = true;
416
417 regionSnap.Load(path);
418 XmlNode nodeOrig = regionSnap["region"];
419 XmlNode nodeDest = requestedSnap.ImportNode(nodeOrig, true);
420 //requestedSnap.AppendChild(nodeDest);
421
422 regiondata.AppendChild(requestedSnap.CreateWhitespace("\r\n"));
423 regiondata.AppendChild(nodeDest);
424 } 255 }
425 } 256 }
426 else 257 else
427 { 258 {
428 Scene scene = SceneForName(regionName); 259 Scene scene = SceneForName(regionName);
429 requestedSnap.Load(DataFileName(scene)); 260 regiondata.AppendChild(m_snapStore.GetScene(scene, requestedSnap));
430 } 261 }
431 // requestedSnap.InsertBefore(requestedSnap.CreateXmlDeclaration("1.0", null, null),
432// requestedSnap.DocumentElement);
433 requestedSnap.AppendChild(regiondata); 262 requestedSnap.AppendChild(regiondata);
434 regiondata.AppendChild(requestedSnap.CreateWhitespace("\r\n")); 263 regiondata.AppendChild(requestedSnap.CreateWhitespace("\r\n"));
435
436
437 } 264 }
438 catch (XmlException e) 265 catch (XmlException e)
439 { 266 {
@@ -469,16 +296,6 @@ namespace OpenSim.Region.DataSnapshot
469 296
470 #endregion 297 #endregion
471 298
472 #region Event callbacks
473
474 private void SnapshotTimerCallback(object timer, ElapsedEventArgs args)
475 {
476 MakeNewSnapshot();
477 //Add extra calls here
478 }
479
480 #endregion
481
482 #region External data services 299 #region External data services
483 private void NotifyDataServices(string servicesStr) 300 private void NotifyDataServices(string servicesStr)
484 { 301 {
@@ -524,5 +341,85 @@ namespace OpenSim.Region.DataSnapshot
524 341
525 } 342 }
526 #endregion 343 #endregion
344
345 #region Latency-based update functions
346
347 public void MarkDataStale(IDataSnapshotProvider provider)
348 {
349 //Behavior here: Wait m_period seconds, then update if there has not been a request in m_period seconds
350 //or m_maxStales has been exceeded
351 m_stales++;
352
353 if ((m_stales >= m_maxStales) && m_periodPassed)
354 SnapshotTimerCallback(m_periodic, null);
355 else if (m_periodic.Enabled == false)
356 m_periodic.Start();
357 else
358 {
359 m_periodic.Stop();
360 m_periodic.Start();
361 }
362 }
363
364 private void SnapshotTimerCallback(object timer, ElapsedEventArgs args)
365 {
366 m_log.Debug("[DATASNAPSHOT]: Marking scenes for snapshot updates.");
367
368 //Finally generate those snapshot updates
369 MakeEverythingStale();
370
371 //Stop the update delay timer
372 m_periodic.Stop();
373
374 //Reset the eligibility flag and timer
375 m_periodPassed = false;
376 m_passedCheck.Stop();
377 m_passedCheck.Start();
378 }
379
380 private void UpdateEligibilityCallback(object timer, ElapsedEventArgs args)
381 {
382 //Set eligibility, so we can start making updates
383 m_periodPassed = true;
384 }
385
386 public void MakeEverythingStale()
387 {
388 m_log.Debug("[DATASNAPSHOT]: Marking all scenes as stale.");
389 foreach (Scene scene in m_scenes)
390 {
391 m_snapStore.ForceSceneStale(scene);
392 }
393 }
394
395 #endregion
396
397 public void OnSimRestart(RegionInfo thisRegion)
398 {
399 m_log.Info("[DATASNAPSHOT]: Region " + thisRegion.RegionName + " is restarting, removing from indexing");
400 Scene restartedScene = SceneForUUID(thisRegion.RegionID);
401
402 m_scenes.Remove(restartedScene);
403 m_snapStore.RemoveScene(restartedScene);
404
405 //Getting around the fact that we can't remove objects from a collection we are enumerating over
406 List<IDataSnapshotProvider> providersToRemove = new List<IDataSnapshotProvider>();
407
408 foreach (IDataSnapshotProvider provider in m_dataproviders)
409 {
410 if (provider.GetParentScene == restartedScene)
411 {
412 providersToRemove.Add(provider);
413 }
414 }
415
416 foreach (IDataSnapshotProvider provider in providersToRemove)
417 {
418 m_dataproviders.Remove(provider);
419 m_snapStore.RemoveProvider(provider);
420 }
421
422 m_snapStore.RemoveScene(restartedScene);
423 }
527 } 424 }
528} 425}
diff --git a/OpenSim/Region/DataSnapshot/EstateSnapshot.cs b/OpenSim/Region/DataSnapshot/EstateSnapshot.cs
index 8020713..40b96d5 100644
--- a/OpenSim/Region/DataSnapshot/EstateSnapshot.cs
+++ b/OpenSim/Region/DataSnapshot/EstateSnapshot.cs
@@ -31,12 +31,19 @@ using libsecondlife;
31using OpenSim.Region.DataSnapshot.Interfaces; 31using OpenSim.Region.DataSnapshot.Interfaces;
32using OpenSim.Region.Environment.Scenes; 32using OpenSim.Region.Environment.Scenes;
33 33
34namespace OpenSim.Region.DataSnapshot 34namespace OpenSim.Region.DataSnapshot.Providers
35{ 35{
36 public class EstateSnapshot : IDataSnapshotProvider 36 public class EstateSnapshot : IDataSnapshotProvider
37 { 37 {
38 /* This module doesn't check for changes, since it's *assumed* there are none.
39 * Nevertheless, it's possible to have changes, since all the fields are public.
40 * There's no event to subscribe to. :/
41 *
42 * I don't think anything changes the fields beyond RegionModule PostInit, however.
43 */
38 private Scene m_scene = null; 44 private Scene m_scene = null;
39 private DataSnapshotManager m_parent = null; 45 private DataSnapshotManager m_parent = null;
46 private bool m_stale = true;
40 47
41 #region IDataSnapshotProvider Members 48 #region IDataSnapshotProvider Members
42 49
@@ -70,6 +77,7 @@ namespace OpenSim.Region.DataSnapshot
70 77
71 estatedata.AppendChild(user); 78 estatedata.AppendChild(user);
72 79
80 this.Stale = false;
73 return estatedata; 81 return estatedata;
74 } 82 }
75 83
@@ -84,6 +92,25 @@ namespace OpenSim.Region.DataSnapshot
84 get { return m_scene; } 92 get { return m_scene; }
85 } 93 }
86 94
95 public String Name {
96 get { return "EstateSnapshot"; }
97 }
98
99 public bool Stale
100 {
101 get {
102 return m_stale;
103 }
104 set {
105 m_stale = value;
106
107 if (m_stale)
108 OnStale(this);
109 }
110 }
111
112 public event ProviderStale OnStale;
113
87 #endregion 114 #endregion
88 } 115 }
89} 116} \ 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 0000000..697d313
--- /dev/null
+++ b/OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshot.cs
@@ -0,0 +1,13 @@
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Xml;
5
6namespace OpenSim.Region.DataSnapshot.Interfaces
7{
8 public interface IDataSnapshot
9 {
10 XmlDocument GetSnapshot(string regionName);
11 void MakeEverythingStale();
12 }
13}
diff --git a/OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshotProvider.cs b/OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshotProvider.cs
index fb9c51a..1519355 100644
--- a/OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshotProvider.cs
+++ b/OpenSim/Region/DataSnapshot/Interfaces/IDataSnapshotProvider.cs
@@ -26,19 +26,21 @@
26* 26*
27*/ 27*/
28 28
29using System;
29using System.Xml; 30using System.Xml;
30using OpenSim.Region.Environment.Scenes; 31using OpenSim.Region.Environment.Scenes;
31 32
32namespace OpenSim.Region.DataSnapshot.Interfaces 33namespace OpenSim.Region.DataSnapshot.Interfaces
33{ 34{
35 public delegate void ProviderStale(IDataSnapshotProvider provider);
36
34 public interface IDataSnapshotProvider 37 public interface IDataSnapshotProvider
35 { 38 {
36 XmlNode RequestSnapshotData(XmlDocument document); 39 XmlNode RequestSnapshotData(XmlDocument document);
37
38 //void PrepareData();
39
40 void Initialize(Scene scene, DataSnapshotManager parent); 40 void Initialize(Scene scene, DataSnapshotManager parent);
41
42 Scene GetParentScene { get; } 41 Scene GetParentScene { get; }
42 String Name { get; }
43 bool Stale { get; set; }
44 event ProviderStale OnStale;
43 } 45 }
44} \ No newline at end of file 46}
diff --git a/OpenSim/Region/DataSnapshot/LLSDDiscovery.cs b/OpenSim/Region/DataSnapshot/LLSDDiscovery.cs
new file mode 100644
index 0000000..7b4f4ec
--- /dev/null
+++ b/OpenSim/Region/DataSnapshot/LLSDDiscovery.cs
@@ -0,0 +1,46 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28
29using System;
30using OpenSim.Framework.Communications.Capabilities;
31
32namespace OpenSim.Region.DataSnapshot
33{
34 [LLSDMap]
35 public class LLSDDiscoveryResponse
36 {
37 public LLSDArray snapshot_resources;
38 }
39
40 [LLSDMap]
41 public class LLSDDiscoveryDataURL
42 {
43 public string snapshot_format;
44 public string snapshot_url;
45 }
46}
diff --git a/OpenSim/Region/DataSnapshot/LandSnapshot.cs b/OpenSim/Region/DataSnapshot/LandSnapshot.cs
index 183c2a3..4bfa87a 100644
--- a/OpenSim/Region/DataSnapshot/LandSnapshot.cs
+++ b/OpenSim/Region/DataSnapshot/LandSnapshot.cs
@@ -23,7 +23,7 @@
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26* 26*
27*/ 27*/
28 28
29using System; 29using System;
@@ -37,8 +37,9 @@ using OpenSim.Region.DataSnapshot.Interfaces;
37using OpenSim.Region.Environment.Interfaces; 37using OpenSim.Region.Environment.Interfaces;
38using OpenSim.Region.Environment.Modules.World.Land; 38using OpenSim.Region.Environment.Modules.World.Land;
39using OpenSim.Region.Environment.Scenes; 39using OpenSim.Region.Environment.Scenes;
40using libsecondlife.Packets;
40 41
41namespace OpenSim.Region.DataSnapshot 42namespace OpenSim.Region.DataSnapshot.Providers
42{ 43{
43 public class LandSnapshot : IDataSnapshotProvider 44 public class LandSnapshot : IDataSnapshotProvider
44 { 45 {
@@ -46,14 +47,15 @@ namespace OpenSim.Region.DataSnapshot
46 private DataSnapshotManager m_parent = null; 47 private DataSnapshotManager m_parent = null;
47 //private Dictionary<int, Land> m_landIndexed = new Dictionary<int, Land>(); 48 //private Dictionary<int, Land> m_landIndexed = new Dictionary<int, Land>();
48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 49 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
50 private bool m_stale = true;
49 51
50 #region Dead code 52 #region Dead code
51 53
52 /* 54 /*
53 * David, I don't think we need this at all. When we do the snapshot, we can 55 * David, I don't think we need this at all. When we do the snapshot, we can
54 * simply look into the parcels that are marked for ShowDirectory -- see 56 * simply look into the parcels that are marked for ShowDirectory -- see
55 * conditional in RequestSnapshotData 57 * conditional in RequestSnapshotData
56 * 58 *
57 //Revise this, look for more direct way of checking for change in land 59 //Revise this, look for more direct way of checking for change in land
58 #region Client hooks 60 #region Client hooks
59 61
@@ -106,7 +108,9 @@ namespace OpenSim.Region.DataSnapshot
106 { 108 {
107 m_scene = scene; 109 m_scene = scene;
108 m_parent = parent; 110 m_parent = parent;
109 //m_scene.EventManager.OnNewClient += OnNewClient; 111
112 //Brought back from the dead for staleness checks.
113 m_scene.EventManager.OnNewClient += OnNewClient;
110 } 114 }
111 115
112 public Scene GetParentScene 116 public Scene GetParentScene
@@ -115,7 +119,7 @@ namespace OpenSim.Region.DataSnapshot
115 } 119 }
116 120
117 public XmlNode RequestSnapshotData(XmlDocument nodeFactory) 121 public XmlNode RequestSnapshotData(XmlDocument nodeFactory)
118 { 122 {
119 ILandChannel landChannel = (LandChannel)m_scene.LandChannel; 123 ILandChannel landChannel = (LandChannel)m_scene.LandChannel;
120 Dictionary<int, ILandObject> landList = null; 124 Dictionary<int, ILandObject> landList = null;
121 try 125 try
@@ -129,7 +133,7 @@ namespace OpenSim.Region.DataSnapshot
129 } 133 }
130 catch (Exception e) 134 catch (Exception e)
131 { 135 {
132 Console.WriteLine("[DATASNAPSHOT] couldn't access field reflectively\n" + e.ToString()); 136 m_log.Error("[DATASNAPSHOT] couldn't access field reflectively\n" + e.ToString());
133 } 137 }
134 XmlNode parent = nodeFactory.CreateNode(XmlNodeType.Element, "parceldata", ""); 138 XmlNode parent = nodeFactory.CreateNode(XmlNodeType.Element, "parceldata", "");
135 if (landList != null) 139 if (landList != null)
@@ -243,9 +247,32 @@ namespace OpenSim.Region.DataSnapshot
243 //snap.AppendChild(parent); 247 //snap.AppendChild(parent);
244 } 248 }
245 249
250 this.Stale = false;
246 return parent; 251 return parent;
247 } 252 }
248 253
254 public String Name
255 {
256 get { return "LandSnapshot"; }
257 }
258
259 public bool Stale
260 {
261 get
262 {
263 return m_stale;
264 }
265 set
266 {
267 m_stale = value;
268
269 if (m_stale)
270 OnStale(this);
271 }
272 }
273
274 public event ProviderStale OnStale;
275
249 #endregion 276 #endregion
250 277
251 #region Helper functions 278 #region Helper functions
@@ -260,5 +287,33 @@ namespace OpenSim.Region.DataSnapshot
260 } 287 }
261 288
262 #endregion 289 #endregion
290
291 #region Change detection hooks
292
293 public void OnNewClient(IClientAPI client)
294 {
295 //Land hooks
296 client.OnParcelDivideRequest += delegate (int west, int south, int east, int north,
297 IClientAPI remote_client) { this.Stale = true; };
298 client.OnParcelJoinRequest += delegate (int west, int south, int east, int north,
299 IClientAPI remote_client) { this.Stale = true; };
300 client.OnParcelPropertiesUpdateRequest += delegate(LandUpdateArgs args, int local_id,
301 IClientAPI remote_client) { this.Stale = true; };
302 client.OnParcelBuy += delegate (LLUUID agentId, LLUUID groupId, bool final, bool groupOwned,
303 bool removeContribution, int parcelLocalID, int parcelArea, int parcelPrice, bool authenticated)
304 { this.Stale = true; };
305 }
306
307 public void ParcelSplitHook(int west, int south, int east, int north, IClientAPI remote_client)
308 {
309 this.Stale = true;
310 }
311
312 public void ParcelPropsHook(LandUpdateArgs args, int local_id, IClientAPI remote_client)
313 {
314 this.Stale = true;
315 }
316
317 #endregion
263 } 318 }
264} 319} \ No newline at end of file
diff --git a/OpenSim/Region/DataSnapshot/ObjectSnapshot.cs b/OpenSim/Region/DataSnapshot/ObjectSnapshot.cs
index 3270c3a..af73ba0 100644
--- a/OpenSim/Region/DataSnapshot/ObjectSnapshot.cs
+++ b/OpenSim/Region/DataSnapshot/ObjectSnapshot.cs
@@ -25,24 +25,63 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System;
29using System.Collections.Generic;
28using System.Reflection; 30using System.Reflection;
29using System.Xml; 31using System.Xml;
30using log4net; 32using log4net;
31using OpenSim.Region.DataSnapshot.Interfaces; 33using OpenSim.Region.DataSnapshot.Interfaces;
32using OpenSim.Region.Environment.Scenes; 34using OpenSim.Region.Environment.Scenes;
35using OpenSim.Framework;
36using libsecondlife;
33 37
34namespace OpenSim.Region.DataSnapshot 38namespace OpenSim.Region.DataSnapshot.Providers
35{ 39{
36 public class ObjectSnapshot : IDataSnapshotProvider 40 public class ObjectSnapshot : IDataSnapshotProvider
37 { 41 {
38 private Scene m_scene = null; 42 private Scene m_scene = null;
39 private DataSnapshotManager m_parent = null; 43 private DataSnapshotManager m_parent = null;
40 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45 private bool m_stale = true;
41 46
42 public void Initialize(Scene scene, DataSnapshotManager parent) 47 public void Initialize(Scene scene, DataSnapshotManager parent)
43 { 48 {
44 m_scene = scene; 49 m_scene = scene;
45 m_parent = parent; 50 m_parent = parent;
51
52 //To check for staleness, we must catch all incoming client packets.
53 m_scene.EventManager.OnNewClient += OnNewClient;
54 m_scene.EventManager.OnParcelPrimCountAdd += delegate(SceneObjectGroup obj) { this.Stale = true; };
55 }
56
57 public void OnNewClient(IClientAPI client)
58 {
59 //Detect object data changes by hooking into the IClientAPI.
60 //Very dirty, and breaks whenever someone changes the client API.
61
62 client.OnAddPrim += delegate (LLUUID ownerID, LLVector3 RayEnd, LLQuaternion rot,
63 PrimitiveBaseShape shape, byte bypassRaycast, LLVector3 RayStart, LLUUID RayTargetID,
64 byte RayEndIsIntersection) { this.Stale = true; };
65 client.OnLinkObjects += delegate (IClientAPI remoteClient, uint parent, List<uint> children)
66 { this.Stale = true; };
67 client.OnDelinkObjects += delegate(List<uint> primIds) { this.Stale = true; };
68 client.OnGrabUpdate += delegate(LLUUID objectID, LLVector3 offset, LLVector3 grapPos,
69 IClientAPI remoteClient) { this.Stale = true; };
70 client.OnObjectAttach += delegate(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt,
71 LLQuaternion rot) { this.Stale = true; };
72 client.OnObjectDuplicate += delegate(uint localID, LLVector3 offset, uint dupeFlags, LLUUID AgentID,
73 LLUUID GroupID) { this.Stale = true; };
74 client.OnObjectDuplicateOnRay += delegate(uint localID, uint dupeFlags, LLUUID AgentID, LLUUID GroupID,
75 LLUUID RayTargetObj, LLVector3 RayEnd, LLVector3 RayStart, bool BypassRaycast,
76 bool RayEndIsIntersection, bool CopyCenters, bool CopyRotates) { this.Stale = true; };
77 client.OnObjectIncludeInSearch += delegate(IClientAPI remoteClient, bool IncludeInSearch, uint localID)
78 { this.Stale = true; };
79 client.OnObjectPermissions += delegate(IClientAPI controller, LLUUID agentID, LLUUID sessionID,
80 byte field, uint localId, uint mask, byte set) { this.Stale = true; };
81 client.OnRezObject += delegate(IClientAPI remoteClient, LLUUID itemID, LLVector3 RayEnd,
82 LLVector3 RayStart, LLUUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection,
83 uint EveryoneMask, uint GroupMask, uint NextOwnerMask, uint ItemFlags, bool RezSelected,
84 bool RemoveItem, LLUUID fromTaskID) { this.Stale = true; };
46 } 85 }
47 86
48 public Scene GetParentScene 87 public Scene GetParentScene
@@ -52,56 +91,83 @@ namespace OpenSim.Region.DataSnapshot
52 91
53 public XmlNode RequestSnapshotData(XmlDocument nodeFactory) 92 public XmlNode RequestSnapshotData(XmlDocument nodeFactory)
54 { 93 {
94 m_log.Debug("[DATASNAPSHOT]: Generating object data for scene " + m_scene.RegionInfo.RegionName);
95
55 XmlNode parent = nodeFactory.CreateNode(XmlNodeType.Element, "objectdata", ""); 96 XmlNode parent = nodeFactory.CreateNode(XmlNodeType.Element, "objectdata", "");
56 XmlNode node; 97 XmlNode node;
57#if LIBSL_IS_FIXED 98
58 foreach (EntityBase entity in m_scene.Entities.Values) 99 foreach (EntityBase entity in m_scene.Entities.Values)
59 { 100 {
60 // only objects, not avatars 101 // only objects, not avatars
61 if (entity is SceneObjectGroup) 102 if (entity is SceneObjectGroup)
62 { 103 {
63 SceneObjectGroup obj = (SceneObjectGroup)entity; 104 SceneObjectGroup obj = (SceneObjectGroup)entity;
64 105
65 XmlNode xmlobject = nodeFactory.CreateNode(XmlNodeType.Element, "object", ""); 106 m_log.Debug("[DATASNAPSHOT]: Found object " + obj.Name + " in scene");
66 107
67 node = nodeFactory.CreateNode(XmlNodeType.Element, "uuid", ""); 108 if ((obj.RootPart.Flags & LLObject.ObjectFlags.JointWheel) == LLObject.ObjectFlags.JointWheel) {
68 node.InnerText = obj.UUID.ToString(); 109 XmlNode xmlobject = nodeFactory.CreateNode(XmlNodeType.Element, "object", "");
69 xmlobject.AppendChild(node);
70 110
71 SceneObjectPart m_rootPart = null; 111 node = nodeFactory.CreateNode(XmlNodeType.Element, "uuid", "");
72 try 112 node.InnerText = obj.UUID.ToString();
73 {
74 Type sog = typeof(SceneObjectGroup);
75 FieldInfo rootField = sog.GetField("m_rootPart", BindingFlags.NonPublic | BindingFlags.Instance);
76 if (rootField != null)
77 {
78 m_rootPart = (SceneObjectPart)rootField.GetValue(obj);
79 }
80 }
81 catch (Exception e)
82 {
83 Console.WriteLine("[DATASNAPSHOT] couldn't access field reflectively\n" + e.ToString());
84 }
85 if (m_rootPart != null)
86 {
87 node = nodeFactory.CreateNode(XmlNodeType.Element, "title", "");
88 node.InnerText = m_rootPart.Name;
89 xmlobject.AppendChild(node); 113 xmlobject.AppendChild(node);
90 114
91 node = nodeFactory.CreateNode(XmlNodeType.Element, "description", ""); 115 SceneObjectPart m_rootPart = null;
92 node.InnerText = m_rootPart.Description; 116 try
93 xmlobject.AppendChild(node); 117 {
118 Type sog = typeof(SceneObjectGroup);
119 FieldInfo rootField = sog.GetField("m_rootPart", BindingFlags.NonPublic | BindingFlags.Instance);
120 if (rootField != null)
121 {
122 m_rootPart = (SceneObjectPart)rootField.GetValue(obj);
123 }
124 }
125 catch (Exception e)
126 {
127 Console.WriteLine("[DATASNAPSHOT] couldn't access field reflectively\n" + e.ToString());
128 }
129 if (m_rootPart != null)
130 {
131 node = nodeFactory.CreateNode(XmlNodeType.Element, "title", "");
132 node.InnerText = m_rootPart.Name;
133 xmlobject.AppendChild(node);
94 134
95 node = nodeFactory.CreateNode(XmlNodeType.Element, "flags", ""); 135 node = nodeFactory.CreateNode(XmlNodeType.Element, "description", "");
96 node.InnerText = String.Format("{0:x}", m_rootPart.ObjectFlags); 136 node.InnerText = m_rootPart.Description;
97 xmlobject.AppendChild(node); 137 xmlobject.AppendChild(node);
138
139 node = nodeFactory.CreateNode(XmlNodeType.Element, "flags", "");
140 node.InnerText = String.Format("{0:x}", m_rootPart.ObjectFlags);
141 xmlobject.AppendChild(node);
142 }
143 parent.AppendChild(xmlobject);
98 } 144 }
99 parent.AppendChild(xmlobject);
100 } 145 }
101 } 146 }
102#endif 147 this.Stale = false;
103 return parent; 148 return parent;
149 }
104 150
151 public String Name
152 {
153 get { return "ObjectSnapshot"; }
105 } 154 }
155
156 public bool Stale
157 {
158 get
159 {
160 return m_stale;
161 }
162 set
163 {
164 m_stale = value;
165
166 if (m_stale)
167 OnStale(this);
168 }
169 }
170
171 public event ProviderStale OnStale;
106 } 172 }
107} 173} \ 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 0000000..05d5640
--- /dev/null
+++ b/OpenSim/Region/DataSnapshot/SnapshotStore.cs
@@ -0,0 +1,316 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28
29using System;
30using System.Collections.Generic;
31using System.Text;
32using System.Xml;
33using System.IO;
34using OpenSim.Region.Environment.Scenes;
35using OpenSim.Region.DataSnapshot.Interfaces;
36using libsecondlife;
37
38namespace OpenSim.Region.DataSnapshot
39{
40 public class SnapshotStore
41 {
42 #region Class Members
43 private String m_directory = "unyuu"; //not an attempt at adding RM references to core SVN, honest
44 private Dictionary<Scene, bool> m_scenes = null;
45 private List<IDataSnapshotProvider> m_providers = null;
46 private log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
47 private Dictionary<String, String> m_gridinfo = null;
48 private bool m_cacheEnabled = true;
49 private string m_listener_port = "9000"; //TODO: Set default port over 9000
50 private string m_hostname = "127.0.0.1";
51 #endregion
52
53 public SnapshotStore(string directory, Dictionary<String, String> gridinfo, string port, string hostname) {
54 m_directory = directory;
55 m_scenes = new Dictionary<Scene, bool>();
56 m_providers = new List<IDataSnapshotProvider>();
57 m_gridinfo = gridinfo;
58 m_listener_port = port;
59 m_hostname = hostname;
60
61 if (Directory.Exists(m_directory))
62 {
63 m_log.Info("[DATASNAPSHOT]: Repsonse and fragment cache directory already exists.");
64 }
65 else
66 {
67 // Try to create the directory.
68 m_log.Info("[DATASNAPSHOT]: Creating directory " + m_directory);
69 try
70 {
71 Directory.CreateDirectory(m_directory);
72 }
73 catch (Exception e)
74 {
75 m_log.Error("[DATASNAPSHOT]: Failed to create directory " + m_directory, e);
76
77 //This isn't a horrible problem, just disable cacheing.
78 m_cacheEnabled = false;
79 m_log.Error("[DATASNAPSHOT]: Could not create directory, response cache has been disabled.");
80 }
81 }
82 }
83
84 public void ForceSceneStale(Scene scene) {
85 m_scenes[scene] = true;
86 }
87
88 #region Fragment storage
89 public XmlNode GetFragment(IDataSnapshotProvider provider, XmlDocument factory)
90 {
91 XmlNode data = null;
92
93 if (provider.Stale || !m_cacheEnabled)
94 {
95 data = provider.RequestSnapshotData(factory);
96
97 if (m_cacheEnabled)
98 {
99 String path = DataFileNameFragment(provider.GetParentScene, provider.Name);
100
101 using (XmlTextWriter snapXWriter = new XmlTextWriter(path, Encoding.Default))
102 {
103 snapXWriter.Formatting = Formatting.Indented;
104 snapXWriter.WriteStartDocument();
105 data.WriteTo(snapXWriter);
106 snapXWriter.WriteEndDocument();
107 }
108 }
109
110 //mark provider as not stale, parent scene as stale
111 provider.Stale = false;
112 m_scenes[provider.GetParentScene] = true;
113
114 m_log.Info("[DATASNAPSHOT]: Generated fragment response for provider type " + provider.Name);
115 }
116 else
117 {
118 String path = DataFileNameFragment(provider.GetParentScene, provider.Name);
119
120 XmlDocument fragDocument = new XmlDocument();
121 fragDocument.PreserveWhitespace = true;
122 fragDocument.Load(path);
123 foreach (XmlNode node in fragDocument)
124 {
125 data = factory.ImportNode(node, true);
126 }
127
128 m_log.Info("[DATASNAPSHOT]: Retrieved fragment response for provider type " + provider.Name);
129 }
130
131 return data;
132 }
133 #endregion
134
135 #region Response storage
136 public XmlNode GetScene(Scene scene, XmlDocument factory)
137 {
138 m_log.Debug("[DATASNAPSHOT]: Data requested for scene " + scene.RegionInfo.RegionName);
139
140 if (!m_scenes.ContainsKey(scene)) {
141 m_scenes.Add(scene, true); //stale by default
142 }
143
144 XmlNode regionElement = null;
145
146 if (!m_scenes[scene])
147 {
148 m_log.Info("[DATASNAPSHOT]: Attempting to retrieve snapshot from cache.");
149 //get snapshot from cache
150 String path = DataFileNameScene(scene);
151
152 XmlDocument fragDocument = new XmlDocument();
153 fragDocument.PreserveWhitespace = true;
154
155 fragDocument.Load(path);
156
157 foreach (XmlNode node in fragDocument)
158 {
159 regionElement = factory.ImportNode(node, true);
160 }
161
162 m_log.Info("[DATASNAPSHOT]: Obtained snapshot from cache for " + scene.RegionInfo.RegionName);
163 }
164 else
165 {
166 m_log.Info("[DATASNAPSHOT]: Attempting to generate snapshot.");
167 //make snapshot
168 regionElement = MakeRegionNode(scene, factory);
169
170 regionElement.AppendChild(GetGridSnapshotData(factory));
171 XmlNode regionData = factory.CreateNode(XmlNodeType.Element, "data", "");
172
173 foreach (IDataSnapshotProvider dataprovider in m_providers)
174 {
175 if (dataprovider.GetParentScene == scene)
176 {
177 regionData.AppendChild(GetFragment(dataprovider, factory));
178 }
179 }
180
181 regionElement.AppendChild(regionData);
182
183 factory.AppendChild(regionElement);
184
185 //save snapshot
186 String path = DataFileNameScene(scene);
187
188 using (XmlTextWriter snapXWriter = new XmlTextWriter(path, Encoding.Default))
189 {
190 snapXWriter.Formatting = Formatting.Indented;
191 snapXWriter.WriteStartDocument();
192 regionElement.WriteTo(snapXWriter);
193 snapXWriter.WriteEndDocument();
194 }
195
196 m_scenes[scene] = false;
197
198 m_log.Info("[DATASNAPSHOT]: Generated new snapshot for " + scene.RegionInfo.RegionName);
199 }
200
201 return regionElement;
202 }
203
204 #endregion
205
206 #region Helpers
207 private string DataFileNameFragment(Scene scene, String fragmentName)
208 {
209 return Path.Combine(m_directory, Path.ChangeExtension(scene.RegionInfo.RegionName + "_" + fragmentName, "xml"));
210 }
211
212 private string DataFileNameScene(Scene scene)
213 {
214 return Path.Combine(m_directory, Path.ChangeExtension(scene.RegionInfo.RegionName, "xml"));
215 //return (m_snapsDir + Path.DirectorySeparatorChar + scene.RegionInfo.RegionName + ".xml");
216 }
217
218 private XmlNode MakeRegionNode(Scene scene, XmlDocument basedoc)
219 {
220 XmlNode docElement = basedoc.CreateNode(XmlNodeType.Element, "region", "");
221
222 XmlAttribute attr = basedoc.CreateAttribute("category");
223 attr.Value = GetRegionCategory(scene);
224 docElement.Attributes.Append(attr);
225
226 attr = basedoc.CreateAttribute("entities");
227 attr.Value = scene.Entities.Count.ToString();
228 docElement.Attributes.Append(attr);
229
230 //attr = basedoc.CreateAttribute("parcels");
231 //attr.Value = scene.LandManager.landList.Count.ToString();
232 //docElement.Attributes.Append(attr);
233
234
235 XmlNode infoblock = basedoc.CreateNode(XmlNodeType.Element, "info", "");
236
237 XmlNode infopiece = basedoc.CreateNode(XmlNodeType.Element, "uuid", "");
238 infopiece.InnerText = scene.RegionInfo.RegionID.ToString();
239 infoblock.AppendChild(infopiece);
240
241 infopiece = basedoc.CreateNode(XmlNodeType.Element, "url", "");
242 infopiece.InnerText = "http://" + m_hostname + ":" + m_listener_port;
243 infoblock.AppendChild(infopiece);
244
245 infopiece = basedoc.CreateNode(XmlNodeType.Element, "name", "");
246 infopiece.InnerText = scene.RegionInfo.RegionName;
247 infoblock.AppendChild(infopiece);
248
249 docElement.AppendChild(infoblock);
250
251 m_log.Debug("[DATASNAPSHOT]: Generated region node");
252 return docElement;
253 }
254
255 private String GetRegionCategory(Scene scene)
256 {
257
258 //Boolean choice between:
259 // "PG" - Mormontown
260 // "Mature" - Sodom and Gomorrah
261 // (Depreciated) "Patriotic Nigra Testing Sandbox" - Abandon Hope All Ye Who Enter Here
262 if ((scene.RegionInfo.EstateSettings.simAccess & Simulator.SimAccess.Mature) == Simulator.SimAccess.Mature)
263 {
264 return "Mature";
265 }
266 else if ((scene.RegionInfo.EstateSettings.simAccess & Simulator.SimAccess.PG) == Simulator.SimAccess.PG)
267 {
268 return "PG";
269 }
270 else
271 {
272 return "Unknown";
273 }
274 }
275
276 private XmlNode GetGridSnapshotData(XmlDocument factory)
277 {
278 XmlNode griddata = factory.CreateNode(XmlNodeType.Element, "grid", "");
279
280 foreach (KeyValuePair<String, String> GridData in m_gridinfo)
281 {
282 //TODO: make it lowercase tag names for diva
283 XmlNode childnode = factory.CreateNode(XmlNodeType.Element, GridData.Key, "");
284 childnode.InnerText = GridData.Value;
285 griddata.AppendChild(childnode);
286 }
287
288 m_log.Debug("[DATASNAPSHOT]: Got grid snapshot data");
289
290 return griddata;
291 }
292 #endregion
293
294 #region Manage internal collections
295 public void AddScene(Scene newScene)
296 {
297 m_scenes.Add(newScene, true);
298 }
299
300 public void RemoveScene(Scene deadScene)
301 {
302 m_scenes.Remove(deadScene);
303 }
304
305 public void AddProvider(IDataSnapshotProvider newProvider)
306 {
307 m_providers.Add(newProvider);
308 }
309
310 public void RemoveProvider(IDataSnapshotProvider deadProvider)
311 {
312 m_providers.Remove(deadProvider);
313 }
314 #endregion
315 }
316}