aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment/LandManagement/LandManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Environment/LandManagement/LandManager.cs')
-rw-r--r--OpenSim/Region/Environment/LandManagement/LandManager.cs617
1 files changed, 617 insertions, 0 deletions
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}