diff --git a/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs b/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs index 343cdb5c1e..66325b458a 100644 --- a/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs @@ -182,6 +182,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Combat.CombatModule try { ILandObject obj = avatar.Scene.LandChannel.GetLandObject(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y); + + if (obj == null) + return; + if ((obj.LandData.Flags & (uint)ParcelFlags.AllowDamage) != 0 || avatar.Scene.RegionInfo.RegionSettings.AllowDamage) { diff --git a/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs index f3e757acf3..bb2a25813d 100644 --- a/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs @@ -657,9 +657,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles /// Enabled. /// public void PickInfoUpdate(IClientAPI remoteClient, UUID pickID, UUID creatorID, bool topPick, string name, string desc, UUID snapshotID, int sortOrder, bool enabled) - { - + { m_log.DebugFormat("[PROFILES]: Start PickInfoUpdate Name: {0} PickId: {1} SnapshotId: {2}", name, pickID.ToString(), snapshotID.ToString()); + UserProfilePick pick = new UserProfilePick(); string serverURI = string.Empty; GetUserProfileServerURI(remoteClient.AgentId, out serverURI); @@ -672,19 +672,29 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles avaPos.Z); string landOwnerName = string.Empty; - ILandObject land = p.Scene.LandChannel.GetLandObject(avaPos.X, avaPos.Y); - if(land.LandData.IsGroupOwned) + ILandObject land = p.Scene.LandChannel.GetLandObject(avaPos.X, avaPos.Y); + + if (land != null) { - IGroupsModule groupMod = p.Scene.RequestModuleInterface(); - UUID groupId = land.LandData.GroupID; - GroupRecord groupRecord = groupMod.GetGroupRecord(groupId); - landOwnerName = groupRecord.GroupName; + if (land.LandData.IsGroupOwned) + { + IGroupsModule groupMod = p.Scene.RequestModuleInterface(); + UUID groupId = land.LandData.GroupID; + GroupRecord groupRecord = groupMod.GetGroupRecord(groupId); + landOwnerName = groupRecord.GroupName; + } + else + { + IUserAccountService accounts = p.Scene.RequestModuleInterface(); + UserAccount user = accounts.GetUserAccount(p.Scene.RegionInfo.ScopeID, land.LandData.OwnerID); + landOwnerName = user.Name; + } } else { - IUserAccountService accounts = p.Scene.RequestModuleInterface(); - UserAccount user = accounts.GetUserAccount(p.Scene.RegionInfo.ScopeID, land.LandData.OwnerID); - landOwnerName = user.Name; + m_log.WarnFormat( + "[PROFILES]: PickInfoUpdate found no parcel info at {0},{1} in {2}", + avaPos.X, avaPos.Y, p.Scene.Name); } pick.PickId = pickID; diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs index 99db7ff5c0..5c20899a6f 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs @@ -66,6 +66,11 @@ namespace OpenSim.Region.CoreModules.World.Land private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly string LogHeader = "[LAND MANAGEMENT MODULE]"; + /// + /// Minimum land unit size in region co-ordinates. + /// + public const int LandUnit = 4; + private static readonly string remoteParcelRequestPath = "0009/"; private LandChannel landChannel; @@ -79,7 +84,6 @@ namespace OpenSim.Region.CoreModules.World.Land /// Local land ids at specified region co-ordinates (region size / 4) /// private int[,] m_landIDList; - private const int landUnit = 4; /// /// Land objects keyed by local id @@ -112,7 +116,7 @@ namespace OpenSim.Region.CoreModules.World.Land public void AddRegion(Scene scene) { m_scene = scene; - m_landIDList = new int[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit]; + m_landIDList = new int[m_scene.RegionInfo.RegionSizeX / LandUnit, m_scene.RegionInfo.RegionSizeY / LandUnit]; m_landIDList.Initialize(); landChannel = new LandChannel(scene, this); @@ -296,7 +300,7 @@ namespace OpenSim.Region.CoreModules.World.Land { m_landList.Clear(); m_lastLandLocalID = LandChannel.START_LAND_LOCAL_ID - 1; - m_landIDList = new int[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit]; + m_landIDList = new int[m_scene.RegionInfo.RegionSizeX / LandUnit, m_scene.RegionInfo.RegionSizeY / LandUnit]; m_landIDList.Initialize(); } } @@ -590,7 +594,10 @@ namespace OpenSim.Region.CoreModules.World.Land /// /// Adds a land object to the stored list and adds them to the landIDList to what they own /// - /// The land object being added + /// + /// The land object being added. + /// Will return null if this overlaps with an existing parcel that has not had its bitmap adjusted. + /// public ILandObject AddLandObject(ILandObject land) { ILandObject new_land = land.Copy(); @@ -602,7 +609,7 @@ namespace OpenSim.Region.CoreModules.World.Land lock (m_landList) { - int newLandLocalID = ++m_lastLandLocalID; + int newLandLocalID = m_lastLandLocalID + 1; new_land.LandData.LocalID = newLandLocalID; bool[,] landBitmap = new_land.GetLandBitmap(); @@ -617,6 +624,33 @@ namespace OpenSim.Region.CoreModules.World.Land } else { + // If other land objects still believe that they occupy any parts of the same space, + // then do not allow the add to proceed. + for (int x = 0; x < landBitmap.GetLength(0); x++) + { + for (int y = 0; y < landBitmap.GetLength(1); y++) + { + if (landBitmap[x, y]) + { + int lastRecordedLandId = m_landIDList[x, y]; + + if (lastRecordedLandId > 0) + { + ILandObject lastRecordedLo = m_landList[lastRecordedLandId]; + + if (lastRecordedLo.LandBitmap[x, y]) + { + m_log.ErrorFormat( + "{0}: Cannot add parcel \"{1}\", local ID {2} at tile {3},{4} because this is still occupied by parcel \"{5}\", local ID {6}.", + LogHeader, new_land.LandData.Name, new_land.LandData.LocalID, x, y, lastRecordedLo.LandData.Name, lastRecordedLo.LandData.LocalID); + + return null; + } + } + } + } + } + for (int x = 0; x < landBitmap.GetLength(0); x++) { for (int y = 0; y < landBitmap.GetLength(1); y++) @@ -626,7 +660,7 @@ namespace OpenSim.Region.CoreModules.World.Land // m_log.DebugFormat( // "[LAND MANAGEMENT MODULE]: Registering parcel {0} for land co-ord ({1}, {2}) on {3}", // new_land.LandData.Name, x, y, m_scene.RegionInfo.RegionName); - + m_landIDList[x, y] = newLandLocalID; } } @@ -634,10 +668,12 @@ namespace OpenSim.Region.CoreModules.World.Land } m_landList.Add(newLandLocalID, new_land); + m_lastLandLocalID++; } new_land.ForceUpdateLandInfo(); m_scene.EventManager.TriggerLandObjectAdded(new_land); + return new_land; } @@ -801,8 +837,18 @@ namespace OpenSim.Region.CoreModules.World.Land return GetLandObject(x, y, false /* returnNullIfLandObjectNotFound */); } - // Given a region position, return the parcel land object for that location - private ILandObject GetLandObject(int x, int y, bool returnNullIfLandObjectNotFound) + /// + /// Given a region position, return the parcel land object for that location + /// + /// + /// The land object. + /// + /// + /// + /// + /// Return null if the land object requested is not within the region's bounds. + /// + private ILandObject GetLandObject(int x, int y, bool returnNullIfLandObjectOutsideBounds) { ILandObject ret = null; @@ -810,7 +856,7 @@ namespace OpenSim.Region.CoreModules.World.Land { // These exceptions here will cause a lot of complaints from the users specifically because // they happen every time at border crossings - if (returnNullIfLandObjectNotFound) + if (returnNullIfLandObjectOutsideBounds) return null; else throw new Exception( @@ -823,7 +869,7 @@ namespace OpenSim.Region.CoreModules.World.Land { try { - int landID = m_landIDList[x / landUnit, y / landUnit]; + int landID = m_landIDList[x / LandUnit, y / LandUnit]; if (landID == 0) { // Zero is the uninitialized value saying there is no parcel for this location. @@ -856,7 +902,11 @@ namespace OpenSim.Region.CoreModules.World.Land newLand.SetLandBitmap(CreateBitmapForID(0)); newLand.LandData.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner; newLand.LandData.ClaimDate = Util.UnixTimeSinceEpoch(); - AddLandObject(newLand); + newLand = AddLandObject(newLand); + + if (newLand == null) + return null; + landID = m_lastLandLocalID; } } @@ -868,16 +918,19 @@ namespace OpenSim.Region.CoreModules.World.Land m_log.ErrorFormat( "{0} GetLandObject: Tried to retrieve land object from out of bounds co-ordinate ({1},{2}) in {3}. landListSize=({4},{5})", LogHeader, x, y, m_scene.RegionInfo.RegionName, m_landIDList.GetLength(0), m_landIDList.GetLength(1)); + return null; } catch { m_log.ErrorFormat( "{0} GetLandObject: LandID not in landlist. XY=<{1},{2}> in {3}. landID[x,y]={4}", - LogHeader, x, y, m_scene.RegionInfo.RegionName, m_landIDList[x/landUnit, y/landUnit]); + LogHeader, x, y, m_scene.RegionInfo.RegionName, m_landIDList[x/LandUnit, y/LandUnit]); + return null; } } + return ret; } @@ -1062,8 +1115,12 @@ namespace OpenSim.Region.CoreModules.World.Land //Now add the new land object ILandObject result = AddLandObject(newLand); - UpdateLandObject(startLandObject.LandData.LocalID, startLandObject.LandData); - result.SendLandUpdateToAvatarsOverMe(); + + if (result != null) + { + UpdateLandObject(startLandObject.LandData.LocalID, startLandObject.LandData); + result.SendLandUpdateToAvatarsOverMe(); + } } /// @@ -1157,11 +1214,11 @@ namespace OpenSim.Region.CoreModules.World.Land int sequenceID = 0; // Layer data is in landUnit (4m) chunks - for (int y = 0; y < m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize * (Constants.TerrainPatchSize / landUnit); y++) + for (int y = 0; y < m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize * (Constants.TerrainPatchSize / LandUnit); y++) { - for (int x = 0; x < m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize * (Constants.TerrainPatchSize / landUnit); x++) + for (int x = 0; x < m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize * (Constants.TerrainPatchSize / LandUnit); x++) { - byteArray[byteArrayCount] = BuildLayerByte(GetLandObject(x * landUnit, y * landUnit), x, y, remote_client); + byteArray[byteArrayCount] = BuildLayerByte(GetLandObject(x * LandUnit, y * LandUnit), x, y, remote_client); byteArrayCount++; if (byteArrayCount >= LAND_BLOCKS_PER_PACKET) { @@ -1214,11 +1271,11 @@ namespace OpenSim.Region.CoreModules.World.Land ILandObject southParcel = null; if (x > 0) { - westParcel = GetLandObject((x - 1) * landUnit, y * landUnit); + westParcel = GetLandObject((x - 1) * LandUnit, y * LandUnit); } if (y > 0) { - southParcel = GetLandObject(x * landUnit, (y - 1) * landUnit); + southParcel = GetLandObject(x * LandUnit, (y - 1) * LandUnit); } if (x == 0) diff --git a/OpenSim/Region/CoreModules/World/Land/Tests/LandManagementModuleTests.cs b/OpenSim/Region/CoreModules/World/Land/Tests/LandManagementModuleTests.cs new file mode 100644 index 0000000000..a886e33d3f --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Land/Tests/LandManagementModuleTests.cs @@ -0,0 +1,114 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 OpenSimulator 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 NUnit.Framework; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Tests.Common; +using OpenSim.Tests.Common.Mock; + +namespace OpenSim.Region.CoreModules.World.Land.Tests +{ + public class LandManagementModuleTests + { + [Test] + public void TestAddLandObject() + { + TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); + + UUID userId = TestHelpers.ParseTail(0x1); + + LandManagementModule lmm = new LandManagementModule(); + Scene scene = new SceneHelpers().SetupScene(); + SceneHelpers.SetupSceneModules(scene, lmm); + + ILandObject lo = new LandObject(userId, false, scene); + lo.LandData.Name = "lo1"; + lo.SetLandBitmap( + lo.GetSquareLandBitmap(0, 0, (int)Constants.RegionSize, (int)Constants.RegionSize)); + lo = lmm.AddLandObject(lo); + + // TODO: Should add asserts to check that land object was added properly. + + // At the moment, this test just makes sure that we can't add a land object that overlaps the areas that + // the first still holds. + ILandObject lo2 = new LandObject(userId, false, scene); + lo2.SetLandBitmap( + lo2.GetSquareLandBitmap(0, 0, (int)Constants.RegionSize, (int)Constants.RegionSize)); + lo2.LandData.Name = "lo2"; + lo2 = lmm.AddLandObject(lo2); + + { + ILandObject loAtCoord = lmm.GetLandObject(0, 0); + Assert.That(loAtCoord.LandData.LocalID, Is.EqualTo(lo.LandData.LocalID)); + Assert.That(loAtCoord.LandData.GlobalID, Is.EqualTo(lo.LandData.GlobalID)); + } + + { + ILandObject loAtCoord = lmm.GetLandObject((int)Constants.RegionSize - 1, ((int)Constants.RegionSize - 1)); + Assert.That(loAtCoord.LandData.LocalID, Is.EqualTo(lo.LandData.LocalID)); + Assert.That(loAtCoord.LandData.GlobalID, Is.EqualTo(lo.LandData.GlobalID)); + } + } + + [Test] + public void TestSubdivide() + { + TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); + + UUID userId = TestHelpers.ParseTail(0x1); + + LandManagementModule lmm = new LandManagementModule(); + Scene scene = new SceneHelpers().SetupScene(); + SceneHelpers.SetupSceneModules(scene, lmm); + + ILandObject lo = new LandObject(userId, false, scene); + lo.LandData.Name = "lo1"; + lo.SetLandBitmap( + lo.GetSquareLandBitmap(0, 0, (int)Constants.RegionSize, (int)Constants.RegionSize)); + lo = lmm.AddLandObject(lo); + + lmm.Subdivide(0, 0, LandManagementModule.LandUnit, LandManagementModule.LandUnit, userId); + + { + ILandObject loAtCoord = lmm.GetLandObject(0, 0); + Assert.That(loAtCoord.LandData.LocalID, Is.Not.EqualTo(lo.LandData.LocalID)); + Assert.That(loAtCoord.LandData.GlobalID, Is.Not.EqualTo(lo.LandData.GlobalID)); + } + + { + ILandObject loAtCoord = lmm.GetLandObject(LandManagementModule.LandUnit, LandManagementModule.LandUnit); + Assert.That(loAtCoord.LandData.LocalID, Is.EqualTo(lo.LandData.LocalID)); + Assert.That(loAtCoord.LandData.GlobalID, Is.EqualTo(lo.LandData.GlobalID)); + } + } + } +} \ No newline at end of file