opensim – Blame information for rev 3
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | eva | 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 |