aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Environment')
-rw-r--r--OpenSim/Region/Environment/LandManagement/Land.cs599
-rw-r--r--OpenSim/Region/Environment/LandManagement/LandManager.cs617
2 files changed, 1216 insertions, 0 deletions
diff --git a/OpenSim/Region/Environment/LandManagement/Land.cs b/OpenSim/Region/Environment/LandManagement/Land.cs
new file mode 100644
index 0000000..b333f36
--- /dev/null
+++ b/OpenSim/Region/Environment/LandManagement/Land.cs
@@ -0,0 +1,599 @@
1using System;
2using System.Collections.Generic;
3using libsecondlife;
4using libsecondlife.Packets;
5using OpenSim.Framework.Interfaces;
6using OpenSim.Framework.Types;
7using OpenSim.Region.Environment.Scenes;
8
9namespace OpenSim.Region.Environment.LandManagement
10{
11 #region Parcel Class
12 /// <summary>
13 /// Keeps track of a specific piece of land's information
14 /// </summary>
15 public class Land
16 {
17 #region Member Variables
18 public LandData landData = new LandData();
19 public List<SceneObject> primsOverMe = new List<SceneObject>();
20
21 public Scene m_world;
22
23 private bool[,] landBitmap = new bool[64, 64];
24
25 #endregion
26
27
28 #region Constructors
29 public Land(LLUUID owner_id, bool is_group_owned, Scene world)
30 {
31 m_world = world;
32 landData.ownerID = owner_id;
33 landData.isGroupOwned = is_group_owned;
34
35 }
36 #endregion
37
38
39 #region Member Functions
40
41 #region General Functions
42 /// <summary>
43 /// Checks to see if this land object contains a point
44 /// </summary>
45 /// <param name="x"></param>
46 /// <param name="y"></param>
47 /// <returns>Returns true if the piece of land contains the specified point</returns>
48 public bool containsPoint(int x, int y)
49 {
50 if (x >= 0 && y >= 0 && x <= 256 && x <= 256)
51 {
52 return (landBitmap[x / 4, y / 4] == true);
53 }
54 else
55 {
56 return false;
57 }
58 }
59
60 public Land Copy()
61 {
62 Land newLand = new Land(this.landData.ownerID, this.landData.isGroupOwned, m_world);
63
64 //Place all new variables here!
65 newLand.landBitmap = (bool[,])(this.landBitmap.Clone());
66 newLand.landData = landData.Copy();
67
68 return newLand;
69 }
70
71 #endregion
72
73
74 #region Packet Request Handling
75 /// <summary>
76 /// Sends land properties as requested
77 /// </summary>
78 /// <param name="sequence_id">ID sent by client for them to keep track of</param>
79 /// <param name="snap_selection">Bool sent by client for them to use</param>
80 /// <param name="remote_client">Object representing the client</param>
81 public void sendLandProperties(int sequence_id, bool snap_selection, int request_result, IClientAPI remote_client)
82 {
83
84 ParcelPropertiesPacket updatePacket = new ParcelPropertiesPacket();
85 updatePacket.ParcelData.AABBMax = landData.AABBMax;
86 updatePacket.ParcelData.AABBMin = landData.AABBMin;
87 updatePacket.ParcelData.Area = landData.area;
88 updatePacket.ParcelData.AuctionID = landData.auctionID;
89 updatePacket.ParcelData.AuthBuyerID = landData.authBuyerID; //unemplemented
90
91 updatePacket.ParcelData.Bitmap = landData.landBitmapByteArray;
92
93 updatePacket.ParcelData.Desc = Helpers.StringToField(landData.landDesc);
94 updatePacket.ParcelData.Category = (byte)landData.category;
95 updatePacket.ParcelData.ClaimDate = landData.claimDate;
96 updatePacket.ParcelData.ClaimPrice = landData.claimPrice;
97 updatePacket.ParcelData.GroupID = landData.groupID;
98 updatePacket.ParcelData.GroupPrims = landData.groupPrims;
99 updatePacket.ParcelData.IsGroupOwned = landData.isGroupOwned;
100 updatePacket.ParcelData.LandingType = (byte)landData.landingType;
101 updatePacket.ParcelData.LocalID = landData.localID;
102 if (landData.area > 0)
103 {
104 updatePacket.ParcelData.MaxPrims = Convert.ToInt32(Math.Round((Convert.ToDecimal(landData.area) / Convert.ToDecimal(65536)) * 15000 * Convert.ToDecimal(m_world.RegionInfo.estateSettings.objectBonusFactor)));
105 }
106 else
107 {
108 updatePacket.ParcelData.MaxPrims = 0;
109 }
110 updatePacket.ParcelData.MediaAutoScale = landData.mediaAutoScale;
111 updatePacket.ParcelData.MediaID = landData.mediaID;
112 updatePacket.ParcelData.MediaURL = Helpers.StringToField(landData.mediaURL);
113 updatePacket.ParcelData.MusicURL = Helpers.StringToField(landData.musicURL);
114 updatePacket.ParcelData.Name = Helpers.StringToField(landData.landName);
115 updatePacket.ParcelData.OtherCleanTime = 0; //unemplemented
116 updatePacket.ParcelData.OtherCount = 0; //unemplemented
117 updatePacket.ParcelData.OtherPrims = landData.otherPrims;
118 updatePacket.ParcelData.OwnerID = landData.ownerID;
119 updatePacket.ParcelData.OwnerPrims = landData.ownerPrims;
120 updatePacket.ParcelData.ParcelFlags = landData.landFlags;
121 updatePacket.ParcelData.ParcelPrimBonus = m_world.RegionInfo.estateSettings.objectBonusFactor;
122 updatePacket.ParcelData.PassHours = landData.passHours;
123 updatePacket.ParcelData.PassPrice = landData.passPrice;
124 updatePacket.ParcelData.PublicCount = 0; //unemplemented
125 updatePacket.ParcelData.RegionDenyAnonymous = (((uint)m_world.RegionInfo.estateSettings.regionFlags & (uint)Simulator.RegionFlags.DenyAnonymous) > 0);
126 updatePacket.ParcelData.RegionDenyIdentified = (((uint)m_world.RegionInfo.estateSettings.regionFlags & (uint)Simulator.RegionFlags.DenyIdentified) > 0);
127 updatePacket.ParcelData.RegionDenyTransacted = (((uint)m_world.RegionInfo.estateSettings.regionFlags & (uint)Simulator.RegionFlags.DenyTransacted) > 0);
128 updatePacket.ParcelData.RegionPushOverride = (((uint)m_world.RegionInfo.estateSettings.regionFlags & (uint)Simulator.RegionFlags.RestrictPushObject) > 0);
129 updatePacket.ParcelData.RentPrice = 0;
130 updatePacket.ParcelData.RequestResult = request_result;
131 updatePacket.ParcelData.SalePrice = landData.salePrice;
132 updatePacket.ParcelData.SelectedPrims = landData.selectedPrims;
133 updatePacket.ParcelData.SelfCount = 0;//unemplemented
134 updatePacket.ParcelData.SequenceID = sequence_id;
135 if (landData.simwideArea > 0)
136 {
137 updatePacket.ParcelData.SimWideMaxPrims = Convert.ToInt32(Math.Round((Convert.ToDecimal(landData.simwideArea) / Convert.ToDecimal(65536)) * 15000 * Convert.ToDecimal(m_world.RegionInfo.estateSettings.objectBonusFactor)));
138 }
139 else
140 {
141 updatePacket.ParcelData.SimWideMaxPrims = 0;
142 }
143 updatePacket.ParcelData.SimWideTotalPrims = landData.simwidePrims;
144 updatePacket.ParcelData.SnapSelection = snap_selection;
145 updatePacket.ParcelData.SnapshotID = landData.snapshotID;
146 updatePacket.ParcelData.Status = (byte)landData.landStatus;
147 updatePacket.ParcelData.TotalPrims = landData.ownerPrims + landData.groupPrims + landData.otherPrims + landData.selectedPrims;
148 updatePacket.ParcelData.UserLocation = landData.userLocation;
149 updatePacket.ParcelData.UserLookAt = landData.userLookAt;
150 remote_client.OutPacket((Packet)updatePacket);
151 }
152
153 public void updateLandProperties(ParcelPropertiesUpdatePacket packet, IClientAPI remote_client)
154 {
155 if (remote_client.AgentId == landData.ownerID)
156 {
157 //Needs later group support
158 landData.authBuyerID = packet.ParcelData.AuthBuyerID;
159 landData.category = (libsecondlife.Parcel.ParcelCategory)packet.ParcelData.Category;
160 landData.landDesc = Helpers.FieldToUTF8String(packet.ParcelData.Desc);
161 landData.groupID = packet.ParcelData.GroupID;
162 landData.landingType = packet.ParcelData.LandingType;
163 landData.mediaAutoScale = packet.ParcelData.MediaAutoScale;
164 landData.mediaID = packet.ParcelData.MediaID;
165 landData.mediaURL = Helpers.FieldToUTF8String(packet.ParcelData.MediaURL);
166 landData.musicURL = Helpers.FieldToUTF8String(packet.ParcelData.MusicURL);
167 landData.landName = Helpers.FieldToUTF8String(packet.ParcelData.Name);
168 landData.landFlags = packet.ParcelData.ParcelFlags;
169 landData.passHours = packet.ParcelData.PassHours;
170 landData.passPrice = packet.ParcelData.PassPrice;
171 landData.salePrice = packet.ParcelData.SalePrice;
172 landData.snapshotID = packet.ParcelData.SnapshotID;
173 landData.userLocation = packet.ParcelData.UserLocation;
174 landData.userLookAt = packet.ParcelData.UserLookAt;
175 sendLandUpdateToAvatarsOverMe();
176
177
178 }
179 }
180
181 public void sendLandUpdateToAvatarsOverMe()
182 {
183 List<ScenePresence> avatars = m_world.RequestAvatarList();
184 for (int i = 0; i < avatars.Count; i++)
185 {
186 Land over = m_world.LandManager.getLandObject((int)Math.Round(avatars[i].Pos.X), (int)Math.Round(avatars[i].Pos.Y));
187 if (over.landData.localID == this.landData.localID)
188 {
189 sendLandProperties(0, false, 0, avatars[i].ControllingClient);
190 }
191 }
192 }
193 #endregion
194
195
196 #region Update Functions
197 /// <summary>
198 /// Updates the AABBMin and AABBMax values after area/shape modification of the land object
199 /// </summary>
200 private void updateAABBAndAreaValues()
201 {
202 int min_x = 64;
203 int min_y = 64;
204 int max_x = 0;
205 int max_y = 0;
206 int tempArea = 0;
207 int x, y;
208 for (x = 0; x < 64; x++)
209 {
210 for (y = 0; y < 64; y++)
211 {
212 if (landBitmap[x, y] == true)
213 {
214 if (min_x > x) min_x = x;
215 if (min_y > y) min_y = y;
216 if (max_x < x) max_x = x;
217 if (max_y < y) max_y = y;
218 tempArea += 16; //16sqm peice of land
219 }
220 }
221 }
222 landData.AABBMin = new LLVector3((float)(min_x * 4), (float)(min_y * 4), (float)m_world.Terrain.get((min_x * 4), (min_y * 4)));
223 landData.AABBMax = new LLVector3((float)(max_x * 4), (float)(max_y * 4), (float)m_world.Terrain.get((max_x * 4), (max_y * 4)));
224 landData.area = tempArea;
225 }
226
227 public void updateLandBitmapByteArray()
228 {
229 landData.landBitmapByteArray = convertLandBitmapToBytes();
230 }
231
232 /// <summary>
233 /// Update all settings in land such as area, bitmap byte array, etc
234 /// </summary>
235 public void forceUpdateLandInfo()
236 {
237 this.updateAABBAndAreaValues();
238 this.updateLandBitmapByteArray();
239 }
240
241 public void setLandBitmapFromByteArray()
242 {
243 landBitmap = convertBytesToLandBitmap();
244 }
245 #endregion
246
247
248 #region Land Bitmap Functions
249 /// <summary>
250 /// Sets the land's bitmap manually
251 /// </summary>
252 /// <param name="bitmap">64x64 block representing where this land is on a map</param>
253 public void setLandBitmap(bool[,] bitmap)
254 {
255 if (bitmap.GetLength(0) != 64 || bitmap.GetLength(1) != 64 || bitmap.Rank != 2)
256 {
257 //Throw an exception - The bitmap is not 64x64
258 throw new Exception("Error: Invalid Parcel Bitmap");
259 }
260 else
261 {
262 //Valid: Lets set it
263 landBitmap = bitmap;
264 forceUpdateLandInfo();
265
266 }
267 }
268 /// <summary>
269 /// Gets the land's bitmap manually
270 /// </summary>
271 /// <returns></returns>
272 public bool[,] getLandBitmap()
273 {
274 return landBitmap;
275 }
276 /// <summary>
277 /// Converts the land bitmap to a packet friendly byte array
278 /// </summary>
279 /// <returns></returns>
280 private byte[] convertLandBitmapToBytes()
281 {
282 byte[] tempConvertArr = new byte[512];
283 byte tempByte = 0;
284 int x, y, i, byteNum = 0;
285 i = 0;
286 for (y = 0; y < 64; y++)
287 {
288 for (x = 0; x < 64; x++)
289 {
290 tempByte = Convert.ToByte(tempByte | Convert.ToByte(landBitmap[x, y]) << (i++ % 8));
291 if (i % 8 == 0)
292 {
293 tempConvertArr[byteNum] = tempByte;
294 tempByte = (byte)0;
295 i = 0;
296 byteNum++;
297 }
298 }
299 }
300 return tempConvertArr;
301 }
302
303 private bool[,] convertBytesToLandBitmap()
304 {
305 bool[,] tempConvertMap = new bool[64, 64];
306 tempConvertMap.Initialize();
307 byte tempByte = 0;
308 int x = 0, y = 0, i = 0, bitNum = 0;
309 for (i = 0; i < 512; i++)
310 {
311 tempByte = landData.landBitmapByteArray[i];
312 for (bitNum = 0; bitNum < 8; bitNum++)
313 {
314 bool bit = Convert.ToBoolean(Convert.ToByte(tempByte >> bitNum) & (byte)1);
315 tempConvertMap[x, y] = bit;
316 x++;
317 if (x > 63)
318 {
319 x = 0;
320 y++;
321 }
322
323 }
324
325 }
326 return tempConvertMap;
327 }
328 /// <summary>
329 /// Full sim land object creation
330 /// </summary>
331 /// <returns></returns>
332 public static bool[,] basicFullRegionLandBitmap()
333 {
334 return getSquareLandBitmap(0, 0, 256, 256);
335 }
336
337 /// <summary>
338 /// Used to modify the bitmap between the x and y points. Points use 64 scale
339 /// </summary>
340 /// <param name="start_x"></param>
341 /// <param name="start_y"></param>
342 /// <param name="end_x"></param>
343 /// <param name="end_y"></param>
344 /// <returns></returns>
345 public static bool[,] getSquareLandBitmap(int start_x, int start_y, int end_x, int end_y)
346 {
347
348 bool[,] tempBitmap = new bool[64, 64];
349 tempBitmap.Initialize();
350
351 tempBitmap = modifyLandBitmapSquare(tempBitmap, start_x, start_y, end_x, end_y, true);
352 return tempBitmap;
353 }
354
355 /// <summary>
356 /// Change a land bitmap at within a square and set those points to a specific value
357 /// </summary>
358 /// <param name="land_bitmap"></param>
359 /// <param name="start_x"></param>
360 /// <param name="start_y"></param>
361 /// <param name="end_x"></param>
362 /// <param name="end_y"></param>
363 /// <param name="set_value"></param>
364 /// <returns></returns>
365 public static bool[,] modifyLandBitmapSquare(bool[,] land_bitmap, int start_x, int start_y, int end_x, int end_y, bool set_value)
366 {
367 if (land_bitmap.GetLength(0) != 64 || land_bitmap.GetLength(1) != 64 || land_bitmap.Rank != 2)
368 {
369 //Throw an exception - The bitmap is not 64x64
370 throw new Exception("Error: Invalid Parcel Bitmap in modifyLandBitmapSquare()");
371 }
372
373 int x, y;
374 for (y = 0; y < 64; y++)
375 {
376 for (x = 0; x < 64; x++)
377 {
378 if (x >= start_x / 4 && x < end_x / 4
379 && y >= start_y / 4 && y < end_y / 4)
380 {
381 land_bitmap[x, y] = set_value;
382 }
383 }
384 }
385 return land_bitmap;
386 }
387 /// <summary>
388 /// Join the true values of 2 bitmaps together
389 /// </summary>
390 /// <param name="bitmap_base"></param>
391 /// <param name="bitmap_add"></param>
392 /// <returns></returns>
393 public static bool[,] mergeLandBitmaps(bool[,] bitmap_base, bool[,] bitmap_add)
394 {
395 if (bitmap_base.GetLength(0) != 64 || bitmap_base.GetLength(1) != 64 || bitmap_base.Rank != 2)
396 {
397 //Throw an exception - The bitmap is not 64x64
398 throw new Exception("Error: Invalid Parcel Bitmap - Bitmap_base in mergeLandBitmaps");
399 }
400 if (bitmap_add.GetLength(0) != 64 || bitmap_add.GetLength(1) != 64 || bitmap_add.Rank != 2)
401 {
402 //Throw an exception - The bitmap is not 64x64
403 throw new Exception("Error: Invalid Parcel Bitmap - Bitmap_add in mergeLandBitmaps");
404
405 }
406
407 int x, y;
408 for (y = 0; y < 64; y++)
409 {
410 for (x = 0; x < 64; x++)
411 {
412 if (bitmap_add[x, y])
413 {
414 bitmap_base[x, y] = true;
415 }
416 }
417 }
418 return bitmap_base;
419 }
420 #endregion
421
422 #region Object Select and Object Owner Listing
423 public void sendForceObjectSelect(int local_id, int request_type, IClientAPI remote_client)
424 {
425 List<uint> resultLocalIDs = new List<uint>();
426 foreach (SceneObject obj in primsOverMe)
427 {
428 if (obj.rootLocalID > 0)
429 {
430 if (request_type == LandManager.LAND_SELECT_OBJECTS_OWNER && obj.rootPrimitive.OwnerID == this.landData.ownerID)
431 {
432 resultLocalIDs.Add(obj.rootLocalID);
433 }
434 else if (request_type == LandManager.LAND_SELECT_OBJECTS_GROUP && false) //TODO: change false to group support!
435 {
436
437 }
438 else if (request_type == LandManager.LAND_SELECT_OBJECTS_OTHER && obj.rootPrimitive.OwnerID != remote_client.AgentId)
439 {
440 resultLocalIDs.Add(obj.rootLocalID);
441 }
442 }
443 }
444
445
446 bool firstCall = true;
447 int MAX_OBJECTS_PER_PACKET = 251;
448 ForceObjectSelectPacket pack = new ForceObjectSelectPacket();
449 ForceObjectSelectPacket.DataBlock[] data;
450 while (resultLocalIDs.Count > 0)
451 {
452 if (firstCall)
453 {
454 pack._Header.ResetList = true;
455 firstCall = false;
456 }
457 else
458 {
459 pack._Header.ResetList = false;
460 }
461
462 if (resultLocalIDs.Count > MAX_OBJECTS_PER_PACKET)
463 {
464 data = new ForceObjectSelectPacket.DataBlock[MAX_OBJECTS_PER_PACKET];
465 }
466 else
467 {
468 data = new ForceObjectSelectPacket.DataBlock[resultLocalIDs.Count];
469 }
470
471 int i;
472 for (i = 0; i < MAX_OBJECTS_PER_PACKET && resultLocalIDs.Count > 0; i++)
473 {
474 data[i] = new ForceObjectSelectPacket.DataBlock();
475 data[i].LocalID = Convert.ToUInt32(resultLocalIDs[0]);
476 resultLocalIDs.RemoveAt(0);
477 }
478 pack.Data = data;
479 remote_client.OutPacket((Packet)pack);
480 }
481
482 }
483 public void sendLandObjectOwners(IClientAPI remote_client)
484 {
485 Dictionary<LLUUID, int> ownersAndCount = new Dictionary<LLUUID, int>();
486 foreach (SceneObject obj in primsOverMe)
487 {
488 if (!ownersAndCount.ContainsKey(obj.rootPrimitive.OwnerID))
489 {
490 ownersAndCount.Add(obj.rootPrimitive.OwnerID, 0);
491 }
492 ownersAndCount[obj.rootPrimitive.OwnerID] += obj.primCount;
493 }
494 if (ownersAndCount.Count > 0)
495 {
496
497 ParcelObjectOwnersReplyPacket.DataBlock[] dataBlock = new ParcelObjectOwnersReplyPacket.DataBlock[32];
498
499 if (ownersAndCount.Count < 32)
500 {
501 dataBlock = new ParcelObjectOwnersReplyPacket.DataBlock[ownersAndCount.Count];
502 }
503
504
505 int num = 0;
506 foreach (LLUUID owner in ownersAndCount.Keys)
507 {
508 dataBlock[num] = new ParcelObjectOwnersReplyPacket.DataBlock();
509 dataBlock[num].Count = ownersAndCount[owner];
510 dataBlock[num].IsGroupOwned = false; //TODO: fix me when group support is added
511 dataBlock[num].OnlineStatus = true; //TODO: fix me later
512 dataBlock[num].OwnerID = owner;
513
514 num++;
515 }
516
517 ParcelObjectOwnersReplyPacket pack = new ParcelObjectOwnersReplyPacket();
518 pack.Data = dataBlock;
519 remote_client.OutPacket(pack);
520 }
521 }
522 #endregion
523
524 #region Object Returning
525 public void returnObject(SceneObject obj)
526 {
527 }
528 public void returnLandObjects(int type, LLUUID owner)
529 {
530
531 }
532 #endregion
533
534 #region Object Adding/Removing from Parcel
535 public void resetLandPrimCounts()
536 {
537 landData.groupPrims = 0;
538 landData.ownerPrims = 0;
539 landData.otherPrims = 0;
540 landData.selectedPrims = 0;
541 primsOverMe.Clear();
542 }
543
544 public void addPrimToCount(SceneObject obj)
545 {
546 LLUUID prim_owner = obj.rootPrimitive.OwnerID;
547 int prim_count = obj.primCount;
548
549 if (obj.isSelected)
550 {
551 landData.selectedPrims += prim_count;
552 }
553 else
554 {
555 if (prim_owner == landData.ownerID)
556 {
557 landData.ownerPrims += prim_count;
558 }
559 else
560 {
561 landData.otherPrims += prim_count;
562 }
563 }
564
565 primsOverMe.Add(obj);
566
567 }
568
569 public void removePrimFromCount(SceneObject obj)
570 {
571 if (primsOverMe.Contains(obj))
572 {
573 LLUUID prim_owner = obj.rootPrimitive.OwnerID;
574 int prim_count = obj.primCount;
575
576 if (prim_owner == landData.ownerID)
577 {
578 landData.ownerPrims -= prim_count;
579 }
580 else if (prim_owner == landData.groupID)
581 {
582 landData.groupPrims -= prim_count;
583 }
584 else
585 {
586 landData.otherPrims -= prim_count;
587 }
588
589 primsOverMe.Remove(obj);
590 }
591 }
592 #endregion
593
594 #endregion
595
596
597 }
598 #endregion
599}
diff --git a/OpenSim/Region/Environment/LandManagement/LandManager.cs b/OpenSim/Region/Environment/LandManagement/LandManager.cs
new file mode 100644
index 0000000..a596fae
--- /dev/null
+++ b/OpenSim/Region/Environment/LandManagement/LandManager.cs
@@ -0,0 +1,617 @@
1/*
2* Copyright (c) Contributors, http://www.openmetaverse.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*/
28using System;
29using System.Collections.Generic;
30using libsecondlife;
31using libsecondlife.Packets;
32using OpenSim.Framework.Interfaces;
33using OpenSim.Framework.Types;
34using OpenSim.Region.Environment.Scenes;
35
36namespace OpenSim.Region.Environment.LandManagement
37{
38
39
40 #region LandManager Class
41 /// <summary>
42 /// Handles Land objects and operations requiring information from other Land objects (divide, join, etc)
43 /// </summary>
44 public class LandManager : ILocalStorageLandObjectReceiver
45 {
46
47 #region Constants
48 //Land types set with flags in ParcelOverlay.
49 //Only one of these can be used.
50 public const byte LAND_TYPE_PUBLIC = (byte)0; //Equals 00000000
51 public const byte LAND_TYPE_OWNED_BY_OTHER = (byte)1; //Equals 00000001
52 public const byte LAND_TYPE_OWNED_BY_GROUP = (byte)2; //Equals 00000010
53 public const byte LAND_TYPE_OWNED_BY_REQUESTER = (byte)3; //Equals 00000011
54 public const byte LAND_TYPE_IS_FOR_SALE = (byte)4; //Equals 00000100
55 public const byte LAND_TYPE_IS_BEING_AUCTIONED = (byte)5; //Equals 00000101
56
57
58 //Flags that when set, a border on the given side will be placed
59 //NOTE: North and East is assumable by the west and south sides (if land to east has a west border, then I have an east border; etc)
60 //This took forever to figure out -- jeesh. /blame LL for even having to send these
61 public const byte LAND_FLAG_PROPERTY_BORDER_WEST = (byte)64; //Equals 01000000
62 public const byte LAND_FLAG_PROPERTY_BORDER_SOUTH = (byte)128; //Equals 10000000
63
64 //RequestResults (I think these are right, they seem to work):
65 public const int LAND_RESULT_SINGLE = 0; // The request they made contained only a single piece of land
66 public const int LAND_RESULT_MULTIPLE = 1; // The request they made contained more than a single peice of land
67
68 //ParcelSelectObjects
69 public const int LAND_SELECT_OBJECTS_OWNER = 2;
70 public const int LAND_SELECT_OBJECTS_GROUP = 4;
71 public const int LAND_SELECT_OBJECTS_OTHER = 8;
72
73
74 //These are other constants. Yay!
75 public const int START_LAND_LOCAL_ID = 1;
76 #endregion
77
78 #region Member Variables
79 public Dictionary<int, Land> landList = new Dictionary<int, Land>();
80 private int lastLandLocalID = START_LAND_LOCAL_ID - 1;
81 private int[,] landIDList = new int[64, 64];
82
83 /// <summary>
84 /// Set to true when a prim is moved, created, added. Performs a prim count update
85 /// </summary>
86 public bool landPrimCountTainted = false;
87
88 private Scene m_world;
89 private RegionInfo m_regInfo;
90
91 #endregion
92
93 #region Constructors
94 public LandManager(Scene world, RegionInfo reginfo)
95 {
96
97 m_world = world;
98 m_regInfo = reginfo;
99 landIDList.Initialize();
100
101 }
102 #endregion
103
104 #region Member Functions
105
106 #region Parcel From Storage Functions
107 public void LandFromStorage(LandData data)
108 {
109 Land new_land = new Land(data.ownerID, data.isGroupOwned, m_world);
110 new_land.landData = data.Copy();
111 new_land.setLandBitmapFromByteArray();
112 addLandObject(new_land);
113
114 }
115
116 public void NoLandDataFromStorage()
117 {
118 resetSimLandObjects();
119 }
120 #endregion
121
122 #region Parcel Add/Remove/Get/Create
123 /// <summary>
124 /// Creates a basic Parcel object without an owner (a zeroed key)
125 /// </summary>
126 /// <returns></returns>
127 public Land createBaseLand()
128 {
129 return new Land(new LLUUID(), false, m_world);
130 }
131
132 /// <summary>
133 /// Adds a land object to the stored list and adds them to the landIDList to what they own
134 /// </summary>
135 /// <param name="new_land">The land object being added</param>
136 public Land addLandObject(Land new_land)
137 {
138 lastLandLocalID++;
139 new_land.landData.localID = lastLandLocalID;
140 landList.Add(lastLandLocalID, new_land.Copy());
141
142
143 bool[,] landBitmap = new_land.getLandBitmap();
144 int x, y;
145 for (x = 0; x < 64; x++)
146 {
147 for (y = 0; y < 64; y++)
148 {
149 if (landBitmap[x, y])
150 {
151 landIDList[x, y] = lastLandLocalID;
152 }
153 }
154 }
155 landList[lastLandLocalID].forceUpdateLandInfo();
156
157 return new_land;
158
159 }
160 /// <summary>
161 /// Removes a land object from the list. Will not remove if local_id is still owning an area in landIDList
162 /// </summary>
163 /// <param name="local_id">Land.localID of the peice of land to remove.</param>
164 public void removeLandObject(int local_id)
165 {
166 int x, y;
167 for (x = 0; x < 64; x++)
168 {
169 for (y = 0; y < 64; y++)
170 {
171 if (landIDList[x, y] == local_id)
172 {
173 throw new Exception("Could not remove land object. Still being used at " + x + ", " + y);
174 }
175 }
176 }
177 m_world.localStorage.RemoveLandObject(landList[local_id].landData);
178 landList.Remove(local_id);
179 }
180
181 private void performFinalLandJoin(Land master, Land slave)
182 {
183 int x, y;
184 bool[,] landBitmapSlave = slave.getLandBitmap();
185 for (x = 0; x < 64; x++)
186 {
187 for (y = 0; y < 64; y++)
188 {
189 if (landBitmapSlave[x, y])
190 {
191 landIDList[x, y] = master.landData.localID;
192 }
193 }
194 }
195 removeLandObject(slave.landData.localID);
196 }
197 /// <summary>
198 /// Get the land object at the specified point
199 /// </summary>
200 /// <param name="x">Value between 0 - 256 on the x axis of the point</param>
201 /// <param name="y">Value between 0 - 256 on the y axis of the point</param>
202 /// <returns>Land object at the point supplied</returns>
203 public Land getLandObject(float x_float, float y_float)
204 {
205 int x = Convert.ToInt32(Math.Floor(Convert.ToDecimal(x_float) / Convert.ToDecimal(4.0)));
206 int y = Convert.ToInt32(Math.Floor(Convert.ToDecimal(y_float) / Convert.ToDecimal(4.0)));
207
208 if (x > 63 || y > 63 || x < 0 || y < 0)
209 {
210 throw new Exception("Error: Parcel not found at point " + x + ", " + y);
211 }
212 else
213 {
214 // Console.WriteLine("Point (" + x + ", " + y + ") determined from point (" + x_float + ", " + y_float + ")");
215 return landList[landIDList[x, y]];
216 }
217 }
218
219 public Land getLandObject(int x, int y)
220 {
221 if (x > 256 || y > 256 || x < 0 || y < 0)
222 {
223 throw new Exception("Error: Parcel not found at point " + x + ", " + y);
224 }
225 else
226 {
227 return landList[landIDList[x / 4, y / 4]];
228 }
229 }
230 #endregion
231
232 #region Parcel Modification
233 /// <summary>
234 /// Subdivides a piece of land
235 /// </summary>
236 /// <param name="start_x">West Point</param>
237 /// <param name="start_y">South Point</param>
238 /// <param name="end_x">East Point</param>
239 /// <param name="end_y">North Point</param>
240 /// <param name="attempting_user_id">LLUUID of user who is trying to subdivide</param>
241 /// <returns>Returns true if successful</returns>
242 private bool subdivide(int start_x, int start_y, int end_x, int end_y, LLUUID attempting_user_id)
243 {
244
245 //First, lets loop through the points and make sure they are all in the same peice of land
246 //Get the land object at start
247 Land startLandObject = getLandObject(start_x, start_y);
248 if (startLandObject == null) return false; //No such land object at the beginning
249
250 //Loop through the points
251 try
252 {
253 int totalX = end_x - start_x;
254 int totalY = end_y - start_y;
255 int x, y;
256 for (y = 0; y < totalY; y++)
257 {
258 for (x = 0; x < totalX; x++)
259 {
260 Land tempLandObject = getLandObject(start_x + x, start_y + y);
261 if (tempLandObject == null) return false; //No such land object at that point
262 if (tempLandObject != startLandObject) return false; //Subdividing over 2 land objects; no-no
263 }
264 }
265 }
266 catch (Exception)
267 {
268 return false; //Exception. For now, lets skip subdivision
269 }
270
271 //If we are still here, then they are subdividing within one piece of land
272 //Check owner
273 if (startLandObject.landData.ownerID != attempting_user_id)
274 {
275 return false; //They cant do this!
276 }
277
278 //Lets create a new land object with bitmap activated at that point (keeping the old land objects info)
279 Land newLand = startLandObject.Copy();
280 newLand.landData.landName = "Subdivision of " + newLand.landData.landName;
281 newLand.landData.globalID = LLUUID.Random();
282
283 newLand.setLandBitmap(Land.getSquareLandBitmap(start_x, start_y, end_x, end_y));
284
285 //Now, lets set the subdivision area of the original to false
286 int startLandObjectIndex = startLandObject.landData.localID;
287 landList[startLandObjectIndex].setLandBitmap(Land.modifyLandBitmapSquare(startLandObject.getLandBitmap(), start_x, start_y, end_x, end_y, false));
288 landList[startLandObjectIndex].forceUpdateLandInfo();
289
290
291 this.setPrimsTainted();
292
293 //Now add the new land object
294 Land result = addLandObject(newLand);
295 result.sendLandUpdateToAvatarsOverMe();
296
297
298
299
300 return true;
301 }
302 /// <summary>
303 /// Join 2 land objects together
304 /// </summary>
305 /// <param name="start_x">x value in first piece of land</param>
306 /// <param name="start_y">y value in first piece of land</param>
307 /// <param name="end_x">x value in second peice of land</param>
308 /// <param name="end_y">y value in second peice of land</param>
309 /// <param name="attempting_user_id">LLUUID of the avatar trying to join the land objects</param>
310 /// <returns>Returns true if successful</returns>
311 private bool join(int start_x, int start_y, int end_x, int end_y, LLUUID attempting_user_id)
312 {
313 end_x -= 4;
314 end_y -= 4;
315
316 List<Land> selectedLandObjects = new List<Land>();
317 int stepXSelected = 0;
318 int stepYSelected = 0;
319 for (stepYSelected = start_y; stepYSelected <= end_y; stepYSelected += 4)
320 {
321 for (stepXSelected = start_x; stepXSelected <= end_x; stepXSelected += 4)
322 {
323 Land p = getLandObject(stepXSelected,stepYSelected);
324 if (!selectedLandObjects.Contains(p))
325 {
326 selectedLandObjects.Add(p);
327 }
328 }
329 }
330 Land masterLandObject = selectedLandObjects[0];
331 selectedLandObjects.RemoveAt(0);
332
333
334 if (selectedLandObjects.Count < 1)
335 {
336 return false; //Only one piece of land selected
337 }
338 if (masterLandObject.landData.ownerID != attempting_user_id)
339 {
340 return false; //Not the same owner
341 }
342 foreach (Land p in selectedLandObjects)
343 {
344 if (p.landData.ownerID != masterLandObject.landData.ownerID)
345 {
346 return false; //Over multiple users. TODO: make this just ignore this piece of land?
347 }
348 }
349 foreach (Land slaveLandObject in selectedLandObjects)
350 {
351 landList[masterLandObject.landData.localID].setLandBitmap(Land.mergeLandBitmaps(masterLandObject.getLandBitmap(), slaveLandObject.getLandBitmap()));
352 performFinalLandJoin(masterLandObject, slaveLandObject);
353 }
354
355
356 this.setPrimsTainted();
357
358 masterLandObject.sendLandUpdateToAvatarsOverMe();
359
360 return true;
361
362
363
364 }
365 #endregion
366
367 #region Parcel Updating
368 /// <summary>
369 /// Where we send the ParcelOverlay packet to the client
370 /// </summary>
371 /// <param name="remote_client">The object representing the client</param>
372 public void sendParcelOverlay(IClientAPI remote_client)
373 {
374 const int LAND_BLOCKS_PER_PACKET = 1024;
375 int x, y = 0;
376 byte[] byteArray = new byte[LAND_BLOCKS_PER_PACKET];
377 int byteArrayCount = 0;
378 int sequenceID = 0;
379 ParcelOverlayPacket packet;
380
381 for (y = 0; y < 64; y++)
382 {
383 for (x = 0; x < 64; x++)
384 {
385 byte tempByte = (byte)0; //This represents the byte for the current 4x4
386 Land currentParcelBlock = getLandObject(x * 4, y * 4);
387
388 if (currentParcelBlock.landData.ownerID == remote_client.AgentId)
389 {
390 //Owner Flag
391 tempByte = Convert.ToByte(tempByte | LAND_TYPE_OWNED_BY_REQUESTER);
392 }
393 else if (currentParcelBlock.landData.salePrice > 0 && (currentParcelBlock.landData.authBuyerID == LLUUID.Zero || currentParcelBlock.landData.authBuyerID == remote_client.AgentId))
394 {
395 //Sale Flag
396 tempByte = Convert.ToByte(tempByte | LAND_TYPE_IS_FOR_SALE);
397 }
398 else if (currentParcelBlock.landData.ownerID == LLUUID.Zero)
399 {
400 //Public Flag
401 tempByte = Convert.ToByte(tempByte | LAND_TYPE_PUBLIC);
402 }
403 else
404 {
405 //Other Flag
406 tempByte = Convert.ToByte(tempByte | LAND_TYPE_OWNED_BY_OTHER);
407 }
408
409
410 //Now for border control
411 if (x == 0)
412 {
413 tempByte = Convert.ToByte(tempByte | LAND_FLAG_PROPERTY_BORDER_WEST);
414 }
415 else if (getLandObject((x - 1) * 4, y * 4) != currentParcelBlock)
416 {
417 tempByte = Convert.ToByte(tempByte | LAND_FLAG_PROPERTY_BORDER_WEST);
418 }
419
420 if (y == 0)
421 {
422 tempByte = Convert.ToByte(tempByte | LAND_FLAG_PROPERTY_BORDER_SOUTH);
423 }
424 else if (getLandObject(x * 4, (y - 1) * 4) != currentParcelBlock)
425 {
426 tempByte = Convert.ToByte(tempByte | LAND_FLAG_PROPERTY_BORDER_SOUTH);
427 }
428
429 byteArray[byteArrayCount] = tempByte;
430 byteArrayCount++;
431 if (byteArrayCount >= LAND_BLOCKS_PER_PACKET)
432 {
433 byteArrayCount = 0;
434 packet = new ParcelOverlayPacket();
435 packet.ParcelData.Data = byteArray;
436 packet.ParcelData.SequenceID = sequenceID;
437 remote_client.OutPacket((Packet)packet);
438 sequenceID++;
439 byteArray = new byte[LAND_BLOCKS_PER_PACKET];
440 }
441 }
442 }
443
444
445 }
446
447 public void handleParcelPropertiesRequest(int start_x, int start_y, int end_x, int end_y, int sequence_id, bool snap_selection, IClientAPI remote_client)
448 {
449 //Get the land objects within the bounds
450 List<Land> temp = new List<Land>();
451 int x, y, i;
452 int inc_x = end_x - start_x;
453 int inc_y = end_y - start_y;
454 for (x = 0; x < inc_x; x++)
455 {
456 for (y = 0; y < inc_y; y++)
457 {
458 Land currentParcel = getLandObject(start_x + x, start_y + y);
459 if (!temp.Contains(currentParcel))
460 {
461 currentParcel.forceUpdateLandInfo();
462 temp.Add(currentParcel);
463 }
464 }
465 }
466
467 int requestResult = LAND_RESULT_SINGLE;
468 if (temp.Count > 1)
469 {
470 requestResult = LAND_RESULT_MULTIPLE;
471 }
472
473 for (i = 0; i < temp.Count; i++)
474 {
475 temp[i].sendLandProperties(sequence_id, snap_selection, requestResult, remote_client);
476 }
477
478
479 sendParcelOverlay(remote_client);
480 }
481
482 public void handleParcelPropertiesUpdateRequest(ParcelPropertiesUpdatePacket packet, IClientAPI remote_client)
483 {
484 if (landList.ContainsKey(packet.ParcelData.LocalID))
485 {
486 landList[packet.ParcelData.LocalID].updateLandProperties(packet, remote_client);
487 }
488 }
489 public void handleParcelDivideRequest(int west, int south, int east, int north, IClientAPI remote_client)
490 {
491 subdivide(west, south, east, north, remote_client.AgentId);
492 }
493 public void handleParcelJoinRequest(int west, int south, int east, int north, IClientAPI remote_client)
494 {
495 join(west, south, east, north, remote_client.AgentId);
496
497 }
498
499 public void handleParcelSelectObjectsRequest(int local_id, int request_type, IClientAPI remote_client)
500 {
501 landList[local_id].sendForceObjectSelect(local_id, request_type, remote_client);
502 }
503
504 public void handleParcelObjectOwnersRequest(int local_id, IClientAPI remote_client)
505 {
506 landList[local_id].sendLandObjectOwners(remote_client);
507 }
508 #endregion
509
510 /// <summary>
511 /// Resets the sim to the default land object (full sim piece of land owned by the default user)
512 /// </summary>
513 public void resetSimLandObjects()
514 {
515 //Remove all the land objects in the sim and add a blank, full sim land object set to public
516 landList.Clear();
517 lastLandLocalID = START_LAND_LOCAL_ID - 1;
518 landIDList.Initialize();
519
520 Land fullSimParcel = new Land(LLUUID.Zero, false, m_world);
521
522 fullSimParcel.setLandBitmap(Land.getSquareLandBitmap(0, 0, 256, 256));
523 fullSimParcel.landData.ownerID = m_regInfo.MasterAvatarAssignedUUID;
524
525 addLandObject(fullSimParcel);
526
527 }
528
529
530 public void handleSignificantClientMovement(IClientAPI remote_client)
531 {
532 ScenePresence clientAvatar = m_world.RequestAvatar(remote_client.AgentId);
533 if (clientAvatar != null)
534 {
535 Land over = getLandObject(clientAvatar.Pos.X,clientAvatar.Pos.Y);
536 if (over != null)
537 {
538 over.sendLandProperties(0, false, 0, remote_client);
539 }
540 }
541 }
542
543 public void resetAllLandPrimCounts()
544 {
545 foreach (Land p in landList.Values)
546 {
547 p.resetLandPrimCounts();
548 }
549 }
550 public void setPrimsTainted()
551 {
552 this.landPrimCountTainted = true;
553 }
554
555 public void addPrimToLandPrimCounts(SceneObject obj)
556 {
557 LLVector3 position = obj.Pos;
558 Land landUnderPrim = getLandObject(position.X, position.Y);
559 if (landUnderPrim != null)
560 {
561 landUnderPrim.addPrimToCount(obj);
562 }
563 }
564
565 public void removePrimFromLandPrimCounts(SceneObject obj)
566 {
567 foreach (Land p in landList.Values)
568 {
569 p.removePrimFromCount(obj);
570 }
571 }
572
573 public void finalizeLandPrimCountUpdate()
574 {
575 //Get Simwide prim count for owner
576 Dictionary<LLUUID, List<Land>> landOwnersAndParcels = new Dictionary<LLUUID,List<Land>>();
577 foreach (Land p in landList.Values)
578 {
579 if(!landOwnersAndParcels.ContainsKey(p.landData.ownerID))
580 {
581 List<Land> tempList = new List<Land>();
582 tempList.Add(p);
583 landOwnersAndParcels.Add(p.landData.ownerID,tempList);
584 }
585 else
586 {
587 landOwnersAndParcels[p.landData.ownerID].Add(p);
588 }
589 }
590
591 foreach (LLUUID owner in landOwnersAndParcels.Keys)
592 {
593 int simArea = 0;
594 int simPrims = 0;
595 foreach (Land p in landOwnersAndParcels[owner])
596 {
597 simArea += p.landData.area;
598 simPrims += p.landData.ownerPrims + p.landData.otherPrims + p.landData.groupPrims + p.landData.selectedPrims;
599 }
600
601 foreach (Land p in landOwnersAndParcels[owner])
602 {
603 p.landData.simwideArea = simArea;
604 p.landData.simwidePrims = simPrims;
605 }
606 }
607
608 }
609 #endregion
610 }
611 #endregion
612
613
614
615
616
617}