diff options
Diffstat (limited to 'OpenSim/Region/OptionalModules/DataSnapshot/LandSnapshot.cs')
-rw-r--r-- | OpenSim/Region/OptionalModules/DataSnapshot/LandSnapshot.cs | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/OpenSim/Region/OptionalModules/DataSnapshot/LandSnapshot.cs b/OpenSim/Region/OptionalModules/DataSnapshot/LandSnapshot.cs new file mode 100644 index 0000000..b8c90cd --- /dev/null +++ b/OpenSim/Region/OptionalModules/DataSnapshot/LandSnapshot.cs | |||
@@ -0,0 +1,433 @@ | |||
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 OpenSimulator 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 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using System.Xml; | ||
32 | using log4net; | ||
33 | using OpenMetaverse; | ||
34 | using OpenSim.Framework; | ||
35 | |||
36 | using OpenSim.Region.CoreModules.World.Land; | ||
37 | using OpenSim.Region.DataSnapshot.Interfaces; | ||
38 | using OpenSim.Region.Framework.Interfaces; | ||
39 | using OpenSim.Region.Framework.Scenes; | ||
40 | using OpenSim.Services.Interfaces; | ||
41 | |||
42 | namespace OpenSim.Region.DataSnapshot.Providers | ||
43 | { | ||
44 | public class LandSnapshot : IDataSnapshotProvider | ||
45 | { | ||
46 | private Scene m_scene = null; | ||
47 | private DataSnapshotManager m_parent = null; | ||
48 | //private Dictionary<int, Land> m_landIndexed = new Dictionary<int, Land>(); | ||
49 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
50 | private bool m_stale = true; | ||
51 | |||
52 | #region Dead code | ||
53 | |||
54 | /* | ||
55 | * David, I don't think we need this at all. When we do the snapshot, we can | ||
56 | * simply look into the parcels that are marked for ShowDirectory -- see | ||
57 | * conditional in RequestSnapshotData | ||
58 | * | ||
59 | //Revise this, look for more direct way of checking for change in land | ||
60 | #region Client hooks | ||
61 | |||
62 | public void OnNewClient(IClientAPI client) | ||
63 | { | ||
64 | //Land hooks | ||
65 | client.OnParcelDivideRequest += ParcelSplitHook; | ||
66 | client.OnParcelJoinRequest += ParcelSplitHook; | ||
67 | client.OnParcelPropertiesUpdateRequest += ParcelPropsHook; | ||
68 | } | ||
69 | |||
70 | public void ParcelSplitHook(int west, int south, int east, int north, IClientAPI remote_client) | ||
71 | { | ||
72 | PrepareData(); | ||
73 | } | ||
74 | |||
75 | public void ParcelPropsHook(ParcelPropertiesUpdatePacket packet, IClientAPI remote_client) | ||
76 | { | ||
77 | PrepareData(); | ||
78 | } | ||
79 | |||
80 | #endregion | ||
81 | |||
82 | public void PrepareData() | ||
83 | { | ||
84 | m_log.Info("[EXTERNALDATA]: Generating land data."); | ||
85 | |||
86 | m_landIndexed.Clear(); | ||
87 | |||
88 | //Index sim land | ||
89 | foreach (KeyValuePair<int, Land> curLand in m_scene.LandManager.landList) | ||
90 | { | ||
91 | //if ((curLand.Value.LandData.landFlags & (uint)ParcelFlags.ShowDirectory) == (uint)ParcelFlags.ShowDirectory) | ||
92 | //{ | ||
93 | m_landIndexed.Add(curLand.Key, curLand.Value.Copy()); | ||
94 | //} | ||
95 | } | ||
96 | } | ||
97 | |||
98 | public Dictionary<int,Land> IndexedLand { | ||
99 | get { return m_landIndexed; } | ||
100 | } | ||
101 | */ | ||
102 | |||
103 | #endregion | ||
104 | |||
105 | #region IDataSnapshotProvider members | ||
106 | |||
107 | public void Initialize(Scene scene, DataSnapshotManager parent) | ||
108 | { | ||
109 | m_scene = scene; | ||
110 | m_parent = parent; | ||
111 | |||
112 | //Brought back from the dead for staleness checks. | ||
113 | m_scene.EventManager.OnNewClient += OnNewClient; | ||
114 | } | ||
115 | |||
116 | public Scene GetParentScene | ||
117 | { | ||
118 | get { return m_scene; } | ||
119 | } | ||
120 | |||
121 | public XmlNode RequestSnapshotData(XmlDocument nodeFactory) | ||
122 | { | ||
123 | ILandChannel landChannel = m_scene.LandChannel; | ||
124 | List<ILandObject> parcels = landChannel.AllParcels(); | ||
125 | |||
126 | IDwellModule dwellModule = m_scene.RequestModuleInterface<IDwellModule>(); | ||
127 | |||
128 | XmlNode parent = nodeFactory.CreateNode(XmlNodeType.Element, "parceldata", ""); | ||
129 | if (parcels != null) | ||
130 | { | ||
131 | |||
132 | //foreach (KeyValuePair<int, Land> curParcel in m_landIndexed) | ||
133 | foreach (ILandObject parcel_interface in parcels) | ||
134 | { | ||
135 | // Play it safe | ||
136 | if (!(parcel_interface is LandObject)) | ||
137 | continue; | ||
138 | |||
139 | LandObject land = (LandObject)parcel_interface; | ||
140 | |||
141 | LandData parcel = land.LandData; | ||
142 | if (m_parent.ExposureLevel.Equals("all") || | ||
143 | (m_parent.ExposureLevel.Equals("minimum") && | ||
144 | (parcel.Flags & (uint)ParcelFlags.ShowDirectory) == (uint)ParcelFlags.ShowDirectory)) | ||
145 | { | ||
146 | |||
147 | //TODO: make better method of marshalling data from LandData to XmlNode | ||
148 | XmlNode xmlparcel = nodeFactory.CreateNode(XmlNodeType.Element, "parcel", ""); | ||
149 | |||
150 | // Attributes of the parcel node | ||
151 | XmlAttribute scripts_attr = nodeFactory.CreateAttribute("scripts"); | ||
152 | scripts_attr.Value = GetScriptsPermissions(parcel); | ||
153 | XmlAttribute build_attr = nodeFactory.CreateAttribute("build"); | ||
154 | build_attr.Value = GetBuildPermissions(parcel); | ||
155 | XmlAttribute public_attr = nodeFactory.CreateAttribute("public"); | ||
156 | public_attr.Value = GetPublicPermissions(parcel); | ||
157 | // Check the category of the Parcel | ||
158 | XmlAttribute category_attr = nodeFactory.CreateAttribute("category"); | ||
159 | category_attr.Value = ((int)parcel.Category).ToString(); | ||
160 | // Check if the parcel is for sale | ||
161 | XmlAttribute forsale_attr = nodeFactory.CreateAttribute("forsale"); | ||
162 | forsale_attr.Value = CheckForSale(parcel); | ||
163 | XmlAttribute sales_attr = nodeFactory.CreateAttribute("salesprice"); | ||
164 | sales_attr.Value = parcel.SalePrice.ToString(); | ||
165 | |||
166 | XmlAttribute directory_attr = nodeFactory.CreateAttribute("showinsearch"); | ||
167 | directory_attr.Value = GetShowInSearch(parcel); | ||
168 | //XmlAttribute entities_attr = nodeFactory.CreateAttribute("entities"); | ||
169 | //entities_attr.Value = land.primsOverMe.Count.ToString(); | ||
170 | xmlparcel.Attributes.Append(directory_attr); | ||
171 | xmlparcel.Attributes.Append(scripts_attr); | ||
172 | xmlparcel.Attributes.Append(build_attr); | ||
173 | xmlparcel.Attributes.Append(public_attr); | ||
174 | xmlparcel.Attributes.Append(category_attr); | ||
175 | xmlparcel.Attributes.Append(forsale_attr); | ||
176 | xmlparcel.Attributes.Append(sales_attr); | ||
177 | //xmlparcel.Attributes.Append(entities_attr); | ||
178 | |||
179 | |||
180 | //name, description, area, and UUID | ||
181 | XmlNode name = nodeFactory.CreateNode(XmlNodeType.Element, "name", ""); | ||
182 | name.InnerText = parcel.Name; | ||
183 | xmlparcel.AppendChild(name); | ||
184 | |||
185 | XmlNode desc = nodeFactory.CreateNode(XmlNodeType.Element, "description", ""); | ||
186 | desc.InnerText = parcel.Description; | ||
187 | xmlparcel.AppendChild(desc); | ||
188 | |||
189 | XmlNode uuid = nodeFactory.CreateNode(XmlNodeType.Element, "uuid", ""); | ||
190 | uuid.InnerText = parcel.GlobalID.ToString(); | ||
191 | xmlparcel.AppendChild(uuid); | ||
192 | |||
193 | XmlNode area = nodeFactory.CreateNode(XmlNodeType.Element, "area", ""); | ||
194 | area.InnerText = parcel.Area.ToString(); | ||
195 | xmlparcel.AppendChild(area); | ||
196 | |||
197 | //default location | ||
198 | XmlNode tpLocation = nodeFactory.CreateNode(XmlNodeType.Element, "location", ""); | ||
199 | Vector3 loc = parcel.UserLocation; | ||
200 | if (loc.Equals(Vector3.Zero)) // This test is moot at this point: the location is wrong by default | ||
201 | loc = new Vector3((parcel.AABBMax.X + parcel.AABBMin.X) / 2, (parcel.AABBMax.Y + parcel.AABBMin.Y) / 2, (parcel.AABBMax.Z + parcel.AABBMin.Z) / 2); | ||
202 | tpLocation.InnerText = loc.X.ToString() + "/" + loc.Y.ToString() + "/" + loc.Z.ToString(); | ||
203 | xmlparcel.AppendChild(tpLocation); | ||
204 | |||
205 | XmlNode infouuid = nodeFactory.CreateNode(XmlNodeType.Element, "infouuid", ""); | ||
206 | uint x = (uint)loc.X, y = (uint)loc.Y; | ||
207 | findPointInParcel(land, ref x, ref y); // find a suitable spot | ||
208 | infouuid.InnerText = Util.BuildFakeParcelID( | ||
209 | m_scene.RegionInfo.RegionHandle, x, y).ToString(); | ||
210 | xmlparcel.AppendChild(infouuid); | ||
211 | |||
212 | XmlNode dwell = nodeFactory.CreateNode(XmlNodeType.Element, "dwell", ""); | ||
213 | if (dwellModule != null) | ||
214 | dwell.InnerText = dwellModule.GetDwell(parcel.GlobalID).ToString(); | ||
215 | else | ||
216 | dwell.InnerText = "0"; | ||
217 | xmlparcel.AppendChild(dwell); | ||
218 | |||
219 | //TODO: figure how to figure out teleport system landData.landingType | ||
220 | |||
221 | //land texture snapshot uuid | ||
222 | if (parcel.SnapshotID != UUID.Zero) | ||
223 | { | ||
224 | XmlNode textureuuid = nodeFactory.CreateNode(XmlNodeType.Element, "image", ""); | ||
225 | textureuuid.InnerText = parcel.SnapshotID.ToString(); | ||
226 | xmlparcel.AppendChild(textureuuid); | ||
227 | } | ||
228 | |||
229 | string groupName = String.Empty; | ||
230 | |||
231 | //attached user and group | ||
232 | if (parcel.GroupID != UUID.Zero) | ||
233 | { | ||
234 | XmlNode groupblock = nodeFactory.CreateNode(XmlNodeType.Element, "group", ""); | ||
235 | XmlNode groupuuid = nodeFactory.CreateNode(XmlNodeType.Element, "groupuuid", ""); | ||
236 | groupuuid.InnerText = parcel.GroupID.ToString(); | ||
237 | groupblock.AppendChild(groupuuid); | ||
238 | |||
239 | IGroupsModule gm = m_scene.RequestModuleInterface<IGroupsModule>(); | ||
240 | if (gm != null) | ||
241 | { | ||
242 | GroupRecord g = gm.GetGroupRecord(parcel.GroupID); | ||
243 | if (g != null) | ||
244 | groupName = g.GroupName; | ||
245 | } | ||
246 | |||
247 | XmlNode groupname = nodeFactory.CreateNode(XmlNodeType.Element, "groupname", ""); | ||
248 | groupname.InnerText = groupName; | ||
249 | groupblock.AppendChild(groupname); | ||
250 | |||
251 | xmlparcel.AppendChild(groupblock); | ||
252 | } | ||
253 | |||
254 | XmlNode userblock = nodeFactory.CreateNode(XmlNodeType.Element, "owner", ""); | ||
255 | |||
256 | UUID userOwnerUUID = parcel.OwnerID; | ||
257 | |||
258 | XmlNode useruuid = nodeFactory.CreateNode(XmlNodeType.Element, "uuid", ""); | ||
259 | useruuid.InnerText = userOwnerUUID.ToString(); | ||
260 | userblock.AppendChild(useruuid); | ||
261 | |||
262 | if (!parcel.IsGroupOwned) | ||
263 | { | ||
264 | try | ||
265 | { | ||
266 | XmlNode username = nodeFactory.CreateNode(XmlNodeType.Element, "name", ""); | ||
267 | UserAccount account = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, userOwnerUUID); | ||
268 | username.InnerText = account.FirstName + " " + account.LastName; | ||
269 | userblock.AppendChild(username); | ||
270 | } | ||
271 | catch (Exception) | ||
272 | { | ||
273 | //m_log.Info("[DATASNAPSHOT]: Cannot find owner name; ignoring this parcel"); | ||
274 | } | ||
275 | |||
276 | } | ||
277 | else | ||
278 | { | ||
279 | XmlNode username = nodeFactory.CreateNode(XmlNodeType.Element, "name", ""); | ||
280 | username.InnerText = groupName; | ||
281 | userblock.AppendChild(username); | ||
282 | } | ||
283 | |||
284 | xmlparcel.AppendChild(userblock); | ||
285 | |||
286 | parent.AppendChild(xmlparcel); | ||
287 | } | ||
288 | } | ||
289 | //snap.AppendChild(parent); | ||
290 | } | ||
291 | |||
292 | this.Stale = false; | ||
293 | return parent; | ||
294 | } | ||
295 | |||
296 | public String Name | ||
297 | { | ||
298 | get { return "LandSnapshot"; } | ||
299 | } | ||
300 | |||
301 | public bool Stale | ||
302 | { | ||
303 | get | ||
304 | { | ||
305 | return m_stale; | ||
306 | } | ||
307 | set | ||
308 | { | ||
309 | m_stale = value; | ||
310 | |||
311 | if (m_stale) | ||
312 | OnStale(this); | ||
313 | } | ||
314 | } | ||
315 | |||
316 | public event ProviderStale OnStale; | ||
317 | |||
318 | #endregion | ||
319 | |||
320 | #region Helper functions | ||
321 | |||
322 | private string GetScriptsPermissions(LandData parcel) | ||
323 | { | ||
324 | if ((parcel.Flags & (uint)ParcelFlags.AllowOtherScripts) == (uint)ParcelFlags.AllowOtherScripts) | ||
325 | return "true"; | ||
326 | else | ||
327 | return "false"; | ||
328 | |||
329 | } | ||
330 | |||
331 | private string GetPublicPermissions(LandData parcel) | ||
332 | { | ||
333 | if ((parcel.Flags & (uint)ParcelFlags.UseAccessList) == (uint)ParcelFlags.UseAccessList) | ||
334 | return "false"; | ||
335 | else | ||
336 | return "true"; | ||
337 | |||
338 | } | ||
339 | |||
340 | private string GetBuildPermissions(LandData parcel) | ||
341 | { | ||
342 | if ((parcel.Flags & (uint)ParcelFlags.CreateObjects) == (uint)ParcelFlags.CreateObjects) | ||
343 | return "true"; | ||
344 | else | ||
345 | return "false"; | ||
346 | |||
347 | } | ||
348 | |||
349 | private string CheckForSale(LandData parcel) | ||
350 | { | ||
351 | if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale) | ||
352 | return "true"; | ||
353 | else | ||
354 | return "false"; | ||
355 | } | ||
356 | |||
357 | private string GetShowInSearch(LandData parcel) | ||
358 | { | ||
359 | if ((parcel.Flags & (uint)ParcelFlags.ShowDirectory) == (uint)ParcelFlags.ShowDirectory) | ||
360 | return "true"; | ||
361 | else | ||
362 | return "false"; | ||
363 | |||
364 | } | ||
365 | |||
366 | #endregion | ||
367 | |||
368 | #region Change detection hooks | ||
369 | |||
370 | public void OnNewClient(IClientAPI client) | ||
371 | { | ||
372 | //Land hooks | ||
373 | client.OnParcelDivideRequest += delegate(int west, int south, int east, int north, | ||
374 | IClientAPI remote_client) { this.Stale = true; }; | ||
375 | client.OnParcelJoinRequest += delegate(int west, int south, int east, int north, | ||
376 | IClientAPI remote_client) { this.Stale = true; }; | ||
377 | client.OnParcelPropertiesUpdateRequest += delegate(LandUpdateArgs args, int local_id, | ||
378 | IClientAPI remote_client) { this.Stale = true; }; | ||
379 | client.OnParcelBuy += delegate(UUID agentId, UUID groupId, bool final, bool groupOwned, | ||
380 | bool removeContribution, int parcelLocalID, int parcelArea, int parcelPrice, bool authenticated) | ||
381 | { this.Stale = true; }; | ||
382 | } | ||
383 | |||
384 | public void ParcelSplitHook(int west, int south, int east, int north, IClientAPI remote_client) | ||
385 | { | ||
386 | this.Stale = true; | ||
387 | } | ||
388 | |||
389 | public void ParcelPropsHook(LandUpdateArgs args, int local_id, IClientAPI remote_client) | ||
390 | { | ||
391 | this.Stale = true; | ||
392 | } | ||
393 | |||
394 | #endregion | ||
395 | |||
396 | // this is needed for non-convex parcels (example: rectangular parcel, and in the exact center | ||
397 | // another, smaller rectangular parcel). Both will have the same initial coordinates. | ||
398 | private void findPointInParcel(ILandObject land, ref uint refX, ref uint refY) | ||
399 | { | ||
400 | m_log.DebugFormat("[DATASNAPSHOT] trying {0}, {1}", refX, refY); | ||
401 | // the point we started with already is in the parcel | ||
402 | if (land.ContainsPoint((int)refX, (int)refY)) return; | ||
403 | |||
404 | // ... otherwise, we have to search for a point within the parcel | ||
405 | uint startX = (uint)land.LandData.AABBMin.X; | ||
406 | uint startY = (uint)land.LandData.AABBMin.Y; | ||
407 | uint endX = (uint)land.LandData.AABBMax.X; | ||
408 | uint endY = (uint)land.LandData.AABBMax.Y; | ||
409 | |||
410 | // default: center of the parcel | ||
411 | refX = (startX + endX) / 2; | ||
412 | refY = (startY + endY) / 2; | ||
413 | // If the center point is within the parcel, take that one | ||
414 | if (land.ContainsPoint((int)refX, (int)refY)) return; | ||
415 | |||
416 | // otherwise, go the long way. | ||
417 | for (uint y = startY; y <= endY; ++y) | ||
418 | { | ||
419 | for (uint x = startX; x <= endX; ++x) | ||
420 | { | ||
421 | if (land.ContainsPoint((int)x, (int)y)) | ||
422 | { | ||
423 | // found a point | ||
424 | refX = x; | ||
425 | refY = y; | ||
426 | return; | ||
427 | } | ||
428 | } | ||
429 | } | ||
430 | } | ||
431 | } | ||
432 | } | ||
433 | |||