From 41b9baa054b135479939528016e31cbad4a9d765 Mon Sep 17 00:00:00 2001 From: mingchen Date: Mon, 16 Jul 2007 19:52:46 +0000 Subject: [PATCH] *Adding some more files --- .../Region/Environment/LandManagement/Land.cs | 599 +++++++++++++++++ .../Environment/LandManagement/LandManager.cs | 617 ++++++++++++++++++ 2 files changed, 1216 insertions(+) create mode 100644 OpenSim/Region/Environment/LandManagement/Land.cs create mode 100644 OpenSim/Region/Environment/LandManagement/LandManager.cs diff --git a/OpenSim/Region/Environment/LandManagement/Land.cs b/OpenSim/Region/Environment/LandManagement/Land.cs new file mode 100644 index 0000000000..b333f36e83 --- /dev/null +++ b/OpenSim/Region/Environment/LandManagement/Land.cs @@ -0,0 +1,599 @@ +using System; +using System.Collections.Generic; +using libsecondlife; +using libsecondlife.Packets; +using OpenSim.Framework.Interfaces; +using OpenSim.Framework.Types; +using OpenSim.Region.Environment.Scenes; + +namespace OpenSim.Region.Environment.LandManagement +{ + #region Parcel Class + /// + /// Keeps track of a specific piece of land's information + /// + public class Land + { + #region Member Variables + public LandData landData = new LandData(); + public List primsOverMe = new List(); + + public Scene m_world; + + private bool[,] landBitmap = new bool[64, 64]; + + #endregion + + + #region Constructors + public Land(LLUUID owner_id, bool is_group_owned, Scene world) + { + m_world = world; + landData.ownerID = owner_id; + landData.isGroupOwned = is_group_owned; + + } + #endregion + + + #region Member Functions + + #region General Functions + /// + /// Checks to see if this land object contains a point + /// + /// + /// + /// Returns true if the piece of land contains the specified point + public bool containsPoint(int x, int y) + { + if (x >= 0 && y >= 0 && x <= 256 && x <= 256) + { + return (landBitmap[x / 4, y / 4] == true); + } + else + { + return false; + } + } + + public Land Copy() + { + Land newLand = new Land(this.landData.ownerID, this.landData.isGroupOwned, m_world); + + //Place all new variables here! + newLand.landBitmap = (bool[,])(this.landBitmap.Clone()); + newLand.landData = landData.Copy(); + + return newLand; + } + + #endregion + + + #region Packet Request Handling + /// + /// Sends land properties as requested + /// + /// ID sent by client for them to keep track of + /// Bool sent by client for them to use + /// Object representing the client + public void sendLandProperties(int sequence_id, bool snap_selection, int request_result, IClientAPI remote_client) + { + + ParcelPropertiesPacket updatePacket = new ParcelPropertiesPacket(); + updatePacket.ParcelData.AABBMax = landData.AABBMax; + updatePacket.ParcelData.AABBMin = landData.AABBMin; + updatePacket.ParcelData.Area = landData.area; + updatePacket.ParcelData.AuctionID = landData.auctionID; + updatePacket.ParcelData.AuthBuyerID = landData.authBuyerID; //unemplemented + + updatePacket.ParcelData.Bitmap = landData.landBitmapByteArray; + + updatePacket.ParcelData.Desc = Helpers.StringToField(landData.landDesc); + updatePacket.ParcelData.Category = (byte)landData.category; + updatePacket.ParcelData.ClaimDate = landData.claimDate; + updatePacket.ParcelData.ClaimPrice = landData.claimPrice; + updatePacket.ParcelData.GroupID = landData.groupID; + updatePacket.ParcelData.GroupPrims = landData.groupPrims; + updatePacket.ParcelData.IsGroupOwned = landData.isGroupOwned; + updatePacket.ParcelData.LandingType = (byte)landData.landingType; + updatePacket.ParcelData.LocalID = landData.localID; + if (landData.area > 0) + { + updatePacket.ParcelData.MaxPrims = Convert.ToInt32(Math.Round((Convert.ToDecimal(landData.area) / Convert.ToDecimal(65536)) * 15000 * Convert.ToDecimal(m_world.RegionInfo.estateSettings.objectBonusFactor))); + } + else + { + updatePacket.ParcelData.MaxPrims = 0; + } + updatePacket.ParcelData.MediaAutoScale = landData.mediaAutoScale; + updatePacket.ParcelData.MediaID = landData.mediaID; + updatePacket.ParcelData.MediaURL = Helpers.StringToField(landData.mediaURL); + updatePacket.ParcelData.MusicURL = Helpers.StringToField(landData.musicURL); + updatePacket.ParcelData.Name = Helpers.StringToField(landData.landName); + updatePacket.ParcelData.OtherCleanTime = 0; //unemplemented + updatePacket.ParcelData.OtherCount = 0; //unemplemented + updatePacket.ParcelData.OtherPrims = landData.otherPrims; + updatePacket.ParcelData.OwnerID = landData.ownerID; + updatePacket.ParcelData.OwnerPrims = landData.ownerPrims; + updatePacket.ParcelData.ParcelFlags = landData.landFlags; + updatePacket.ParcelData.ParcelPrimBonus = m_world.RegionInfo.estateSettings.objectBonusFactor; + updatePacket.ParcelData.PassHours = landData.passHours; + updatePacket.ParcelData.PassPrice = landData.passPrice; + updatePacket.ParcelData.PublicCount = 0; //unemplemented + updatePacket.ParcelData.RegionDenyAnonymous = (((uint)m_world.RegionInfo.estateSettings.regionFlags & (uint)Simulator.RegionFlags.DenyAnonymous) > 0); + updatePacket.ParcelData.RegionDenyIdentified = (((uint)m_world.RegionInfo.estateSettings.regionFlags & (uint)Simulator.RegionFlags.DenyIdentified) > 0); + updatePacket.ParcelData.RegionDenyTransacted = (((uint)m_world.RegionInfo.estateSettings.regionFlags & (uint)Simulator.RegionFlags.DenyTransacted) > 0); + updatePacket.ParcelData.RegionPushOverride = (((uint)m_world.RegionInfo.estateSettings.regionFlags & (uint)Simulator.RegionFlags.RestrictPushObject) > 0); + updatePacket.ParcelData.RentPrice = 0; + updatePacket.ParcelData.RequestResult = request_result; + updatePacket.ParcelData.SalePrice = landData.salePrice; + updatePacket.ParcelData.SelectedPrims = landData.selectedPrims; + updatePacket.ParcelData.SelfCount = 0;//unemplemented + updatePacket.ParcelData.SequenceID = sequence_id; + if (landData.simwideArea > 0) + { + updatePacket.ParcelData.SimWideMaxPrims = Convert.ToInt32(Math.Round((Convert.ToDecimal(landData.simwideArea) / Convert.ToDecimal(65536)) * 15000 * Convert.ToDecimal(m_world.RegionInfo.estateSettings.objectBonusFactor))); + } + else + { + updatePacket.ParcelData.SimWideMaxPrims = 0; + } + updatePacket.ParcelData.SimWideTotalPrims = landData.simwidePrims; + updatePacket.ParcelData.SnapSelection = snap_selection; + updatePacket.ParcelData.SnapshotID = landData.snapshotID; + updatePacket.ParcelData.Status = (byte)landData.landStatus; + updatePacket.ParcelData.TotalPrims = landData.ownerPrims + landData.groupPrims + landData.otherPrims + landData.selectedPrims; + updatePacket.ParcelData.UserLocation = landData.userLocation; + updatePacket.ParcelData.UserLookAt = landData.userLookAt; + remote_client.OutPacket((Packet)updatePacket); + } + + public void updateLandProperties(ParcelPropertiesUpdatePacket packet, IClientAPI remote_client) + { + if (remote_client.AgentId == landData.ownerID) + { + //Needs later group support + landData.authBuyerID = packet.ParcelData.AuthBuyerID; + landData.category = (libsecondlife.Parcel.ParcelCategory)packet.ParcelData.Category; + landData.landDesc = Helpers.FieldToUTF8String(packet.ParcelData.Desc); + landData.groupID = packet.ParcelData.GroupID; + landData.landingType = packet.ParcelData.LandingType; + landData.mediaAutoScale = packet.ParcelData.MediaAutoScale; + landData.mediaID = packet.ParcelData.MediaID; + landData.mediaURL = Helpers.FieldToUTF8String(packet.ParcelData.MediaURL); + landData.musicURL = Helpers.FieldToUTF8String(packet.ParcelData.MusicURL); + landData.landName = Helpers.FieldToUTF8String(packet.ParcelData.Name); + landData.landFlags = packet.ParcelData.ParcelFlags; + landData.passHours = packet.ParcelData.PassHours; + landData.passPrice = packet.ParcelData.PassPrice; + landData.salePrice = packet.ParcelData.SalePrice; + landData.snapshotID = packet.ParcelData.SnapshotID; + landData.userLocation = packet.ParcelData.UserLocation; + landData.userLookAt = packet.ParcelData.UserLookAt; + sendLandUpdateToAvatarsOverMe(); + + + } + } + + public void sendLandUpdateToAvatarsOverMe() + { + List avatars = m_world.RequestAvatarList(); + for (int i = 0; i < avatars.Count; i++) + { + Land over = m_world.LandManager.getLandObject((int)Math.Round(avatars[i].Pos.X), (int)Math.Round(avatars[i].Pos.Y)); + if (over.landData.localID == this.landData.localID) + { + sendLandProperties(0, false, 0, avatars[i].ControllingClient); + } + } + } + #endregion + + + #region Update Functions + /// + /// Updates the AABBMin and AABBMax values after area/shape modification of the land object + /// + private void updateAABBAndAreaValues() + { + int min_x = 64; + int min_y = 64; + int max_x = 0; + int max_y = 0; + int tempArea = 0; + int x, y; + for (x = 0; x < 64; x++) + { + for (y = 0; y < 64; y++) + { + if (landBitmap[x, y] == true) + { + if (min_x > x) min_x = x; + if (min_y > y) min_y = y; + if (max_x < x) max_x = x; + if (max_y < y) max_y = y; + tempArea += 16; //16sqm peice of land + } + } + } + landData.AABBMin = new LLVector3((float)(min_x * 4), (float)(min_y * 4), (float)m_world.Terrain.get((min_x * 4), (min_y * 4))); + landData.AABBMax = new LLVector3((float)(max_x * 4), (float)(max_y * 4), (float)m_world.Terrain.get((max_x * 4), (max_y * 4))); + landData.area = tempArea; + } + + public void updateLandBitmapByteArray() + { + landData.landBitmapByteArray = convertLandBitmapToBytes(); + } + + /// + /// Update all settings in land such as area, bitmap byte array, etc + /// + public void forceUpdateLandInfo() + { + this.updateAABBAndAreaValues(); + this.updateLandBitmapByteArray(); + } + + public void setLandBitmapFromByteArray() + { + landBitmap = convertBytesToLandBitmap(); + } + #endregion + + + #region Land Bitmap Functions + /// + /// Sets the land's bitmap manually + /// + /// 64x64 block representing where this land is on a map + public void setLandBitmap(bool[,] bitmap) + { + if (bitmap.GetLength(0) != 64 || bitmap.GetLength(1) != 64 || bitmap.Rank != 2) + { + //Throw an exception - The bitmap is not 64x64 + throw new Exception("Error: Invalid Parcel Bitmap"); + } + else + { + //Valid: Lets set it + landBitmap = bitmap; + forceUpdateLandInfo(); + + } + } + /// + /// Gets the land's bitmap manually + /// + /// + public bool[,] getLandBitmap() + { + return landBitmap; + } + /// + /// Converts the land bitmap to a packet friendly byte array + /// + /// + private byte[] convertLandBitmapToBytes() + { + byte[] tempConvertArr = new byte[512]; + byte tempByte = 0; + int x, y, i, byteNum = 0; + i = 0; + for (y = 0; y < 64; y++) + { + for (x = 0; x < 64; x++) + { + tempByte = Convert.ToByte(tempByte | Convert.ToByte(landBitmap[x, y]) << (i++ % 8)); + if (i % 8 == 0) + { + tempConvertArr[byteNum] = tempByte; + tempByte = (byte)0; + i = 0; + byteNum++; + } + } + } + return tempConvertArr; + } + + private bool[,] convertBytesToLandBitmap() + { + bool[,] tempConvertMap = new bool[64, 64]; + tempConvertMap.Initialize(); + byte tempByte = 0; + int x = 0, y = 0, i = 0, bitNum = 0; + for (i = 0; i < 512; i++) + { + tempByte = landData.landBitmapByteArray[i]; + for (bitNum = 0; bitNum < 8; bitNum++) + { + bool bit = Convert.ToBoolean(Convert.ToByte(tempByte >> bitNum) & (byte)1); + tempConvertMap[x, y] = bit; + x++; + if (x > 63) + { + x = 0; + y++; + } + + } + + } + return tempConvertMap; + } + /// + /// Full sim land object creation + /// + /// + public static bool[,] basicFullRegionLandBitmap() + { + return getSquareLandBitmap(0, 0, 256, 256); + } + + /// + /// Used to modify the bitmap between the x and y points. Points use 64 scale + /// + /// + /// + /// + /// + /// + public static bool[,] getSquareLandBitmap(int start_x, int start_y, int end_x, int end_y) + { + + bool[,] tempBitmap = new bool[64, 64]; + tempBitmap.Initialize(); + + tempBitmap = modifyLandBitmapSquare(tempBitmap, start_x, start_y, end_x, end_y, true); + return tempBitmap; + } + + /// + /// Change a land bitmap at within a square and set those points to a specific value + /// + /// + /// + /// + /// + /// + /// + /// + public static bool[,] modifyLandBitmapSquare(bool[,] land_bitmap, int start_x, int start_y, int end_x, int end_y, bool set_value) + { + if (land_bitmap.GetLength(0) != 64 || land_bitmap.GetLength(1) != 64 || land_bitmap.Rank != 2) + { + //Throw an exception - The bitmap is not 64x64 + throw new Exception("Error: Invalid Parcel Bitmap in modifyLandBitmapSquare()"); + } + + int x, y; + for (y = 0; y < 64; y++) + { + for (x = 0; x < 64; x++) + { + if (x >= start_x / 4 && x < end_x / 4 + && y >= start_y / 4 && y < end_y / 4) + { + land_bitmap[x, y] = set_value; + } + } + } + return land_bitmap; + } + /// + /// Join the true values of 2 bitmaps together + /// + /// + /// + /// + public static bool[,] mergeLandBitmaps(bool[,] bitmap_base, bool[,] bitmap_add) + { + if (bitmap_base.GetLength(0) != 64 || bitmap_base.GetLength(1) != 64 || bitmap_base.Rank != 2) + { + //Throw an exception - The bitmap is not 64x64 + throw new Exception("Error: Invalid Parcel Bitmap - Bitmap_base in mergeLandBitmaps"); + } + if (bitmap_add.GetLength(0) != 64 || bitmap_add.GetLength(1) != 64 || bitmap_add.Rank != 2) + { + //Throw an exception - The bitmap is not 64x64 + throw new Exception("Error: Invalid Parcel Bitmap - Bitmap_add in mergeLandBitmaps"); + + } + + int x, y; + for (y = 0; y < 64; y++) + { + for (x = 0; x < 64; x++) + { + if (bitmap_add[x, y]) + { + bitmap_base[x, y] = true; + } + } + } + return bitmap_base; + } + #endregion + + #region Object Select and Object Owner Listing + public void sendForceObjectSelect(int local_id, int request_type, IClientAPI remote_client) + { + List resultLocalIDs = new List(); + foreach (SceneObject obj in primsOverMe) + { + if (obj.rootLocalID > 0) + { + if (request_type == LandManager.LAND_SELECT_OBJECTS_OWNER && obj.rootPrimitive.OwnerID == this.landData.ownerID) + { + resultLocalIDs.Add(obj.rootLocalID); + } + else if (request_type == LandManager.LAND_SELECT_OBJECTS_GROUP && false) //TODO: change false to group support! + { + + } + else if (request_type == LandManager.LAND_SELECT_OBJECTS_OTHER && obj.rootPrimitive.OwnerID != remote_client.AgentId) + { + resultLocalIDs.Add(obj.rootLocalID); + } + } + } + + + bool firstCall = true; + int MAX_OBJECTS_PER_PACKET = 251; + ForceObjectSelectPacket pack = new ForceObjectSelectPacket(); + ForceObjectSelectPacket.DataBlock[] data; + while (resultLocalIDs.Count > 0) + { + if (firstCall) + { + pack._Header.ResetList = true; + firstCall = false; + } + else + { + pack._Header.ResetList = false; + } + + if (resultLocalIDs.Count > MAX_OBJECTS_PER_PACKET) + { + data = new ForceObjectSelectPacket.DataBlock[MAX_OBJECTS_PER_PACKET]; + } + else + { + data = new ForceObjectSelectPacket.DataBlock[resultLocalIDs.Count]; + } + + int i; + for (i = 0; i < MAX_OBJECTS_PER_PACKET && resultLocalIDs.Count > 0; i++) + { + data[i] = new ForceObjectSelectPacket.DataBlock(); + data[i].LocalID = Convert.ToUInt32(resultLocalIDs[0]); + resultLocalIDs.RemoveAt(0); + } + pack.Data = data; + remote_client.OutPacket((Packet)pack); + } + + } + public void sendLandObjectOwners(IClientAPI remote_client) + { + Dictionary ownersAndCount = new Dictionary(); + foreach (SceneObject obj in primsOverMe) + { + if (!ownersAndCount.ContainsKey(obj.rootPrimitive.OwnerID)) + { + ownersAndCount.Add(obj.rootPrimitive.OwnerID, 0); + } + ownersAndCount[obj.rootPrimitive.OwnerID] += obj.primCount; + } + if (ownersAndCount.Count > 0) + { + + ParcelObjectOwnersReplyPacket.DataBlock[] dataBlock = new ParcelObjectOwnersReplyPacket.DataBlock[32]; + + if (ownersAndCount.Count < 32) + { + dataBlock = new ParcelObjectOwnersReplyPacket.DataBlock[ownersAndCount.Count]; + } + + + int num = 0; + foreach (LLUUID owner in ownersAndCount.Keys) + { + dataBlock[num] = new ParcelObjectOwnersReplyPacket.DataBlock(); + dataBlock[num].Count = ownersAndCount[owner]; + dataBlock[num].IsGroupOwned = false; //TODO: fix me when group support is added + dataBlock[num].OnlineStatus = true; //TODO: fix me later + dataBlock[num].OwnerID = owner; + + num++; + } + + ParcelObjectOwnersReplyPacket pack = new ParcelObjectOwnersReplyPacket(); + pack.Data = dataBlock; + remote_client.OutPacket(pack); + } + } + #endregion + + #region Object Returning + public void returnObject(SceneObject obj) + { + } + public void returnLandObjects(int type, LLUUID owner) + { + + } + #endregion + + #region Object Adding/Removing from Parcel + public void resetLandPrimCounts() + { + landData.groupPrims = 0; + landData.ownerPrims = 0; + landData.otherPrims = 0; + landData.selectedPrims = 0; + primsOverMe.Clear(); + } + + public void addPrimToCount(SceneObject obj) + { + LLUUID prim_owner = obj.rootPrimitive.OwnerID; + int prim_count = obj.primCount; + + if (obj.isSelected) + { + landData.selectedPrims += prim_count; + } + else + { + if (prim_owner == landData.ownerID) + { + landData.ownerPrims += prim_count; + } + else + { + landData.otherPrims += prim_count; + } + } + + primsOverMe.Add(obj); + + } + + public void removePrimFromCount(SceneObject obj) + { + if (primsOverMe.Contains(obj)) + { + LLUUID prim_owner = obj.rootPrimitive.OwnerID; + int prim_count = obj.primCount; + + if (prim_owner == landData.ownerID) + { + landData.ownerPrims -= prim_count; + } + else if (prim_owner == landData.groupID) + { + landData.groupPrims -= prim_count; + } + else + { + landData.otherPrims -= prim_count; + } + + primsOverMe.Remove(obj); + } + } + #endregion + + #endregion + + + } + #endregion +} diff --git a/OpenSim/Region/Environment/LandManagement/LandManager.cs b/OpenSim/Region/Environment/LandManagement/LandManager.cs new file mode 100644 index 0000000000..a596fae32b --- /dev/null +++ b/OpenSim/Region/Environment/LandManagement/LandManager.cs @@ -0,0 +1,617 @@ +/* +* Copyright (c) Contributors, http://www.openmetaverse.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ +using System; +using System.Collections.Generic; +using libsecondlife; +using libsecondlife.Packets; +using OpenSim.Framework.Interfaces; +using OpenSim.Framework.Types; +using OpenSim.Region.Environment.Scenes; + +namespace OpenSim.Region.Environment.LandManagement +{ + + + #region LandManager Class + /// + /// Handles Land objects and operations requiring information from other Land objects (divide, join, etc) + /// + public class LandManager : ILocalStorageLandObjectReceiver + { + + #region Constants + //Land types set with flags in ParcelOverlay. + //Only one of these can be used. + public const byte LAND_TYPE_PUBLIC = (byte)0; //Equals 00000000 + public const byte LAND_TYPE_OWNED_BY_OTHER = (byte)1; //Equals 00000001 + public const byte LAND_TYPE_OWNED_BY_GROUP = (byte)2; //Equals 00000010 + public const byte LAND_TYPE_OWNED_BY_REQUESTER = (byte)3; //Equals 00000011 + public const byte LAND_TYPE_IS_FOR_SALE = (byte)4; //Equals 00000100 + public const byte LAND_TYPE_IS_BEING_AUCTIONED = (byte)5; //Equals 00000101 + + + //Flags that when set, a border on the given side will be placed + //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) + //This took forever to figure out -- jeesh. /blame LL for even having to send these + public const byte LAND_FLAG_PROPERTY_BORDER_WEST = (byte)64; //Equals 01000000 + public const byte LAND_FLAG_PROPERTY_BORDER_SOUTH = (byte)128; //Equals 10000000 + + //RequestResults (I think these are right, they seem to work): + public const int LAND_RESULT_SINGLE = 0; // The request they made contained only a single piece of land + public const int LAND_RESULT_MULTIPLE = 1; // The request they made contained more than a single peice of land + + //ParcelSelectObjects + public const int LAND_SELECT_OBJECTS_OWNER = 2; + public const int LAND_SELECT_OBJECTS_GROUP = 4; + public const int LAND_SELECT_OBJECTS_OTHER = 8; + + + //These are other constants. Yay! + public const int START_LAND_LOCAL_ID = 1; + #endregion + + #region Member Variables + public Dictionary landList = new Dictionary(); + private int lastLandLocalID = START_LAND_LOCAL_ID - 1; + private int[,] landIDList = new int[64, 64]; + + /// + /// Set to true when a prim is moved, created, added. Performs a prim count update + /// + public bool landPrimCountTainted = false; + + private Scene m_world; + private RegionInfo m_regInfo; + + #endregion + + #region Constructors + public LandManager(Scene world, RegionInfo reginfo) + { + + m_world = world; + m_regInfo = reginfo; + landIDList.Initialize(); + + } + #endregion + + #region Member Functions + + #region Parcel From Storage Functions + public void LandFromStorage(LandData data) + { + Land new_land = new Land(data.ownerID, data.isGroupOwned, m_world); + new_land.landData = data.Copy(); + new_land.setLandBitmapFromByteArray(); + addLandObject(new_land); + + } + + public void NoLandDataFromStorage() + { + resetSimLandObjects(); + } + #endregion + + #region Parcel Add/Remove/Get/Create + /// + /// Creates a basic Parcel object without an owner (a zeroed key) + /// + /// + public Land createBaseLand() + { + return new Land(new LLUUID(), false, m_world); + } + + /// + /// Adds a land object to the stored list and adds them to the landIDList to what they own + /// + /// The land object being added + public Land addLandObject(Land new_land) + { + lastLandLocalID++; + new_land.landData.localID = lastLandLocalID; + landList.Add(lastLandLocalID, new_land.Copy()); + + + bool[,] landBitmap = new_land.getLandBitmap(); + int x, y; + for (x = 0; x < 64; x++) + { + for (y = 0; y < 64; y++) + { + if (landBitmap[x, y]) + { + landIDList[x, y] = lastLandLocalID; + } + } + } + landList[lastLandLocalID].forceUpdateLandInfo(); + + return new_land; + + } + /// + /// Removes a land object from the list. Will not remove if local_id is still owning an area in landIDList + /// + /// Land.localID of the peice of land to remove. + public void removeLandObject(int local_id) + { + int x, y; + for (x = 0; x < 64; x++) + { + for (y = 0; y < 64; y++) + { + if (landIDList[x, y] == local_id) + { + throw new Exception("Could not remove land object. Still being used at " + x + ", " + y); + } + } + } + m_world.localStorage.RemoveLandObject(landList[local_id].landData); + landList.Remove(local_id); + } + + private void performFinalLandJoin(Land master, Land slave) + { + int x, y; + bool[,] landBitmapSlave = slave.getLandBitmap(); + for (x = 0; x < 64; x++) + { + for (y = 0; y < 64; y++) + { + if (landBitmapSlave[x, y]) + { + landIDList[x, y] = master.landData.localID; + } + } + } + removeLandObject(slave.landData.localID); + } + /// + /// Get the land object at the specified point + /// + /// Value between 0 - 256 on the x axis of the point + /// Value between 0 - 256 on the y axis of the point + /// Land object at the point supplied + public Land getLandObject(float x_float, float y_float) + { + int x = Convert.ToInt32(Math.Floor(Convert.ToDecimal(x_float) / Convert.ToDecimal(4.0))); + int y = Convert.ToInt32(Math.Floor(Convert.ToDecimal(y_float) / Convert.ToDecimal(4.0))); + + if (x > 63 || y > 63 || x < 0 || y < 0) + { + throw new Exception("Error: Parcel not found at point " + x + ", " + y); + } + else + { + // Console.WriteLine("Point (" + x + ", " + y + ") determined from point (" + x_float + ", " + y_float + ")"); + return landList[landIDList[x, y]]; + } + } + + public Land getLandObject(int x, int y) + { + if (x > 256 || y > 256 || x < 0 || y < 0) + { + throw new Exception("Error: Parcel not found at point " + x + ", " + y); + } + else + { + return landList[landIDList[x / 4, y / 4]]; + } + } + #endregion + + #region Parcel Modification + /// + /// Subdivides a piece of land + /// + /// West Point + /// South Point + /// East Point + /// North Point + /// LLUUID of user who is trying to subdivide + /// Returns true if successful + private bool subdivide(int start_x, int start_y, int end_x, int end_y, LLUUID attempting_user_id) + { + + //First, lets loop through the points and make sure they are all in the same peice of land + //Get the land object at start + Land startLandObject = getLandObject(start_x, start_y); + if (startLandObject == null) return false; //No such land object at the beginning + + //Loop through the points + try + { + int totalX = end_x - start_x; + int totalY = end_y - start_y; + int x, y; + for (y = 0; y < totalY; y++) + { + for (x = 0; x < totalX; x++) + { + Land tempLandObject = getLandObject(start_x + x, start_y + y); + if (tempLandObject == null) return false; //No such land object at that point + if (tempLandObject != startLandObject) return false; //Subdividing over 2 land objects; no-no + } + } + } + catch (Exception) + { + return false; //Exception. For now, lets skip subdivision + } + + //If we are still here, then they are subdividing within one piece of land + //Check owner + if (startLandObject.landData.ownerID != attempting_user_id) + { + return false; //They cant do this! + } + + //Lets create a new land object with bitmap activated at that point (keeping the old land objects info) + Land newLand = startLandObject.Copy(); + newLand.landData.landName = "Subdivision of " + newLand.landData.landName; + newLand.landData.globalID = LLUUID.Random(); + + newLand.setLandBitmap(Land.getSquareLandBitmap(start_x, start_y, end_x, end_y)); + + //Now, lets set the subdivision area of the original to false + int startLandObjectIndex = startLandObject.landData.localID; + landList[startLandObjectIndex].setLandBitmap(Land.modifyLandBitmapSquare(startLandObject.getLandBitmap(), start_x, start_y, end_x, end_y, false)); + landList[startLandObjectIndex].forceUpdateLandInfo(); + + + this.setPrimsTainted(); + + //Now add the new land object + Land result = addLandObject(newLand); + result.sendLandUpdateToAvatarsOverMe(); + + + + + return true; + } + /// + /// Join 2 land objects together + /// + /// x value in first piece of land + /// y value in first piece of land + /// x value in second peice of land + /// y value in second peice of land + /// LLUUID of the avatar trying to join the land objects + /// Returns true if successful + private bool join(int start_x, int start_y, int end_x, int end_y, LLUUID attempting_user_id) + { + end_x -= 4; + end_y -= 4; + + List selectedLandObjects = new List(); + int stepXSelected = 0; + int stepYSelected = 0; + for (stepYSelected = start_y; stepYSelected <= end_y; stepYSelected += 4) + { + for (stepXSelected = start_x; stepXSelected <= end_x; stepXSelected += 4) + { + Land p = getLandObject(stepXSelected,stepYSelected); + if (!selectedLandObjects.Contains(p)) + { + selectedLandObjects.Add(p); + } + } + } + Land masterLandObject = selectedLandObjects[0]; + selectedLandObjects.RemoveAt(0); + + + if (selectedLandObjects.Count < 1) + { + return false; //Only one piece of land selected + } + if (masterLandObject.landData.ownerID != attempting_user_id) + { + return false; //Not the same owner + } + foreach (Land p in selectedLandObjects) + { + if (p.landData.ownerID != masterLandObject.landData.ownerID) + { + return false; //Over multiple users. TODO: make this just ignore this piece of land? + } + } + foreach (Land slaveLandObject in selectedLandObjects) + { + landList[masterLandObject.landData.localID].setLandBitmap(Land.mergeLandBitmaps(masterLandObject.getLandBitmap(), slaveLandObject.getLandBitmap())); + performFinalLandJoin(masterLandObject, slaveLandObject); + } + + + this.setPrimsTainted(); + + masterLandObject.sendLandUpdateToAvatarsOverMe(); + + return true; + + + + } + #endregion + + #region Parcel Updating + /// + /// Where we send the ParcelOverlay packet to the client + /// + /// The object representing the client + public void sendParcelOverlay(IClientAPI remote_client) + { + const int LAND_BLOCKS_PER_PACKET = 1024; + int x, y = 0; + byte[] byteArray = new byte[LAND_BLOCKS_PER_PACKET]; + int byteArrayCount = 0; + int sequenceID = 0; + ParcelOverlayPacket packet; + + for (y = 0; y < 64; y++) + { + for (x = 0; x < 64; x++) + { + byte tempByte = (byte)0; //This represents the byte for the current 4x4 + Land currentParcelBlock = getLandObject(x * 4, y * 4); + + if (currentParcelBlock.landData.ownerID == remote_client.AgentId) + { + //Owner Flag + tempByte = Convert.ToByte(tempByte | LAND_TYPE_OWNED_BY_REQUESTER); + } + else if (currentParcelBlock.landData.salePrice > 0 && (currentParcelBlock.landData.authBuyerID == LLUUID.Zero || currentParcelBlock.landData.authBuyerID == remote_client.AgentId)) + { + //Sale Flag + tempByte = Convert.ToByte(tempByte | LAND_TYPE_IS_FOR_SALE); + } + else if (currentParcelBlock.landData.ownerID == LLUUID.Zero) + { + //Public Flag + tempByte = Convert.ToByte(tempByte | LAND_TYPE_PUBLIC); + } + else + { + //Other Flag + tempByte = Convert.ToByte(tempByte | LAND_TYPE_OWNED_BY_OTHER); + } + + + //Now for border control + if (x == 0) + { + tempByte = Convert.ToByte(tempByte | LAND_FLAG_PROPERTY_BORDER_WEST); + } + else if (getLandObject((x - 1) * 4, y * 4) != currentParcelBlock) + { + tempByte = Convert.ToByte(tempByte | LAND_FLAG_PROPERTY_BORDER_WEST); + } + + if (y == 0) + { + tempByte = Convert.ToByte(tempByte | LAND_FLAG_PROPERTY_BORDER_SOUTH); + } + else if (getLandObject(x * 4, (y - 1) * 4) != currentParcelBlock) + { + tempByte = Convert.ToByte(tempByte | LAND_FLAG_PROPERTY_BORDER_SOUTH); + } + + byteArray[byteArrayCount] = tempByte; + byteArrayCount++; + if (byteArrayCount >= LAND_BLOCKS_PER_PACKET) + { + byteArrayCount = 0; + packet = new ParcelOverlayPacket(); + packet.ParcelData.Data = byteArray; + packet.ParcelData.SequenceID = sequenceID; + remote_client.OutPacket((Packet)packet); + sequenceID++; + byteArray = new byte[LAND_BLOCKS_PER_PACKET]; + } + } + } + + + } + + public void handleParcelPropertiesRequest(int start_x, int start_y, int end_x, int end_y, int sequence_id, bool snap_selection, IClientAPI remote_client) + { + //Get the land objects within the bounds + List temp = new List(); + int x, y, i; + int inc_x = end_x - start_x; + int inc_y = end_y - start_y; + for (x = 0; x < inc_x; x++) + { + for (y = 0; y < inc_y; y++) + { + Land currentParcel = getLandObject(start_x + x, start_y + y); + if (!temp.Contains(currentParcel)) + { + currentParcel.forceUpdateLandInfo(); + temp.Add(currentParcel); + } + } + } + + int requestResult = LAND_RESULT_SINGLE; + if (temp.Count > 1) + { + requestResult = LAND_RESULT_MULTIPLE; + } + + for (i = 0; i < temp.Count; i++) + { + temp[i].sendLandProperties(sequence_id, snap_selection, requestResult, remote_client); + } + + + sendParcelOverlay(remote_client); + } + + public void handleParcelPropertiesUpdateRequest(ParcelPropertiesUpdatePacket packet, IClientAPI remote_client) + { + if (landList.ContainsKey(packet.ParcelData.LocalID)) + { + landList[packet.ParcelData.LocalID].updateLandProperties(packet, remote_client); + } + } + public void handleParcelDivideRequest(int west, int south, int east, int north, IClientAPI remote_client) + { + subdivide(west, south, east, north, remote_client.AgentId); + } + public void handleParcelJoinRequest(int west, int south, int east, int north, IClientAPI remote_client) + { + join(west, south, east, north, remote_client.AgentId); + + } + + public void handleParcelSelectObjectsRequest(int local_id, int request_type, IClientAPI remote_client) + { + landList[local_id].sendForceObjectSelect(local_id, request_type, remote_client); + } + + public void handleParcelObjectOwnersRequest(int local_id, IClientAPI remote_client) + { + landList[local_id].sendLandObjectOwners(remote_client); + } + #endregion + + /// + /// Resets the sim to the default land object (full sim piece of land owned by the default user) + /// + public void resetSimLandObjects() + { + //Remove all the land objects in the sim and add a blank, full sim land object set to public + landList.Clear(); + lastLandLocalID = START_LAND_LOCAL_ID - 1; + landIDList.Initialize(); + + Land fullSimParcel = new Land(LLUUID.Zero, false, m_world); + + fullSimParcel.setLandBitmap(Land.getSquareLandBitmap(0, 0, 256, 256)); + fullSimParcel.landData.ownerID = m_regInfo.MasterAvatarAssignedUUID; + + addLandObject(fullSimParcel); + + } + + + public void handleSignificantClientMovement(IClientAPI remote_client) + { + ScenePresence clientAvatar = m_world.RequestAvatar(remote_client.AgentId); + if (clientAvatar != null) + { + Land over = getLandObject(clientAvatar.Pos.X,clientAvatar.Pos.Y); + if (over != null) + { + over.sendLandProperties(0, false, 0, remote_client); + } + } + } + + public void resetAllLandPrimCounts() + { + foreach (Land p in landList.Values) + { + p.resetLandPrimCounts(); + } + } + public void setPrimsTainted() + { + this.landPrimCountTainted = true; + } + + public void addPrimToLandPrimCounts(SceneObject obj) + { + LLVector3 position = obj.Pos; + Land landUnderPrim = getLandObject(position.X, position.Y); + if (landUnderPrim != null) + { + landUnderPrim.addPrimToCount(obj); + } + } + + public void removePrimFromLandPrimCounts(SceneObject obj) + { + foreach (Land p in landList.Values) + { + p.removePrimFromCount(obj); + } + } + + public void finalizeLandPrimCountUpdate() + { + //Get Simwide prim count for owner + Dictionary> landOwnersAndParcels = new Dictionary>(); + foreach (Land p in landList.Values) + { + if(!landOwnersAndParcels.ContainsKey(p.landData.ownerID)) + { + List tempList = new List(); + tempList.Add(p); + landOwnersAndParcels.Add(p.landData.ownerID,tempList); + } + else + { + landOwnersAndParcels[p.landData.ownerID].Add(p); + } + } + + foreach (LLUUID owner in landOwnersAndParcels.Keys) + { + int simArea = 0; + int simPrims = 0; + foreach (Land p in landOwnersAndParcels[owner]) + { + simArea += p.landData.area; + simPrims += p.landData.ownerPrims + p.landData.otherPrims + p.landData.groupPrims + p.landData.selectedPrims; + } + + foreach (Land p in landOwnersAndParcels[owner]) + { + p.landData.simwideArea = simArea; + p.landData.simwidePrims = simPrims; + } + } + + } + #endregion + } + #endregion + + + + + +}