diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index e71e81c804..2e128a708a 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -1157,7 +1157,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController // Set home position GridRegion home = scene.GridService.GetRegionByPosition(scopeID, - (int)(regionXLocation * Constants.RegionSize), (int)(regionYLocation * Constants.RegionSize)); + (int)Util.RegionToWorldLoc(regionXLocation), (int)Util.RegionToWorldLoc(regionYLocation)); if (null == home) { m_log.WarnFormat("[RADMIN]: Unable to set home region for newly created user account {0} {1}", firstName, lastName); @@ -1387,7 +1387,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController if ((null != regionXLocation) && (null != regionYLocation)) { GridRegion home = scene.GridService.GetRegionByPosition(scopeID, - (int)(regionXLocation * Constants.RegionSize), (int)(regionYLocation * Constants.RegionSize)); + (int)Util.RegionToWorldLoc((uint)regionXLocation), (int)Util.RegionToWorldLoc((uint)regionYLocation)); if (null == home) { m_log.WarnFormat("[RADMIN]: Unable to set home region for updated user account {0} {1}", firstName, lastName); } else { @@ -3118,7 +3118,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController // Set home position GridRegion home = scene.GridService.GetRegionByPosition(scopeID, - (int)(regionXLocation * Constants.RegionSize), (int)(regionYLocation * Constants.RegionSize)); + (int)Util.RegionToWorldLoc(regionXLocation), (int)Util.RegionToWorldLoc(regionYLocation)); if (null == home) { m_log.WarnFormat("[RADMIN]: Unable to set home region for newly created user account {0} {1}", names[0], names[1]); } else { diff --git a/OpenSim/Data/IRegionData.cs b/OpenSim/Data/IRegionData.cs index 463c621859..ca9b32716c 100644 --- a/OpenSim/Data/IRegionData.cs +++ b/OpenSim/Data/IRegionData.cs @@ -52,14 +52,14 @@ namespace OpenSim.Data public int sizeY; /// - /// Return the x-coordinate of this region. + /// Return the x-coordinate of this region in region units. /// - public int coordX { get { return posX / (int)Constants.RegionSize; } } + public int coordX { get { return (int)Util.WorldToRegionLoc((uint)posX); } } /// - /// Return the y-coordinate of this region. + /// Return the y-coordinate of this region in region units. /// - public int coordY { get { return posY / (int)Constants.RegionSize; } } + public int coordY { get { return (int)Util.WorldToRegionLoc((uint)posY); } } public Dictionary Data; } diff --git a/OpenSim/Data/MSSQL/MSSQLSimulationData.cs b/OpenSim/Data/MSSQL/MSSQLSimulationData.cs index 0d09be6361..145b9c0aad 100644 --- a/OpenSim/Data/MSSQL/MSSQLSimulationData.cs +++ b/OpenSim/Data/MSSQL/MSSQLSimulationData.cs @@ -530,43 +530,52 @@ ELSE /// public double[,] LoadTerrain(UUID regionID) { - double[,] terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; - terrain.Initialize(); + double[,] ret = null; + TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); + if (terrData != null) + ret = terrData.GetDoubles(); + return ret; + } + + // Returns 'null' if region not found + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + TerrainData terrData = null; string sql = "select top 1 RegionUUID, Revision, Heightfield from terrain where RegionUUID = @RegionUUID order by Revision desc"; using (SqlConnection conn = new SqlConnection(m_connectionString)) - using (SqlCommand cmd = new SqlCommand(sql, conn)) { - // MySqlParameter param = new MySqlParameter(); - cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID)); - conn.Open(); - using (SqlDataReader reader = cmd.ExecuteReader()) + using (SqlCommand cmd = new SqlCommand(sql, conn)) { - int rev; - if (reader.Read()) + // MySqlParameter param = new MySqlParameter(); + cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID)); + conn.Open(); + using (SqlDataReader reader = cmd.ExecuteReader()) { - MemoryStream str = new MemoryStream((byte[])reader["Heightfield"]); - BinaryReader br = new BinaryReader(str); - for (int x = 0; x < (int)Constants.RegionSize; x++) + if (reader.Read()) { - for (int y = 0; y < (int)Constants.RegionSize; y++) - { - terrain[x, y] = br.ReadDouble(); - } + int rev = (int)reader["Revision"]; + byte[] blob = (byte[])reader["Heightfield"]; + terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob); } - rev = (int)reader["Revision"]; + else + { + _Log.Info("[REGION DB]: No terrain found for region"); + return null; + } + _Log.Info("[REGION DB]: Loaded terrain"); } - else - { - _Log.Info("[REGION DB]: No terrain found for region"); - return null; - } - _Log.Info("[REGION DB]: Loaded terrain revision r" + rev); } } - return terrain; + return terrData; + } + + // Legacy entry point for when terrain was always a 256x256 hieghtmap + public void StoreTerrain(double[,] ter, UUID regionID) + { + StoreTerrain(new HeightmapTerrainData(ter), regionID); } /// @@ -574,10 +583,8 @@ ELSE /// /// terrain map data. /// regionID. - public void StoreTerrain(double[,] terrain, UUID regionID) + public void StoreTerrain(TerrainData terrData, UUID regionID) { - int revision = Util.UnixTimeSinceEpoch(); - //Delete old terrain map string sql = "delete from terrain where RegionUUID=@RegionUUID"; using (SqlConnection conn = new SqlConnection(m_connectionString)) @@ -590,17 +597,23 @@ ELSE sql = "insert into terrain(RegionUUID, Revision, Heightfield) values(@RegionUUID, @Revision, @Heightfield)"; + int terrainDBRevision; + Array terrainDBblob; + terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob); + using (SqlConnection conn = new SqlConnection(m_connectionString)) - using (SqlCommand cmd = new SqlCommand(sql, conn)) { - cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID)); - cmd.Parameters.Add(_Database.CreateParameter("@Revision", revision)); - cmd.Parameters.Add(_Database.CreateParameter("@Heightfield", serializeTerrain(terrain))); - conn.Open(); - cmd.ExecuteNonQuery(); + using (SqlCommand cmd = new SqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID)); + cmd.Parameters.Add(_Database.CreateParameter("@Revision", terrainDBRevision)); + cmd.Parameters.Add(_Database.CreateParameter("@Heightfield", terrainDBblob)); + conn.Open(); + cmd.ExecuteNonQuery(); + } } - _Log.Info("[REGION DB]: Stored terrain revision r " + revision); + _Log.Info("[REGION DB]: Stored terrain"); } /// @@ -1344,6 +1357,7 @@ VALUES #region Private Methods + /* /// /// Serializes the terrain data for storage in DB. /// @@ -1367,6 +1381,7 @@ VALUES return str.ToArray(); } + */ /// /// Stores new regionsettings. diff --git a/OpenSim/Data/MySQL/MySQLSimulationData.cs b/OpenSim/Data/MySQL/MySQLSimulationData.cs index cc844aef76..f910e443fc 100644 --- a/OpenSim/Data/MySQL/MySQLSimulationData.cs +++ b/OpenSim/Data/MySQL/MySQLSimulationData.cs @@ -48,8 +48,18 @@ namespace OpenSim.Data.MySQL public class MySQLSimulationData : ISimulationDataStore { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static string LogHeader = "[REGION DB MYSQL]"; private string m_connectionString; + + /// + /// This lock was being used to serialize database operations when the connection was shared, but this has + /// been unnecessary for a long time after we switched to using MySQL's underlying connection pooling instead. + /// FIXME: However, the locks remain in many places since they are effectively providing a level of + /// transactionality. This should be replaced by more efficient database transactions which would not require + /// unrelated operations to block each other or unrelated operations on the same tables from blocking each + /// other. + /// private object m_dbLock = new object(); protected virtual Assembly Assembly @@ -91,7 +101,7 @@ namespace OpenSim.Data.MySQL } catch (Exception e) { - m_log.Error("[REGION DB]: MySQL error in ExecuteReader: " + e.Message); + m_log.ErrorFormat("{0} MySQL error in ExecuteReader: {1}", LogHeader, e); throw; } @@ -574,12 +584,16 @@ namespace OpenSim.Data.MySQL } } - public virtual void StoreTerrain(double[,] ter, UUID regionID) + // Legacy entry point for when terrain was always a 256x256 hieghtmap + public void StoreTerrain(double[,] ter, UUID regionID) + { + StoreTerrain(new HeightmapTerrainData(ter), regionID); + } + + public void StoreTerrain(TerrainData terrData, UUID regionID) { Util.FireAndForget(delegate(object x) { - double[,] oldTerrain = LoadTerrain(regionID); - m_log.Info("[REGION DB]: Storing terrain"); lock (m_dbLock) @@ -601,8 +615,12 @@ namespace OpenSim.Data.MySQL "Revision, Heightfield) values (?RegionUUID, " + "1, ?Heightfield)"; - cmd2.Parameters.AddWithValue("RegionUUID", regionID.ToString()); - cmd2.Parameters.AddWithValue("Heightfield", SerializeTerrain(ter, oldTerrain)); + int terrainDBRevision; + Array terrainDBblob; + terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob); + + cmd2.Parameters.AddWithValue("Revision", terrainDBRevision); + cmd2.Parameters.AddWithValue("Heightfield", terrainDBblob); ExecuteNonQuery(cmd); ExecuteNonQuery(cmd2); @@ -618,9 +636,20 @@ namespace OpenSim.Data.MySQL }); } + // Legacy region loading public virtual double[,] LoadTerrain(UUID regionID) { - double[,] terrain = null; + double[,] ret = null; + TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); + if (terrData != null) + ret = terrData.GetDoubles(); + return ret; + } + + // Returns 'null' if region not found + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + TerrainData terrData = null; lock (m_dbLock) { @@ -640,32 +669,15 @@ namespace OpenSim.Data.MySQL while (reader.Read()) { int rev = Convert.ToInt32(reader["Revision"]); - - terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; - terrain.Initialize(); - - using (MemoryStream mstr = new MemoryStream((byte[])reader["Heightfield"])) - { - using (BinaryReader br = new BinaryReader(mstr)) - { - for (int x = 0; x < (int)Constants.RegionSize; x++) - { - for (int y = 0; y < (int)Constants.RegionSize; y++) - { - terrain[x, y] = br.ReadDouble(); - } - } - } - - m_log.InfoFormat("[REGION DB]: Loaded terrain revision r{0}", rev); - } + byte[] blob = (byte[])reader["Heightfield"]; + terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob); } } } } } - return terrain; + return terrData; } public virtual void RemoveLandObject(UUID globalID) diff --git a/OpenSim/Data/Null/NullSimulationData.cs b/OpenSim/Data/Null/NullSimulationData.cs index 15824a9ebe..339e7f45b0 100644 --- a/OpenSim/Data/Null/NullSimulationData.cs +++ b/OpenSim/Data/Null/NullSimulationData.cs @@ -132,15 +132,33 @@ namespace OpenSim.Data.Null return new List(); } - Dictionary m_terrains = new Dictionary(); - public void StoreTerrain(double[,] ter, UUID regionID) + Dictionary m_terrains = new Dictionary(); + public void StoreTerrain(TerrainData ter, UUID regionID) { if (m_terrains.ContainsKey(regionID)) m_terrains.Remove(regionID); m_terrains.Add(regionID, ter); } + // Legacy. Just don't do this. + public void StoreTerrain(double[,] ter, UUID regionID) + { + TerrainData terrData = new HeightmapTerrainData(ter); + StoreTerrain(terrData, regionID); + } + + // Legacy. Just don't do this. + // Returns 'null' if region not found public double[,] LoadTerrain(UUID regionID) + { + if (m_terrains.ContainsKey(regionID)) + { + return m_terrains[regionID].GetDoubles(); + } + return null; + } + + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) { if (m_terrains.ContainsKey(regionID)) { diff --git a/OpenSim/Data/PGSQL/PGSQLSimulationData.cs b/OpenSim/Data/PGSQL/PGSQLSimulationData.cs index 3243635a3a..cd02e058bc 100644 --- a/OpenSim/Data/PGSQL/PGSQLSimulationData.cs +++ b/OpenSim/Data/PGSQL/PGSQLSimulationData.cs @@ -46,6 +46,7 @@ namespace OpenSim.Data.PGSQL public class PGSQLSimulationData : ISimulationDataStore { private const string _migrationStore = "RegionStore"; + private const string LogHeader = "[REGION DB PGSQL]"; // private static FileSystemDataStore Instance = new FileSystemDataStore(); private static readonly ILog _Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); @@ -523,8 +524,17 @@ namespace OpenSim.Data.PGSQL /// public double[,] LoadTerrain(UUID regionID) { - double[,] terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; - terrain.Initialize(); + double[,] ret = null; + TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); + if (terrData != null) + ret = terrData.GetDoubles(); + return ret; + } + + // Returns 'null' if region not found + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + TerrainData terrData = null; string sql = @"select ""RegionUUID"", ""Revision"", ""Heightfield"" from terrain where ""RegionUUID"" = :RegionUUID order by ""Revision"" desc limit 1; "; @@ -540,16 +550,9 @@ namespace OpenSim.Data.PGSQL int rev; if (reader.Read()) { - MemoryStream str = new MemoryStream((byte[])reader["Heightfield"]); - BinaryReader br = new BinaryReader(str); - for (int x = 0; x < (int)Constants.RegionSize; x++) - { - for (int y = 0; y < (int)Constants.RegionSize; y++) - { - terrain[x, y] = br.ReadDouble(); - } - } - rev = (int)reader["Revision"]; + rev = Convert.ToInt32(reader["Revision"]); + byte[] blob = (byte[])reader["Heightfield"]; + terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob); } else { @@ -560,7 +563,13 @@ namespace OpenSim.Data.PGSQL } } - return terrain; + return terrData; + } + + // Legacy entry point for when terrain was always a 256x256 heightmap + public void StoreTerrain(double[,] terrain, UUID regionID) + { + StoreTerrain(new HeightmapTerrainData(terrain), regionID); } /// @@ -568,35 +577,38 @@ namespace OpenSim.Data.PGSQL /// /// terrain map data. /// regionID. - public void StoreTerrain(double[,] terrain, UUID regionID) + public void StoreTerrain(TerrainData terrData, UUID regionID) { - int revision = Util.UnixTimeSinceEpoch(); - //Delete old terrain map string sql = @"delete from terrain where ""RegionUUID""=:RegionUUID"; using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) - using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) { - cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); - conn.Open(); - cmd.ExecuteNonQuery(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } } - - _Log.Info("[REGION DB]: Deleted terrain revision r " + revision); + int terrainDBRevision; + Array terrainDBblob; + terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob); sql = @"insert into terrain(""RegionUUID"", ""Revision"", ""Heightfield"") values(:RegionUUID, :Revision, :Heightfield)"; using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) - using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) { - cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); - cmd.Parameters.Add(_Database.CreateParameter("Revision", revision)); - cmd.Parameters.Add(_Database.CreateParameter("Heightfield", serializeTerrain(terrain))); - conn.Open(); - cmd.ExecuteNonQuery(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); + cmd.Parameters.Add(_Database.CreateParameter("Revision", terrainDBRevision)); + cmd.Parameters.Add(_Database.CreateParameter("Heightfield", terrainDBblob)); + conn.Open(); + cmd.ExecuteNonQuery(); + } } - _Log.Info("[REGION DB]: Stored terrain revision r " + revision); + _Log.Info("[REGION DB]: Stored terrain revision r " + terrainDBRevision); } /// @@ -1349,6 +1361,7 @@ namespace OpenSim.Data.PGSQL #region Private Methods + /* /// /// Serializes the terrain data for storage in DB. /// @@ -1372,6 +1385,7 @@ namespace OpenSim.Data.PGSQL return str.ToArray(); } + */ /// /// Stores new regionsettings. diff --git a/OpenSim/Data/SQLite/SQLiteSimulationData.cs b/OpenSim/Data/SQLite/SQLiteSimulationData.cs index 4d6a80ad22..d28c2277e0 100644 --- a/OpenSim/Data/SQLite/SQLiteSimulationData.cs +++ b/OpenSim/Data/SQLite/SQLiteSimulationData.cs @@ -51,6 +51,7 @@ namespace OpenSim.Data.SQLite public class SQLiteSimulationData : ISimulationDataStore { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[REGION DB SQLLITE]"; private const string primSelect = "select * from prims"; private const string shapeSelect = "select * from primshapes"; @@ -819,12 +820,18 @@ namespace OpenSim.Data.SQLite prim.Inventory.RestoreInventoryItems(inventory); } + // Legacy entry point for when terrain was always a 256x256 hieghtmap + public void StoreTerrain(double[,] ter, UUID regionID) + { + StoreTerrain(new HeightmapTerrainData(ter), regionID); + } + /// /// Store a terrain revision in region storage /// /// terrain heightfield /// region UUID - public void StoreTerrain(double[,] ter, UUID regionID) + public void StoreTerrain(TerrainData terrData, UUID regionID) { lock (ds) { @@ -853,11 +860,17 @@ namespace OpenSim.Data.SQLite String sql = "insert into terrain(RegionUUID, Revision, Heightfield)" + " values(:RegionUUID, :Revision, :Heightfield)"; + int terrainDBRevision; + Array terrainDBblob; + terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob); + + m_log.DebugFormat("{0} Storing terrain revision r {1}", LogHeader, terrainDBRevision); + using (SqliteCommand cmd = new SqliteCommand(sql, m_conn)) { cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString())); - cmd.Parameters.Add(new SqliteParameter(":Revision", revision)); - cmd.Parameters.Add(new SqliteParameter(":Heightfield", serializeTerrain(ter))); + cmd.Parameters.Add(new SqliteParameter(":Revision", terrainDBRevision)); + cmd.Parameters.Add(new SqliteParameter(":Heightfield", terrainDBblob)); cmd.ExecuteNonQuery(); } } @@ -870,11 +883,20 @@ namespace OpenSim.Data.SQLite /// Heightfield data public double[,] LoadTerrain(UUID regionID) { + double[,] ret = null; + TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); + if (terrData != null) + ret = terrData.GetDoubles(); + return ret; + } + + // Returns 'null' if region not found + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + TerrainData terrData = null; + lock (ds) { - double[,] terret = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; - terret.Initialize(); - String sql = "select RegionUUID, Revision, Heightfield from terrain" + " where RegionUUID=:RegionUUID order by Revision desc"; @@ -887,21 +909,9 @@ namespace OpenSim.Data.SQLite int rev = 0; if (row.Read()) { - // TODO: put this into a function - using (MemoryStream str = new MemoryStream((byte[])row["Heightfield"])) - { - using (BinaryReader br = new BinaryReader(str)) - { - for (int x = 0; x < (int)Constants.RegionSize; x++) - { - for (int y = 0; y < (int)Constants.RegionSize; y++) - { - terret[x, y] = br.ReadDouble(); - } - } - } - } rev = Convert.ToInt32(row["Revision"]); + byte[] blob = (byte[])row["Heightfield"]; + terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob); } else { @@ -912,8 +922,8 @@ namespace OpenSim.Data.SQLite m_log.Debug("[SQLITE REGION DB]: Loaded terrain revision r" + rev.ToString()); } } - return terret; } + return terrData; } public void RemoveLandObject(UUID globalID) @@ -2016,6 +2026,7 @@ namespace OpenSim.Data.SQLite return entry; } + /* /// /// /// @@ -2033,6 +2044,7 @@ namespace OpenSim.Data.SQLite return str.ToArray(); } + */ // private void fillTerrainRow(DataRow row, UUID regionUUID, int rev, double[,] val) // { diff --git a/OpenSim/Framework/MapBlockData.cs b/OpenSim/Framework/MapBlockData.cs index 2298ac57e2..4bee4995f3 100644 --- a/OpenSim/Framework/MapBlockData.cs +++ b/OpenSim/Framework/MapBlockData.cs @@ -27,6 +27,7 @@ using System; using OpenMetaverse; +using OpenMetaverse.StructuredData; namespace OpenSim.Framework { @@ -40,9 +41,26 @@ namespace OpenSim.Framework public byte WaterHeight; public ushort X; public ushort Y; + public ushort SizeX; + public ushort SizeY; public MapBlockData() { } + + public OSDMap ToOSD() + { + OSDMap map = new OSDMap(); + map["X"] = X; + map["Y"] = Y; + map["SizeX"] = SizeX; + map["SizeY"] = SizeY; + map["Name"] = Name; + map["Access"] = Access; + map["RegionFlags"] = RegionFlags; + map["WaterHeight"] = WaterHeight; + map["MapImageID"] = MapImageId; + return map; + } } } diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs index ae2ff639eb..019fffc8ba 100644 --- a/OpenSim/Framework/RegionInfo.cs +++ b/OpenSim/Framework/RegionInfo.cs @@ -149,11 +149,32 @@ namespace OpenSim.Framework public uint WorldLocX = 0; public uint WorldLocY = 0; public uint WorldLocZ = 0; + + /// + /// X dimension of the region. + /// + /// + /// If this is a varregion then the default size set here will be replaced when we load the region config. + /// public uint RegionSizeX = Constants.RegionSize; + + /// + /// X dimension of the region. + /// + /// + /// If this is a varregion then the default size set here will be replaced when we load the region config. + /// public uint RegionSizeY = Constants.RegionSize; + + /// + /// Z dimension of the region. + /// + /// + /// XXX: Unknown if this accounts for regions with negative Z. + /// public uint RegionSizeZ = Constants.RegionHeight; - private Dictionary m_otherSettings = new Dictionary(); + private Dictionary m_extraSettings = new Dictionary(); // Apparently, we're applying the same estatesettings regardless of whether it's local or remote. @@ -506,16 +527,16 @@ namespace OpenSim.Framework { string val; string keylower = key.ToLower(); - if (m_otherSettings.TryGetValue(keylower, out val)) + if (m_extraSettings.TryGetValue(keylower, out val)) return val; m_log.DebugFormat("[RegionInfo] Could not locate value for parameter {0}", key); return null; } - public void SetOtherSetting(string key, string value) + public void SetExtraSetting(string key, string value) { string keylower = key.ToLower(); - m_otherSettings[keylower] = value; + m_extraSettings[keylower] = value; } private void ReadNiniConfig(IConfigSource source, string name) @@ -733,7 +754,7 @@ namespace OpenSim.Framework foreach (String s in allKeys) { - SetOtherSetting(s, config.GetString(s)); + SetExtraSetting(s, config.GetString(s)); } } @@ -804,6 +825,7 @@ namespace OpenSim.Framework if (DataStore != String.Empty) config.Set("Datastore", DataStore); + if (RegionSizeX != Constants.RegionSize || RegionSizeY != Constants.RegionSize) { config.Set("SizeX", RegionSizeX); diff --git a/OpenSim/Framework/TerrainData.cs b/OpenSim/Framework/TerrainData.cs new file mode 100644 index 0000000000..6b1be4e9fa --- /dev/null +++ b/OpenSim/Framework/TerrainData.cs @@ -0,0 +1,464 @@ +/* + * 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 System.Collections.Generic; +using System.IO; +using System.Reflection; + +using OpenMetaverse; + +using log4net; + +namespace OpenSim.Framework +{ + public abstract class TerrainData + { + // Terrain always is a square + public int SizeX { get; protected set; } + public int SizeY { get; protected set; } + public int SizeZ { get; protected set; } + + // A height used when the user doesn't specify anything + public const float DefaultTerrainHeight = 21f; + + public abstract float this[int x, int y] { get; set; } + // Someday terrain will have caves + public abstract float this[int x, int y, int z] { get; set; } + + public abstract bool IsTaintedAt(int xx, int yy); + public abstract bool IsTaintedAt(int xx, int yy, bool clearOnTest); + public abstract void TaintAllTerrain(); + public abstract void ClearTaint(); + + public abstract void ClearLand(); + public abstract void ClearLand(float height); + + // Return a representation of this terrain for storing as a blob in the database. + // Returns 'true' to say blob was stored in the 'out' locations. + public abstract bool GetDatabaseBlob(out int DBFormatRevisionCode, out Array blob); + + // Given a revision code and a blob from the database, create and return the right type of TerrainData. + // The sizes passed are the expected size of the region. The database info will be used to + // initialize the heightmap of that sized region with as much data is in the blob. + // Return created TerrainData or 'null' if unsuccessful. + public static TerrainData CreateFromDatabaseBlobFactory(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob) + { + // For the moment, there is only one implementation class + return new HeightmapTerrainData(pSizeX, pSizeY, pSizeZ, pFormatCode, pBlob); + } + + // return a special compressed representation of the heightmap in ints + public abstract int[] GetCompressedMap(); + public abstract float CompressionFactor { get; } + + public abstract float[] GetFloatsSerialized(); + public abstract double[,] GetDoubles(); + public abstract TerrainData Clone(); + } + + // The terrain is stored in the database as a blob with a 'revision' field. + // Some implementations of terrain storage would fill the revision field with + // the time the terrain was stored. When real revisions were added and this + // feature removed, that left some old entries with the time in the revision + // field. + // Thus, if revision is greater than 'RevisionHigh' then terrain db entry is + // left over and it is presumed to be 'Legacy256'. + // Numbers are arbitrary and are chosen to to reduce possible mis-interpretation. + // If a revision does not match any of these, it is assumed to be Legacy256. + public enum DBTerrainRevision + { + // Terrain is 'double[256,256]' + Legacy256 = 11, + // Terrain is 'int32, int32, float[,]' where the ints are X and Y dimensions + // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256. + Variable2D = 22, + // Terrain is 'int32, int32, int32, int16[]' where the ints are X and Y dimensions + // and third int is the 'compression factor'. The heights are compressed as + // "int compressedHeight = (int)(height * compressionFactor);" + // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256. + Compressed2D = 27, + // A revision that is not listed above or any revision greater than this value is 'Legacy256'. + RevisionHigh = 1234 + } + + // Version of terrain that is a heightmap. + // This should really be 'LLOptimizedHeightmapTerrainData' as it includes knowledge + // of 'patches' which are 16x16 terrain areas which can be sent separately to the viewer. + // The heighmap is kept as an array of integers. The integer values are converted to + // and from floats by TerrainCompressionFactor. + public class HeightmapTerrainData : TerrainData + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static string LogHeader = "[HEIGHTMAP TERRAIN DATA]"; + + // TerrainData.this[x, y] + public override float this[int x, int y] + { + get { return FromCompressedHeight(m_heightmap[x, y]); } + set { + int newVal = ToCompressedHeight(value); + if (m_heightmap[x, y] != newVal) + { + m_heightmap[x, y] = newVal; + m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize] = true; + } + } + } + + // TerrainData.this[x, y, z] + public override float this[int x, int y, int z] + { + get { return this[x, y]; } + set { this[x, y] = value; } + } + + // TerrainData.ClearTaint + public override void ClearTaint() + { + SetAllTaint(false); + } + + // TerrainData.TaintAllTerrain + public override void TaintAllTerrain() + { + SetAllTaint(true); + } + + private void SetAllTaint(bool setting) + { + for (int ii = 0; ii < m_taint.GetLength(0); ii++) + for (int jj = 0; jj < m_taint.GetLength(1); jj++) + m_taint[ii, jj] = setting; + } + + // TerrainData.ClearLand + public override void ClearLand() + { + ClearLand(DefaultTerrainHeight); + } + // TerrainData.ClearLand(float) + public override void ClearLand(float pHeight) + { + int flatHeight = ToCompressedHeight(pHeight); + for (int xx = 0; xx < SizeX; xx++) + for (int yy = 0; yy < SizeY; yy++) + m_heightmap[xx, yy] = flatHeight; + } + + // Return 'true' of the patch that contains these region coordinates has been modified. + // Note that checking the taint clears it. + // There is existing code that relies on this feature. + public override bool IsTaintedAt(int xx, int yy, bool clearOnTest) + { + int tx = xx / Constants.TerrainPatchSize; + int ty = yy / Constants.TerrainPatchSize; + bool ret = m_taint[tx, ty]; + if (ret && clearOnTest) + m_taint[tx, ty] = false; + return ret; + } + + // Old form that clears the taint flag when we check it. + public override bool IsTaintedAt(int xx, int yy) + { + return IsTaintedAt(xx, yy, true /* clearOnTest */); + } + + // TerrainData.GetDatabaseBlob + // The user wants something to store in the database. + public override bool GetDatabaseBlob(out int DBRevisionCode, out Array blob) + { + bool ret = false; + if (SizeX == Constants.RegionSize && SizeY == Constants.RegionSize) + { + DBRevisionCode = (int)DBTerrainRevision.Legacy256; + blob = ToLegacyTerrainSerialization(); + ret = true; + } + else + { + DBRevisionCode = (int)DBTerrainRevision.Compressed2D; + blob = ToCompressedTerrainSerialization(); + ret = true; + } + return ret; + } + + // TerrainData.CompressionFactor + private float m_compressionFactor = 100.0f; + public override float CompressionFactor { get { return m_compressionFactor; } } + + // TerrainData.GetCompressedMap + public override int[] GetCompressedMap() + { + int[] newMap = new int[SizeX * SizeY]; + + int ind = 0; + for (int xx = 0; xx < SizeX; xx++) + for (int yy = 0; yy < SizeY; yy++) + newMap[ind++] = m_heightmap[xx, yy]; + + return newMap; + + } + // TerrainData.Clone + public override TerrainData Clone() + { + HeightmapTerrainData ret = new HeightmapTerrainData(SizeX, SizeY, SizeZ); + ret.m_heightmap = (int[,])this.m_heightmap.Clone(); + return ret; + } + + // TerrainData.GetFloatsSerialized + // This one dimensional version is ordered so height = map[y*sizeX+x]; + // DEPRECATED: don't use this function as it does not retain the dimensions of the terrain + // and the caller will probably do the wrong thing if the terrain is not the legacy 256x256. + public override float[] GetFloatsSerialized() + { + int points = SizeX * SizeY; + float[] heights = new float[points]; + + int idx = 0; + for (int jj = 0; jj < SizeY; jj++) + for (int ii = 0; ii < SizeX; ii++) + { + heights[idx++] = FromCompressedHeight(m_heightmap[ii, jj]); + } + + return heights; + } + + // TerrainData.GetDoubles + public override double[,] GetDoubles() + { + double[,] ret = new double[SizeX, SizeY]; + for (int xx = 0; xx < SizeX; xx++) + for (int yy = 0; yy < SizeY; yy++) + ret[xx, yy] = FromCompressedHeight(m_heightmap[xx, yy]); + + return ret; + } + + + // ============================================================= + + private int[,] m_heightmap; + // Remember subregions of the heightmap that has changed. + private bool[,] m_taint; + + // To save space (especially for large regions), keep the height as a short integer + // that is coded as the float height times the compression factor (usually '100' + // to make for two decimal points). + public int ToCompressedHeight(double pHeight) + { + return (int)(pHeight * CompressionFactor); + } + + public float FromCompressedHeight(int pHeight) + { + return ((float)pHeight) / CompressionFactor; + } + + // To keep with the legacy theme, create an instance of this class based on the + // way terrain used to be passed around. + public HeightmapTerrainData(double[,] pTerrain) + { + SizeX = pTerrain.GetLength(0); + SizeY = pTerrain.GetLength(1); + SizeZ = (int)Constants.RegionHeight; + m_compressionFactor = 100.0f; + + m_heightmap = new int[SizeX, SizeY]; + for (int ii = 0; ii < SizeX; ii++) + { + for (int jj = 0; jj < SizeY; jj++) + { + m_heightmap[ii, jj] = ToCompressedHeight(pTerrain[ii, jj]); + + } + } + // m_log.DebugFormat("{0} new by doubles. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ); + + m_taint = new bool[SizeX / Constants.TerrainPatchSize, SizeY / Constants.TerrainPatchSize]; + ClearTaint(); + } + + // Create underlying structures but don't initialize the heightmap assuming the caller will immediately do that + public HeightmapTerrainData(int pX, int pY, int pZ) + { + SizeX = pX; + SizeY = pY; + SizeZ = pZ; + m_compressionFactor = 100.0f; + m_heightmap = new int[SizeX, SizeY]; + m_taint = new bool[SizeX / Constants.TerrainPatchSize, SizeY / Constants.TerrainPatchSize]; + // m_log.DebugFormat("{0} new by dimensions. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ); + ClearTaint(); + ClearLand(0f); + } + + public HeightmapTerrainData(int[] cmap, float pCompressionFactor, int pX, int pY, int pZ) : this(pX, pY, pZ) + { + m_compressionFactor = pCompressionFactor; + int ind = 0; + for (int xx = 0; xx < SizeX; xx++) + for (int yy = 0; yy < SizeY; yy++) + m_heightmap[xx, yy] = cmap[ind++]; + // m_log.DebugFormat("{0} new by compressed map. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ); + } + + // Create a heighmap from a database blob + public HeightmapTerrainData(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob) : this(pSizeX, pSizeY, pSizeZ) + { + switch ((DBTerrainRevision)pFormatCode) + { + case DBTerrainRevision.Compressed2D: + FromCompressedTerrainSerialization(pBlob); + m_log.DebugFormat("{0} HeightmapTerrainData create from Compressed2D serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY); + break; + default: + FromLegacyTerrainSerialization(pBlob); + m_log.DebugFormat("{0} HeightmapTerrainData create from legacy serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY); + break; + } + } + + // Just create an array of doubles. Presumes the caller implicitly knows the size. + public Array ToLegacyTerrainSerialization() + { + Array ret = null; + + using (MemoryStream str = new MemoryStream((int)Constants.RegionSize * (int)Constants.RegionSize * sizeof(double))) + { + using (BinaryWriter bw = new BinaryWriter(str)) + { + for (int xx = 0; xx < Constants.RegionSize; xx++) + { + for (int yy = 0; yy < Constants.RegionSize; yy++) + { + double height = this[xx, yy]; + if (height == 0.0) + height = double.Epsilon; + bw.Write(height); + } + } + } + ret = str.ToArray(); + } + return ret; + } + + // Just create an array of doubles. Presumes the caller implicitly knows the size. + public void FromLegacyTerrainSerialization(byte[] pBlob) + { + // In case database info doesn't match real terrain size, initialize the whole terrain. + ClearLand(); + + using (MemoryStream mstr = new MemoryStream(pBlob)) + { + using (BinaryReader br = new BinaryReader(mstr)) + { + for (int xx = 0; xx < (int)Constants.RegionSize; xx++) + { + for (int yy = 0; yy < (int)Constants.RegionSize; yy++) + { + float val = (float)br.ReadDouble(); + if (xx < SizeX && yy < SizeY) + m_heightmap[xx, yy] = ToCompressedHeight(val); + } + } + } + ClearTaint(); + } + } + + // See the reader below. + public Array ToCompressedTerrainSerialization() + { + Array ret = null; + using (MemoryStream str = new MemoryStream((3 * sizeof(Int32)) + (SizeX * SizeY * sizeof(Int16)))) + { + using (BinaryWriter bw = new BinaryWriter(str)) + { + bw.Write((Int32)DBTerrainRevision.Compressed2D); + bw.Write((Int32)SizeX); + bw.Write((Int32)SizeY); + bw.Write((Int32)CompressionFactor); + for (int yy = 0; yy < SizeY; yy++) + for (int xx = 0; xx < SizeX; xx++) + { + bw.Write((Int16)m_heightmap[xx, yy]); + } + } + ret = str.ToArray(); + } + return ret; + } + + // Initialize heightmap from blob consisting of: + // int32, int32, int32, int32, int16[] + // where the first int32 is format code, next two int32s are the X and y of heightmap data and + // the forth int is the compression factor for the following int16s + // This is just sets heightmap info. The actual size of the region was set on this instance's + // creation and any heights not initialized by theis blob are set to the default height. + public void FromCompressedTerrainSerialization(byte[] pBlob) + { + Int32 hmFormatCode, hmSizeX, hmSizeY, hmCompressionFactor; + + using (MemoryStream mstr = new MemoryStream(pBlob)) + { + using (BinaryReader br = new BinaryReader(mstr)) + { + hmFormatCode = br.ReadInt32(); + hmSizeX = br.ReadInt32(); + hmSizeY = br.ReadInt32(); + hmCompressionFactor = br.ReadInt32(); + + m_compressionFactor = hmCompressionFactor; + + // In case database info doesn't match real terrain size, initialize the whole terrain. + ClearLand(); + + for (int yy = 0; yy < hmSizeY; yy++) + { + for (int xx = 0; xx < hmSizeX; xx++) + { + Int16 val = br.ReadInt16(); + if (xx < SizeX && yy < SizeY) + m_heightmap[xx, yy] = val; + } + } + } + ClearTaint(); + + m_log.InfoFormat("{0} Read compressed 2d heightmap. Heightmap size=<{1},{2}>. Region size=<{3},{4}>. CompFact={5}", + LogHeader, hmSizeX, hmSizeY, SizeX, SizeY, hmCompressionFactor); + } + } + } +} diff --git a/OpenSim/Framework/UserProfileData.cs b/OpenSim/Framework/UserProfileData.cs index 9bac739589..f7069a56a2 100644 --- a/OpenSim/Framework/UserProfileData.cs +++ b/OpenSim/Framework/UserProfileData.cs @@ -161,14 +161,18 @@ namespace OpenSim.Framework { get { - return Utils.UIntsToLong( - m_homeRegionX * (uint)Constants.RegionSize, m_homeRegionY * (uint)Constants.RegionSize); + return Util.RegionWorldLocToHandle(Util.RegionToWorldLoc(m_homeRegionX), Util.RegionToWorldLoc(m_homeRegionY)); + // return Utils.UIntsToLong( m_homeRegionX * (uint)Constants.RegionSize, m_homeRegionY * (uint)Constants.RegionSize); } set { - m_homeRegionX = (uint) (value >> 40); - m_homeRegionY = (((uint) (value)) >> 8); + uint regionWorldLocX, regionWorldLocY; + Util.RegionHandleToWorldLoc(value, out regionWorldLocX, out regionWorldLocY); + m_homeRegionX = Util.WorldToRegionLoc(regionWorldLocX); + m_homeRegionY = Util.WorldToRegionLoc(regionWorldLocY); + // m_homeRegionX = (uint) (value >> 40); + // m_homeRegionY = (((uint) (value)) >> 8); } } diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs index ca6c3ca1a7..51535a665b 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs @@ -59,6 +59,7 @@ namespace OpenSim.Region.ClientStack.Linden public class EventQueueGetModule : IEventQueue, INonSharedRegionModule { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static string LogHeader = "[EVENT QUEUE GET MODULE]"; /// /// Debug level. @@ -479,7 +480,7 @@ namespace OpenSim.Region.ClientStack.Linden public virtual void EnableSimulator(ulong handle, IPEndPoint endPoint, UUID avatarID, int regionSizeX, int regionSizeY) { m_log.DebugFormat("{0} EnableSimulator. handle={1}, avatarID={2}, regionSize={3},{4}>", - "[EVENT QUEUE GET MODULE]", handle, avatarID, regionSizeX, regionSizeY); + LogHeader, handle, avatarID, regionSizeX, regionSizeY); OSD item = EventQueueHelper.EnableSimulator(handle, endPoint, regionSizeX, regionSizeY); Enqueue(item, avatarID); @@ -489,7 +490,7 @@ namespace OpenSim.Region.ClientStack.Linden ulong regionHandle, int regionSizeX, int regionSizeY) { m_log.DebugFormat("{0} EstablishAgentCommunication. handle={1}, avatarID={2}, regionSize={3},{4}>", - "[EVENT QUEUE GET MODULE]", regionHandle, avatarID, regionSizeX, regionSizeY); + LogHeader, regionHandle, avatarID, regionSizeX, regionSizeY); OSD item = EventQueueHelper.EstablishAgentCommunication(avatarID, endPoint.ToString(), capsPath, regionHandle, regionSizeX, regionSizeY); Enqueue(item, avatarID); } @@ -500,7 +501,7 @@ namespace OpenSim.Region.ClientStack.Linden UUID avatarID, int regionSizeX, int regionSizeY) { m_log.DebugFormat("{0} TeleportFinishEvent. handle={1}, avatarID={2}, regionSize={3},{4}>", - "[EVENT QUEUE GET MODULE]", regionHandle, avatarID, regionSizeX, regionSizeY); + LogHeader, regionHandle, avatarID, regionSizeX, regionSizeY); OSD item = EventQueueHelper.TeleportFinishEvent(regionHandle, simAccess, regionExternalEndPoint, locationID, flags, capsURL, avatarID, regionSizeX, regionSizeY); @@ -512,7 +513,7 @@ namespace OpenSim.Region.ClientStack.Linden string capsURL, UUID avatarID, UUID sessionID, int regionSizeX, int regionSizeY) { m_log.DebugFormat("{0} CrossRegion. handle={1}, avatarID={2}, regionSize={3},{4}>", - "[EVENT QUEUE GET MODULE]", handle, avatarID, regionSizeX, regionSizeY); + LogHeader, handle, avatarID, regionSizeX, regionSizeY); OSD item = EventQueueHelper.CrossRegion(handle, pos, lookAt, newRegionExternalEndPoint, capsURL, avatarID, sessionID, regionSizeX, regionSizeY); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 7b6889ab09..226f2a1aca 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -312,6 +312,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static string LogHeader = "[LLCLIENTVIEW]"; protected static Dictionary PacketHandlers = new Dictionary(); //Global/static handlers for all clients /// @@ -691,13 +692,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// true if the handler was added. This is currently always the case. public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync) + { + return AddLocalPacketHandler(packetType, handler, doAsync, false); + } + + /// + /// Add a handler for the given packet type. + /// + /// + /// + /// + /// If true, when the packet is received handle it on a different thread. Whether this is given direct to + /// a threadpool thread or placed in a queue depends on the inEngine parameter. + /// + /// + /// If async is false then this parameter is ignored. + /// If async is true and inEngine is false, then the packet is sent directly to a + /// threadpool thread. + /// If async is true and inEngine is true, then the packet is sent to the IncomingPacketAsyncHandlingEngine. + /// This may result in slower handling but reduces the risk of overloading the simulator when there are many + /// simultaneous async requests. + /// + /// true if the handler was added. This is currently always the case. + public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync, bool inEngine) { bool result = false; lock (m_packetHandlers) { if (!m_packetHandlers.ContainsKey(packetType)) { - m_packetHandlers.Add(packetType, new PacketProcessor() { method = handler, Async = doAsync }); + m_packetHandlers.Add( + packetType, new PacketProcessor() { method = handler, Async = doAsync }); result = true; } } @@ -1176,11 +1201,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// Send the region heightmap to the client + /// This method is only called when not doing intellegent terrain patch sending and + /// is only called when the scene presence is initially created and sends all of the + /// region's patches to the client. /// /// heightmap public virtual void SendLayerData(float[] map) { - Util.FireAndForget(DoSendLayerData, map); + Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData()); // Send it sync, and async. It's not that much data // and it improves user experience just so much! @@ -1193,15 +1221,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// private void DoSendLayerData(object o) { - float[] map = LLHeightFieldMoronize((float[])o); + TerrainData map = (TerrainData)o; try { + // Send LayerData in typerwriter pattern for (int y = 0; y < 16; y++) { - for (int x = 0; x < 16; x+=4) + for (int x = 0; x < 16; x++) { - SendLayerPacket(x, y, map); + SendLayerData(x, y, map); } } } @@ -1211,77 +1240,95 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - /// - /// Sends a set of four patches (x, x+1, ..., x+3) to the client - /// - /// heightmap - /// X coordinate for patches 0..12 - /// Y coordinate for patches 0..15 - private void SendLayerPacket(int x, int y, float[] map) + // Legacy form of invocation that passes around a bare data array. + // Just ignore what was passed and use the real terrain info that is part of the scene. + // As a HORRIBLE kludge in an attempt to not change the definition of IClientAPI, + // there is a special form for specifying multiple terrain patches to send. + // The form is to pass 'px' as negative the number of patches to send and to + // pass the float array as pairs of patch X and Y coordinates. So, passing 'px' + // as -2 and map= [3, 5, 8, 4] would mean to send two terrain heightmap patches + // and the patches to send are <3,5> and <8,4>. + public void SendLayerData(int px, int py, float[] map) { - int[] patches = new int[4]; - patches[0] = x + 0 + y * 16; - patches[1] = x + 1 + y * 16; - patches[2] = x + 2 + y * 16; - patches[3] = x + 3 + y * 16; - - float[] heightmap = (map.Length == 65536) ? - map : - LLHeightFieldMoronize(map); - - try + if (px >= 0) { - Packet layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches); - OutPacket(layerpack, ThrottleOutPacketType.Land); + SendLayerData(px, py, m_scene.Heightmap.GetTerrainData()); } - catch + else { - for (int px = x ; px < x + 4 ; px++) - SendLayerData(px, y, map); + int numPatches = -px; + int[] xPatches = new int[numPatches]; + int[] yPatches = new int[numPatches]; + for (int pp = 0; pp < numPatches; pp++) + { + xPatches[pp] = (int)map[pp * 2]; + yPatches[pp] = (int)map[pp * 2 + 1]; + } + + // DebugSendingPatches("SendLayerData", xPatches, yPatches); + + SendLayerData(xPatches, yPatches, m_scene.Heightmap.GetTerrainData()); + } + } + + private void DebugSendingPatches(string pWho, int[] pX, int[] pY) + { + if (m_log.IsDebugEnabled) + { + int numPatches = pX.Length; + string Xs = ""; + string Ys = ""; + for (int pp = 0; pp < numPatches; pp++) + { + Xs += String.Format("{0}", (int)pX[pp]) + ","; + Ys += String.Format("{0}", (int)pY[pp]) + ","; + } + m_log.DebugFormat("{0} {1}: numPatches={2}, X={3}, Y={4}", LogHeader, pWho, numPatches, Xs, Ys); } } /// - /// Sends a specified patch to a client + /// Sends a terrain packet for the point specified. + /// This is a legacy call that has refarbed the terrain into a flat map of floats. + /// We just use the terrain from the region we know about. /// /// Patch coordinate (x) 0..15 /// Patch coordinate (y) 0..15 /// heightmap - public void SendLayerData(int px, int py, float[] map) + public void SendLayerData(int px, int py, TerrainData terrData) + { + int[] xPatches = new[] { px }; + int[] yPatches = new[] { py }; + SendLayerData(xPatches, yPatches, terrData); + } + + private void SendLayerData(int[] px, int[] py, TerrainData terrData) { try { - int[] patches = new int[] { py * 16 + px }; - float[] heightmap = (map.Length == 65536) ? - map : - LLHeightFieldMoronize(map); - - LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches); - - // When a user edits the terrain, so much data is sent, the data queues up fast and presents a sub optimal editing experience. - // To alleviate this issue, when the user edits the terrain, we start skipping the queues until they're done editing the terrain. - // We also make them unreliable because it's extremely likely that multiple packets will be sent for a terrain patch area - // invalidating previous packets for that area. - - // It's possible for an editing user to flood themselves with edited packets but the majority of use cases are such that only a - // tiny percentage of users will be editing the terrain. Other, non-editing users will see the edits much slower. - - // One last note on this topic, by the time users are going to be editing the terrain, it's extremely likely that the sim will - // have rezzed already and therefore this is not likely going to cause any additional issues with lost packets, objects or terrain - // patches. - - // m_justEditedTerrain is volatile, so test once and duplicate two affected statements so we only have one cache miss. - if (m_justEditedTerrain) + /* test code using the terrain compressor in libOpenMetaverse + int[] patchInd = new int[1]; + patchInd[0] = px + (py * Constants.TerrainPatchSize); + LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(terrData.GetFloatsSerialized(), patchInd); + */ + // Many, many patches could have been passed to us. Since the patches will be compressed + // into variable sized blocks, we cannot pre-compute how many will fit into one + // packet. While some fancy packing algorithm is possible, 4 seems to always fit. + int PatchesAssumedToFit = 4; + for (int pcnt = 0; pcnt < px.Length; pcnt += PatchesAssumedToFit) { - layerpack.Header.Reliable = false; - OutPacket(layerpack, - ThrottleOutPacketType.Unknown ); - } - else - { - layerpack.Header.Reliable = true; - OutPacket(layerpack, - ThrottleOutPacketType.Task); + int remaining = Math.Min(px.Length - pcnt, PatchesAssumedToFit); + int[] xPatches = new int[remaining]; + int[] yPatches = new int[remaining]; + for (int ii = 0; ii < remaining; ii++) + { + xPatches[ii] = px[pcnt + ii]; + yPatches[ii] = py[pcnt + ii]; + } + LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLandPacket(terrData, xPatches, yPatches); + // DebugSendingPatches("SendLayerDataInternal", xPatches, yPatches); + + SendTheLayerPacket(layerpack); } } catch (Exception e) @@ -1290,36 +1337,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - /// - /// Munges heightfield into the LLUDP backed in restricted heightfield. - /// - /// float array in the base; Constants.RegionSize - /// float array in the base 256 - internal float[] LLHeightFieldMoronize(float[] map) + // When a user edits the terrain, so much data is sent, the data queues up fast and presents a + // sub optimal editing experience. To alleviate this issue, when the user edits the terrain, we + // start skipping the queues until they're done editing the terrain. We also make them + // unreliable because it's extremely likely that multiple packets will be sent for a terrain patch + // area invalidating previous packets for that area. + + // It's possible for an editing user to flood themselves with edited packets but the majority + // of use cases are such that only a tiny percentage of users will be editing the terrain. + // Other, non-editing users will see the edits much slower. + + // One last note on this topic, by the time users are going to be editing the terrain, it's + // extremely likely that the sim will have rezzed already and therefore this is not likely going + // to cause any additional issues with lost packets, objects or terrain patches. + + // m_justEditedTerrain is volatile, so test once and duplicate two affected statements so we + // only have one cache miss. + private void SendTheLayerPacket(LayerDataPacket layerpack) { - if (map.Length == 65536) - return map; + if (m_justEditedTerrain) + { + layerpack.Header.Reliable = false; + OutPacket(layerpack, ThrottleOutPacketType.Unknown ); + } else { - float[] returnmap = new float[65536]; - - if (map.Length < 65535) - { - // rebase the vector stride to 256 - for (int i = 0; i < Constants.RegionSize; i++) - Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, (int)Constants.RegionSize); - } - else - { - for (int i = 0; i < 256; i++) - Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, 256); - } - - //Array.Copy(map,0,returnmap,0,(map.Length < 65536)? map.Length : 65536); - - return returnmap; + layerpack.Header.Reliable = true; + OutPacket(layerpack, ThrottleOutPacketType.Land); } - } /// @@ -1348,21 +1393,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP { Vector2[] windSpeeds = (Vector2[])o; TerrainPatch[] patches = new TerrainPatch[2]; - patches[0] = new TerrainPatch(); - patches[0].Data = new float[16 * 16]; - patches[1] = new TerrainPatch(); - patches[1].Data = new float[16 * 16]; + patches[0] = new TerrainPatch { Data = new float[16 * 16] }; + patches[1] = new TerrainPatch { Data = new float[16 * 16] }; - for (int y = 0; y < 16; y++) + for (int x = 0; x < 16 * 16; x++) { - for (int x = 0; x < 16; x++) - { - patches[0].Data[y * 16 + x] = windSpeeds[y * 16 + x].X; - patches[1].Data[y * 16 + x] = windSpeeds[y * 16 + x].Y; - } + patches[0].Data[x] = windSpeeds[x].X; + patches[1].Data[x] = windSpeeds[x].Y; } - LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, TerrainPatch.LayerType.Wind); + byte layerType = (byte)TerrainPatch.LayerType.Wind; + if (m_scene.RegionInfo.RegionSizeX > Constants.RegionSize || m_scene.RegionInfo.RegionSizeY > Constants.RegionSize) + layerType = (byte)TerrainPatch.LayerType.WindExtended; + + // LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, (TerrainPatch.LayerType)layerType); + LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLayerDataPacket(patches, layerType, + (int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY); layerpack.Header.Zerocoded = true; OutPacket(layerpack, ThrottleOutPacketType.Wind); } @@ -1386,7 +1432,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, TerrainPatch.LayerType.Cloud); + byte layerType = (byte)TerrainPatch.LayerType.Cloud; + if (m_scene.RegionInfo.RegionSizeX > Constants.RegionSize || m_scene.RegionInfo.RegionSizeY > Constants.RegionSize) + layerType = (byte)TerrainPatch.LayerType.CloudExtended; + + // LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, (TerrainPatch.LayerType)layerType); + LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLayerDataPacket(patches, layerType, + (int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY); layerpack.Header.Zerocoded = true; OutPacket(layerpack, ThrottleOutPacketType.Cloud); } @@ -1491,10 +1543,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP mapReply.Data[i].Access = mapBlocks2[i].Access; mapReply.Data[i].Agents = mapBlocks2[i].Agents; - // TODO: hookup varregion sim size here mapReply.Size[i] = new MapBlockReplyPacket.SizeBlock(); - mapReply.Size[i].SizeX = 256; - mapReply.Size[i].SizeY = 256; + mapReply.Size[i].SizeX = mapBlocks2[i].SizeX; + mapReply.Size[i].SizeY = mapBlocks2[i].SizeY; } OutPacket(mapReply, ThrottleOutPacketType.Land); } @@ -1659,15 +1710,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendKillObject(List localIDs) { -// foreach (uint id in localIDs) -// m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, id, regionHandle); - - // remove pending entities - lock (m_entityProps.SyncRoot) - m_entityProps.Remove(localIDs); - lock (m_entityUpdates.SyncRoot) - m_entityUpdates.Remove(localIDs); - KillObjectPacket kill = (KillObjectPacket)PacketPool.Instance.GetPacket(PacketType.KillObject); // TODO: don't create new blocks if recycling an old packet kill.ObjectData = new KillObjectPacket.ObjectDataBlock[localIDs.Count]; @@ -9093,6 +9135,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP TeleportLocationRequest handlerTeleportLocationRequest = OnTeleportLocationRequest; if (handlerTeleportLocationRequest != null) { + // Adjust teleport location to base of a larger region if requested to teleport to a sub-region + uint locX, locY; + Util.RegionHandleToWorldLoc(tpLocReq.Info.RegionHandle, out locX, out locY); + if ((locX >= m_scene.RegionInfo.WorldLocX) + && (locX < (m_scene.RegionInfo.WorldLocX + m_scene.RegionInfo.RegionSizeX)) + && (locY >= m_scene.RegionInfo.WorldLocY) + && (locY < (m_scene.RegionInfo.WorldLocY + m_scene.RegionInfo.RegionSizeY)) ) + { + tpLocReq.Info.RegionHandle = m_scene.RegionInfo.RegionHandle; + tpLocReq.Info.Position.X += locX - m_scene.RegionInfo.WorldLocX; + tpLocReq.Info.Position.Y += locY - m_scene.RegionInfo.WorldLocY; + } + handlerTeleportLocationRequest(this, tpLocReq.Info.RegionHandle, tpLocReq.Info.Position, tpLocReq.Info.LookAt, 16); } diff --git a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs index dcfc630c5d..9d70063317 100644 --- a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs @@ -213,8 +213,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat IScene scene = c.Scene; UUID destination = c.Destination; Vector3 fromPos = c.Position; - Vector3 regionPos = new Vector3(scene.RegionInfo.RegionLocX * Constants.RegionSize, - scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); + Vector3 regionPos = new Vector3(scene.RegionInfo.WorldLocX, scene.RegionInfo.WorldLocY, 0); bool checkParcelHide = false; UUID sourceParcelID = UUID.Zero; @@ -424,8 +423,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat { Vector3 fromRegionPos = fromPos + regionPos; Vector3 toRegionPos = presence.AbsolutePosition + - new Vector3(presence.Scene.RegionInfo.RegionLocX * Constants.RegionSize, - presence.Scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); + new Vector3(presence.Scene.RegionInfo.WorldLocX, presence.Scene.RegionInfo.WorldLocY, 0); int dis = (int)Util.GetDistanceTo(toRegionPos, fromRegionPos); diff --git a/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs index db8405bdb2..7177d9b7bd 100644 --- a/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs @@ -663,8 +663,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles Vector3 avaPos = p.AbsolutePosition; // Getting the global position for the Avatar - Vector3 posGlobal = new Vector3(remoteClient.Scene.RegionInfo.RegionLocX*Constants.RegionSize + avaPos.X, - remoteClient.Scene.RegionInfo.RegionLocY*Constants.RegionSize + avaPos.Y, + Vector3 posGlobal = new Vector3(remoteClient.Scene.RegionInfo.WorldLocX + avaPos.X, + remoteClient.Scene.RegionInfo.WorldLocY + avaPos.Y, avaPos.Z); string landOwnerName = string.Empty; @@ -1353,4 +1353,4 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles } #endregion Web Util } -} \ No newline at end of file +} diff --git a/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs b/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs index 6e0fd03419..f615c6b9e6 100644 --- a/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs @@ -317,9 +317,7 @@ namespace OpenSim.Region.CoreModules.Framework foreach (KeyValuePair kvp in m_childrenSeeds[agentID]) { uint x, y; - Utils.LongToUInts(kvp.Key, out x, out y); - x = x / Constants.RegionSize; - y = y / Constants.RegionSize; + Util.RegionHandleToRegionLoc(kvp.Key, out x, out y); m_log.Info(" >> "+x+", "+y+": "+kvp.Value); } } diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index b32a169c97..c81e5aa6cf 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -121,8 +121,57 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer /// private EntityTransferStateMachine m_entityTransferStateMachine; - private ExpiringCache> m_bannedRegions = - new ExpiringCache>(); + // For performance, we keed a cached of banned regions so we don't keep going + // to the grid service. + private class BannedRegionCache + { + private ExpiringCache> m_bannedRegions = + new ExpiringCache>(); + ExpiringCache m_idCache; + DateTime m_banUntil; + public BannedRegionCache() + { + } + // Return 'true' if there is a valid ban entry for this agent in this region + public bool IfBanned(ulong pRegionHandle, UUID pAgentID) + { + bool ret = false; + if (m_bannedRegions.TryGetValue(pAgentID, out m_idCache)) + { + if (m_idCache.TryGetValue(pRegionHandle, out m_banUntil)) + { + if (DateTime.Now < m_banUntil) + { + ret = true; + } + } + } + return ret; + } + // Add this agent in this region as a banned person + public void Add(ulong pRegionHandle, UUID pAgentID) + { + this.Add(pRegionHandle, pAgentID, 45, 15); + } + public void Add(ulong pRegionHandle, UUID pAgentID, double newTime, double extendTime) + { + if (!m_bannedRegions.TryGetValue(pAgentID, out m_idCache)) + { + m_idCache = new ExpiringCache(); + m_bannedRegions.Add(pAgentID, m_idCache, TimeSpan.FromSeconds(newTime)); + } + m_idCache.Add(pRegionHandle, DateTime.Now + TimeSpan.FromSeconds(extendTime), TimeSpan.FromSeconds(extendTime)); + } + // Remove the agent from the region's banned list + public void Remove(ulong pRegionHandle, UUID pAgentID) + { + if (m_bannedRegions.TryGetValue(pAgentID, out m_idCache)) + { + m_idCache.Remove(pRegionHandle); + } + } + } + private BannedRegionCache m_bannedRegionCache = new BannedRegionCache(); private IEventQueue m_eqModule; private IRegionCombinerModule m_regionCombinerModule; @@ -337,6 +386,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer "[ENTITY TRANSFER MODULE]: Received teleport cancel request from {0} in {1}", client.Name, Scene.Name); } + // Attempt to teleport the ScenePresence to the specified position in the specified region (spec'ed by its handle). public void Teleport(ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags) { if (sp.Scene.Permissions.IsGridGod(sp.UUID)) @@ -418,7 +468,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer sp.Name, position, sp.Scene.RegionInfo.RegionName); // Teleport within the same region - if (IsOutsideRegion(sp.Scene, position) || position.Z < 0) + if (!sp.Scene.PositionIsInCurrentRegion(position) || position.Z < 0) { Vector3 emergencyPos = new Vector3(128, 128, 128); @@ -437,10 +487,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer float posZLimit = 22; // TODO: Check other Scene HeightField - if (position.X > 0 && position.X <= (int)Constants.RegionSize && position.Y > 0 && position.Y <= (int)Constants.RegionSize) - { - posZLimit = (float)sp.Scene.Heightmap[(int)position.X, (int)position.Y]; - } + posZLimit = (float)sp.Scene.Heightmap[(int)position.X, (int)position.Y]; posZLimit += localHalfAVHeight + 0.1f; @@ -484,9 +531,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags, out GridRegion finalDestination) { - uint x = 0, y = 0; - Utils.LongToUInts(regionHandle, out x, out y); - GridRegion reg = Scene.GridService.GetRegionByPosition(sp.Scene.RegionInfo.ScopeID, (int)x, (int)y); + // Get destination region taking into account that the address could be an offset + // region inside a varregion. + GridRegion reg = GetTeleportDestinationRegion(sp.Scene.GridService, sp.Scene.RegionInfo.ScopeID, regionHandle, ref position); if (reg != null) { @@ -537,12 +584,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // and set the map-tile to '(Offline)' uint regX, regY; - Utils.LongToUInts(regionHandle, out regX, out regY); + Util.RegionHandleToRegionLoc(regionHandle, out regX, out regY); MapBlockData block = new MapBlockData(); - block.X = (ushort)(regX / Constants.RegionSize); - block.Y = (ushort)(regY / Constants.RegionSize); - block.Access = 254; // == not there + block.X = (ushort)(regX); + block.Y = (ushort)(regY); + block.Access = (byte)SimAccess.Down; // == not there List blocks = new List(); blocks.Add(block); @@ -550,6 +597,31 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer } } + // The teleport address could be an address in a subregion of a larger varregion. + // Find the real base region and adjust the teleport location to account for the + // larger region. + private GridRegion GetTeleportDestinationRegion(IGridService gridService, UUID scope, ulong regionHandle, ref Vector3 position) + { + uint x = 0, y = 0; + Util.RegionHandleToWorldLoc(regionHandle, out x, out y); + + // Compute the world location we're teleporting to + double worldX = (double)x + position.X; + double worldY = (double)y + position.Y; + + // Find the region that contains the position + GridRegion reg = GetRegionContainingWorldLocation(gridService, scope, worldX, worldY); + + if (reg != null) + { + // modify the position for the offset into the actual region returned + position.X += x - reg.RegionLocX; + position.Y += y - reg.RegionLocY; + } + + return reg; + } + // Nothing to validate here protected virtual bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason) { @@ -650,10 +722,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer return; } - uint newRegionX = (uint)(reg.RegionHandle >> 40); - uint newRegionY = (((uint)(reg.RegionHandle)) >> 8); - uint oldRegionX = (uint)(sp.Scene.RegionInfo.RegionHandle >> 40); - uint oldRegionY = (((uint)(sp.Scene.RegionInfo.RegionHandle)) >> 8); + uint newRegionX, newRegionY, oldRegionX, oldRegionY; + Util.RegionHandleToRegionLoc(reg.RegionHandle, out newRegionX, out newRegionY); + Util.RegionHandleToRegionLoc(sp.Scene.RegionInfo.RegionHandle, out oldRegionX, out oldRegionY); ulong destinationHandle = finalDestination.RegionHandle; @@ -675,8 +746,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer string reason; string version; + string myversion = string.Format("{0}/{1}", OutgoingTransferVersionName, MaxOutgoingTransferVersion); if (!Scene.SimulationService.QueryAccess( - finalDestination, sp.ControllingClient.AgentId, Vector3.Zero, out version, out reason)) + finalDestination, sp.ControllingClient.AgentId, position, out version, out reason)) { sp.ControllingClient.SendTeleportFailed(reason); @@ -1274,6 +1346,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer return region; } + // This returns 'true' if the new region already has a child agent for our + // incoming agent. The implication is that, if 'false', we have to create the + // child and then teleport into the region. protected virtual bool NeedsNewAgent(float drawdist, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY) { if (m_regionCombinerModule != null && m_regionCombinerModule.IsRootForMegaregion(Scene.RegionInfo.RegionID)) @@ -1298,20 +1373,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer return Util.IsOutsideView(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY); } - protected virtual bool IsOutsideRegion(Scene s, Vector3 pos) - { - if (s.TestBorderCross(pos, Cardinals.N)) - return true; - if (s.TestBorderCross(pos, Cardinals.S)) - return true; - if (s.TestBorderCross(pos, Cardinals.E)) - return true; - if (s.TestBorderCross(pos, Cardinals.W)) - return true; - - return false; - } - #endregion #region Landmark Teleport @@ -1398,42 +1459,20 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer UUID agentID = agent.UUID; ulong destinyHandle = destiny.RegionHandle; - ExpiringCache r; - DateTime banUntil; - if (m_bannedRegions.TryGetValue(agentID, out r)) + if (m_bannedRegionCache.IfBanned(destinyHandle, agentID)) { - if (r.TryGetValue(destinyHandle, out banUntil)) - { - if (DateTime.Now < banUntil) - { - reason = "Cannot connect to region"; - return false; - } - r.Remove(destinyHandle); - } - } - else - { - r = null; + reason = "Cannot connect to region"; + return false; } Scene ascene = agent.Scene; if (!ascene.SimulationService.QueryAccess(destiny, agentID, position, out version, out reason)) { - if (r == null) - { - r = new ExpiringCache(); - r.Add(destinyHandle, DateTime.Now + TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(30)); - - m_bannedRegions.Add(agentID, r, TimeSpan.FromSeconds(30)); - } - else - { - r.Add(destinyHandle, DateTime.Now + TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(30)); - } + m_bannedRegionCache.Add(destinyHandle, agentID, 30.0, 30.0); return false; } + return true; } @@ -1443,161 +1482,74 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer return GetDestination(scene, agentID, pos, out version, out newpos, out r); } - public GridRegion GetDestination(Scene scene, UUID agentID, Vector3 pos, out string version, out Vector3 newpos, out string reason) + // Given a position relative to the current region (which has previously been tested to + // see that it is actually outside the current region), find the new region that the + // point is actually in. + // Returns the coordinates and information of the new region or 'null' of it doesn't exist. + public GridRegion GetDestination(Scene scene, UUID agentID, Vector3 pos, + out string version, out Vector3 newpos, out string failureReason) { version = String.Empty; - reason = String.Empty; newpos = pos; + failureReason = string.Empty; // m_log.DebugFormat( // "[ENTITY TRANSFER MODULE]: Crossing agent {0} at pos {1} in {2}", agent.Name, pos, scene.Name); - RegionInfo regInfo = scene.RegionInfo; + // Compute world location of the object's position + double presenceWorldX = (double)scene.RegionInfo.WorldLocX + pos.X; + double presenceWorldY = (double)scene.RegionInfo.WorldLocY + pos.Y; - uint neighbourx = regInfo.RegionLocX; - uint neighboury = regInfo.RegionLocY; - const float boundaryDistance = 0.7f; + // Call the grid service to lookup the region containing the new position. + GridRegion neighbourRegion = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID, + presenceWorldX, presenceWorldY, + Math.Max(scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY)); -/* - Vector3 northCross = new Vector3(0, boundaryDistance, 0); - Vector3 southCross = new Vector3(0, -1 * boundaryDistance, 0); - Vector3 eastCross = new Vector3(boundaryDistance, 0, 0); - Vector3 westCross = new Vector3(-1 * boundaryDistance, 0, 0); - - // distance into new region to place avatar - const float enterDistance = 0.5f; - const float maxX = Constants.RegionSize - enterDistance; - const float maxY = Constants.RegionSize - enterDistance; - - if (scene.TestBorderCross(pos + westCross, Cardinals.W)) + if (neighbourRegion != null) { - if (scene.TestBorderCross(pos + northCross, Cardinals.N)) + // Compute the entity's position relative to the new region + newpos = new Vector3((float)(presenceWorldX - (double)neighbourRegion.RegionLocX), + (float)(presenceWorldY - (double)neighbourRegion.RegionLocY), + pos.Z); + + if (m_bannedRegionCache.IfBanned(neighbourRegion.RegionHandle, agentID)) { - Border b = scene.GetCrossedBorder(pos + northCross, Cardinals.N); - neighboury += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize); - newpos.Y -= Constants.RegionSize; + failureReason = "Cannot region cross into banned parcel"; + neighbourRegion = null; } - else if (scene.TestBorderCross(pos + southCross, Cardinals.S)) + else { - neighboury--; - newpos.Y += Constants.RegionSize; + // If not banned, make sure this agent is not in the list. + m_bannedRegionCache.Remove(neighbourRegion.RegionHandle, agentID); } - neighbourx--; - newpos.X += Constants.RegionSize; - } - else if (scene.TestBorderCross(pos + eastCross, Cardinals.E)) - { - Border b = scene.GetCrossedBorder(pos + eastCross, Cardinals.E); - neighbourx += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize); - newpos.X -= Constants.RegionSize; - - if (scene.TestBorderCross(pos + southCross, Cardinals.S)) + // Check to see if we have access to the target region. + string myversion = string.Format("{0}/{1}", OutgoingTransferVersionName, MaxOutgoingTransferVersion); + if (neighbourRegion != null + && !scene.SimulationService.QueryAccess(neighbourRegion, agentID, newpos, out version, out failureReason)) { - neighboury--; - newpos.Y += Constants.RegionSize; - } - else if (scene.TestBorderCross(pos + northCross, Cardinals.N)) - { - Border c = scene.GetCrossedBorder(pos + northCross, Cardinals.N); - neighboury += (uint)(int)(c.BorderLine.Z / (int)Constants.RegionSize); - newpos.Y -= Constants.RegionSize; - } - } - else if (scene.TestBorderCross(pos + southCross, Cardinals.S)) - { - Border b = scene.GetCrossedBorder(pos + southCross, Cardinals.S); - neighboury--; - newpos.Y += Constants.RegionSize; - } - else if (scene.TestBorderCross(pos + northCross, Cardinals.N)) - { - Border b = scene.GetCrossedBorder(pos + northCross, Cardinals.N); - neighboury += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize); - newpos.Y -= Constants.RegionSize; - } - - newpos.X = Util.Clamp(newpos.X, enterDistance, maxX); - newpos.Y = Util.Clamp(newpos.Y, enterDistance, maxY); -*/ - float regionSizeX = regInfo.RegionSizeX; - float regionSizeY = regInfo.RegionSizeY; - - if (pos.X < boundaryDistance) - neighbourx--; - else if (pos.X > regionSizeX - boundaryDistance) - neighbourx += (uint)(regionSizeX / Constants.RegionSize); - - if (pos.Y < boundaryDistance) - neighboury--; - else if (pos.Y > regionSizeY - boundaryDistance) - neighboury += (uint)(regionSizeY / Constants.RegionSize); - - int x = (int)(neighbourx * Constants.RegionSize); - int y = (int)(neighboury * Constants.RegionSize); - - ulong neighbourHandle = Utils.UIntsToLong((uint)x, (uint)y); - - ExpiringCache r; - DateTime banUntil; - - if (m_bannedRegions.TryGetValue(agentID, out r)) - { - if (r.TryGetValue(neighbourHandle, out banUntil)) - { - if (DateTime.Now < banUntil) - return null; - r.Remove(neighbourHandle); + // remember banned + m_bannedRegionCache.Add(neighbourRegion.RegionHandle, agentID); + neighbourRegion = null; } } else { - r = null; + // The destination region just doesn't exist + failureReason = "Cannot cross into non-existent region"; } - GridRegion neighbourRegion = scene.GridService.GetRegionByPosition(scene.RegionInfo.ScopeID, (int)x, (int)y); if (neighbourRegion == null) - { - reason = ""; - return null; - } - - float newRegionSizeX = neighbourRegion.RegionSizeX; - float newRegionSizeY = neighbourRegion.RegionSizeY; - if (newRegionSizeX == 0) - newRegionSizeX = Constants.RegionSize; - if (newRegionSizeY == 0) - newRegionSizeY = Constants.RegionSize; - - if (pos.X < boundaryDistance) - newpos.X += newRegionSizeX; - else if (pos.X > regionSizeX - boundaryDistance) - newpos.X -= regionSizeX; - - if (pos.Y < boundaryDistance) - newpos.Y += newRegionSizeY; - else if (pos.Y > regionSizeY - boundaryDistance) - newpos.Y -= regionSizeY; - - const float enterDistance = 0.5f; - newpos.X = Util.Clamp(newpos.X, enterDistance, newRegionSizeX - enterDistance); - newpos.Y = Util.Clamp(newpos.Y, enterDistance, newRegionSizeY - enterDistance); - - if (!scene.SimulationService.QueryAccess(neighbourRegion, agentID, newpos, out version, out reason)) - { - if (r == null) - { - r = new ExpiringCache(); - r.Add(neighbourHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); - - m_bannedRegions.Add(agentID, r, TimeSpan.FromSeconds(45)); - } - else - { - r.Add(neighbourHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); - } - return null; - } + m_log.DebugFormat("{0} GetDestination: region not found. Old region name={1} at <{2},{3}> of size <{4},{5}>. Old pos={6}", + LogHeader, scene.RegionInfo.RegionName, + scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY, + scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY, + pos); + else + m_log.DebugFormat("{0} GetDestination: new region={1} at <{2},{3}> of size <{4},{5}>, newpos=<{6},{7}>", + LogHeader, neighbourRegion.RegionName, + neighbourRegion.RegionLocX, neighbourRegion.RegionLocY, neighbourRegion.RegionSizeX, neighbourRegion.RegionSizeY, + newpos.X, newpos.Y); return neighbourRegion; } @@ -1632,15 +1584,16 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer uint y; Vector3 newpos; string version; - string reason; + string failureReason; Vector3 pos = agent.AbsolutePosition + agent.Velocity; - GridRegion neighbourRegion = GetDestination(agent.Scene, agent.UUID, pos, out version, out newpos, out reason); + GridRegion neighbourRegion = GetDestination(agent.Scene, agent.UUID, pos, + out version, out newpos, out failureReason); if (neighbourRegion == null) { - if (reason != String.Empty) - agent.ControllingClient.SendAlertMessage("Cannot cross to region"); + if (failureReason != String.Empty) + agent.ControllingClient.SendAlertMessage(failureReason); return agent; } @@ -1678,7 +1631,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer agent.Scene.RequestTeleportLocation( agent.ControllingClient, - Utils.UIntsToLong(regionX * (uint)Constants.RegionSize, regionY * (uint)Constants.RegionSize), + Util.RegionLocToHandle(regionX, regionY), position, agent.Lookat, (uint)Constants.TeleportFlags.ViaLocation); @@ -1688,11 +1641,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer if (im != null) { UUID gotoLocation = Util.BuildFakeParcelID( - Util.UIntsToLong( - (regionX * - (uint)Constants.RegionSize), - (regionY * - (uint)Constants.RegionSize)), + Util.RegionLocToHandle(regionX, regionY), (uint)(int)position.X, (uint)(int)position.Y, (uint)(int)position.Z); @@ -1745,8 +1694,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer /// Calls an asynchronous method to do so.. so it doesn't lag the sim. /// public ScenePresence CrossAgentToNewRegionAsync( - ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, - bool isFlying, string version) + ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, + bool isFlying, string version) { if (!CrossAgentToNewRegionPrep(agent, neighbourRegion)) { @@ -1893,11 +1842,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // Next, let's close the child agent connections that are too far away. uint neighbourx; uint neighboury; - - Utils.LongToUInts(neighbourRegion.RegionHandle, out neighbourx, out neighboury); - - neighbourx /= Constants.RegionSize; - neighboury /= Constants.RegionSize; + Util.RegionHandleToRegionLoc(neighbourRegion.RegionHandle, out neighbourx, out neighboury); agent.CloseChildAgents(neighbourx, neighboury); @@ -2059,7 +2004,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer if (m_regionInfo != null) { - neighbours = RequestNeighbours(sp, m_regionInfo.RegionLocX, m_regionInfo.RegionLocY); + neighbours = GetNeighbours(sp, m_regionInfo.RegionLocX, m_regionInfo.RegionLocY); } else { @@ -2216,15 +2161,195 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer } } + // Computes the difference between two region bases. + // Returns a vector of world coordinates (meters) from base of first region to the second. + // The first region is the home region of the passed scene presence. Vector3 CalculateOffset(ScenePresence sp, GridRegion neighbour) { - int rRegionX = (int)sp.Scene.RegionInfo.RegionLocX; - int rRegionY = (int)sp.Scene.RegionInfo.RegionLocY; + /* + int rRegionX = (int)sp.Scene.RegionInfo.LegacyRegionLocX; + int rRegionY = (int)sp.Scene.RegionInfo.LegacyRegionLocY; int tRegionX = neighbour.RegionLocX / (int)Constants.RegionSize; int tRegionY = neighbour.RegionLocY / (int)Constants.RegionSize; int shiftx = (rRegionX - tRegionX) * (int)Constants.RegionSize; int shifty = (rRegionY - tRegionY) * (int)Constants.RegionSize; return new Vector3(shiftx, shifty, 0f); + */ + return new Vector3( sp.Scene.RegionInfo.WorldLocX - neighbour.RegionLocX, + sp.Scene.RegionInfo.WorldLocY - neighbour.RegionLocY, + 0f); + } + + public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID, double px, double py) + { + // Since we don't know how big the regions could be, we have to search a very large area + // to find possible regions. + return GetRegionContainingWorldLocation(pGridService, pScopeID, px, py, Constants.MaximumRegionSize); + } + + #region NotFoundLocationCache class + // A collection of not found locations to make future lookups 'not found' lookups quick. + // A simple expiring cache that keeps not found locations for some number of seconds. + // A 'not found' location is presumed to be anywhere in the minimum sized region that + // contains that point. A conservitive estimate. + private class NotFoundLocationCache + { + private struct NotFoundLocation + { + public double minX, maxX, minY, maxY; + public DateTime expireTime; + } + private List m_notFoundLocations = new List(); + public NotFoundLocationCache() + { + } + // Add an area to the list of 'not found' places. The area is the snapped region + // area around the added point. + public void Add(double pX, double pY) + { + lock (m_notFoundLocations) + { + if (!LockedContains(pX, pY)) + { + NotFoundLocation nfl = new NotFoundLocation(); + // A not found location is not found for at least a whole region sized area + nfl.minX = pX - (pX % (double)Constants.RegionSize); + nfl.minY = pY - (pY % (double)Constants.RegionSize); + nfl.maxX = nfl.minX + (double)Constants.RegionSize; + nfl.maxY = nfl.minY + (double)Constants.RegionSize; + nfl.expireTime = DateTime.Now + TimeSpan.FromSeconds(30); + m_notFoundLocations.Add(nfl); + } + } + + } + // Test to see of this point is in any of the 'not found' areas. + // Return 'true' if the point is found inside the 'not found' areas. + public bool Contains(double pX, double pY) + { + bool ret = false; + lock (m_notFoundLocations) + ret = LockedContains(pX, pY); + return ret; + } + private bool LockedContains(double pX, double pY) + { + bool ret = false; + this.DoExpiration(); + foreach (NotFoundLocation nfl in m_notFoundLocations) + { + if (pX >= nfl.minX && pX < nfl.maxX && pY >= nfl.minY && pY < nfl.maxY) + { + ret = true; + break; + } + } + return ret; + } + private void DoExpiration() + { + List m_toRemove = null; + DateTime now = DateTime.Now; + foreach (NotFoundLocation nfl in m_notFoundLocations) + { + if (nfl.expireTime < now) + { + if (m_toRemove == null) + m_toRemove = new List(); + m_toRemove.Add(nfl); + } + } + if (m_toRemove != null) + { + foreach (NotFoundLocation nfl in m_toRemove) + m_notFoundLocations.Remove(nfl); + m_toRemove.Clear(); + } + } + } + #endregion // NotFoundLocationCache class + private NotFoundLocationCache m_notFoundLocationCache = new NotFoundLocationCache(); + + // Given a world position (fractional meter coordinate), get the GridRegion info for + // the region containing that point. + // Someday this should be a method on GridService. + // 'pSizeHint' is the size of the source region but since the destination point can be anywhere + // the size of the target region is unknown thus the search area might have to be very large. + // Return 'null' if no such region exists. + public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID, + double px, double py, uint pSizeHint) + { + m_log.DebugFormat("{0} GetRegionContainingWorldLocation: call, XY=<{1},{2}>", LogHeader, px, py); + GridRegion ret = null; + const double fudge = 2.0; + + // One problem with this routine is negative results. That is, this can be called lots of times + // for regions that don't exist. m_notFoundLocationCache remembers 'not found' results so they + // will be quick 'not found's next time. + // NotFoundLocationCache is an expiring cache so it will eventually forget about 'not found' and + // thus re-ask the GridService about the location. + if (m_notFoundLocationCache.Contains(px, py)) + { + m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Not found via cache. loc=<{1},{2}>", LogHeader, px, py); + return null; + } + + // As an optimization, since most regions will be legacy sized regions (256x256), first try to get + // the region at the appropriate legacy region location. + uint possibleX = (uint)Math.Floor(px); + possibleX -= possibleX % Constants.RegionSize; + uint possibleY = (uint)Math.Floor(py); + possibleY -= possibleY % Constants.RegionSize; + ret = pGridService.GetRegionByPosition(pScopeID, (int)possibleX, (int)possibleY); + if (ret != null) + { + m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Found region using legacy size. rloc=<{1},{2}>. Rname={3}", + LogHeader, possibleX, possibleY, ret.RegionName); + } + + if (ret == null) + { + // If the simple lookup failed, search the larger area for a region that contains this point + double range = (double)pSizeHint + fudge; + while (ret == null && range <= (Constants.MaximumRegionSize + Constants.RegionSize)) + { + // Get from the grid service a list of regions that might contain this point. + // The region origin will be in the zero direction so only subtract the range. + List possibleRegions = pGridService.GetRegionRange(pScopeID, + (int)(px - range), (int)(px), + (int)(py - range), (int)(py)); + m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegions cnt={1}, range={2}", + LogHeader, possibleRegions.Count, range); + if (possibleRegions != null && possibleRegions.Count > 0) + { + // If we found some regions, check to see if the point is within + foreach (GridRegion gr in possibleRegions) + { + m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegion nm={1}, regionLoc=<{2},{3}>, regionSize=<{4},{5}>", + LogHeader, gr.RegionName, gr.RegionLocX, gr.RegionLocY, gr.RegionSizeX, gr.RegionSizeY); + if (px >= (double)gr.RegionLocX && px < (double)(gr.RegionLocX + gr.RegionSizeX) + && py >= (double)gr.RegionLocY && py < (double)(gr.RegionLocY + gr.RegionSizeY)) + { + // Found a region that contains the point + ret = gr; + m_log.DebugFormat("{0} GetRegionContainingWorldLocation: found. RegionName={1}", LogHeader, ret.RegionName); + break; + } + } + } + // Larger search area for next time around if not found + range *= 2; + } + } + + if (ret == null) + { + // remember this location was not found so we can quickly not find it next time + m_notFoundLocationCache.Add(px, py); + m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Not found. Remembering loc=<{1},{2}>", LogHeader, px, py); + } + + return ret; } private void InformClientOfNeighbourCompleted(IAsyncResult iar) @@ -2310,22 +2435,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer /// private void GetMegaregionViewRange(out Vector2 swCorner, out Vector2 neCorner) { - Border[] northBorders = Scene.NorthBorders.ToArray(); - Border[] eastBorders = Scene.EastBorders.ToArray(); - Vector2 extent = Vector2.Zero; - for (int i = 0; i < eastBorders.Length; i++) + + if (m_regionCombinerModule != null) { - extent.X = (eastBorders[i].BorderLine.Z > extent.X) ? eastBorders[i].BorderLine.Z : extent.X; - } - for (int i = 0; i < northBorders.Length; i++) - { - extent.Y = (northBorders[i].BorderLine.Z > extent.Y) ? northBorders[i].BorderLine.Z : extent.Y; + Vector2 megaRegionSize = m_regionCombinerModule.GetSizeOfMegaregion(Scene.RegionInfo.RegionID); + extent.X = (float)Util.WorldToRegionLoc((uint)megaRegionSize.X); + extent.Y = (float)Util.WorldToRegionLoc((uint)megaRegionSize.Y); } - // Loss of fraction on purpose - extent.X = ((int)extent.X / (int)Constants.RegionSize); - extent.Y = ((int)extent.Y / (int)Constants.RegionSize); swCorner.X = Scene.RegionInfo.RegionLocX - 1; swCorner.Y = Scene.RegionInfo.RegionLocY - 1; @@ -2340,56 +2458,49 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer /// /// /// - protected List RequestNeighbours(ScenePresence avatar, uint pRegionLocX, uint pRegionLocY) + protected List GetNeighbours(ScenePresence avatar, uint pRegionLocX, uint pRegionLocY) { Scene pScene = avatar.Scene; RegionInfo m_regionInfo = pScene.RegionInfo; + List neighbours; // Leaving this as a "megaregions" computation vs "non-megaregions" computation; it isn't // clear what should be done with a "far view" given that megaregions already extended the // view to include everything in the megaregion if (m_regionCombinerModule == null || !m_regionCombinerModule.IsRootForMegaregion(Scene.RegionInfo.RegionID)) { - int dd = avatar.DrawDistance < Constants.RegionSize ? (int)Constants.RegionSize : (int)avatar.DrawDistance; + // The area to check is as big as the current region. + // We presume all adjacent regions are the same size as this region. + uint dd = Math.Max((uint)avatar.Scene.DefaultDrawDistance, + Math.Max(Scene.RegionInfo.RegionSizeX, Scene.RegionInfo.RegionSizeY)); - dd--; + uint startX = Util.RegionToWorldLoc(pRegionLocX) - dd + Constants.RegionSize/2; + uint startY = Util.RegionToWorldLoc(pRegionLocY) - dd + Constants.RegionSize/2; - // region center - int endX = (int)pRegionLocX * (int)Constants.RegionSize + (int)(Constants.RegionSize / 2); - int endY = (int)pRegionLocY * (int)Constants.RegionSize + (int)(Constants.RegionSize / 2); - - int startX = endX - dd; - int startY = endY - dd; + uint endX = Util.RegionToWorldLoc(pRegionLocX) + dd + Constants.RegionSize/2; + uint endY = Util.RegionToWorldLoc(pRegionLocY) + dd + Constants.RegionSize/2; - endX += dd; - endY += dd; + neighbours + = avatar.Scene.GridService.GetRegionRange( + m_regionInfo.ScopeID, (int)startX, (int)endX, (int)startY, (int)endY); - if (startX < 0) startX = 0; - if (startY < 0) startY = 0; - - List neighbours = - avatar.Scene.GridService.GetRegionRange(m_regionInfo.ScopeID, startX, endX, startY, endY); - - neighbours.RemoveAll(delegate(GridRegion r) { return r.RegionID == m_regionInfo.RegionID; }); - return neighbours; } else { Vector2 swCorner, neCorner; GetMegaregionViewRange(out swCorner, out neCorner); - List neighbours + neighbours = pScene.GridService.GetRegionRange( m_regionInfo.ScopeID, - (int)swCorner.X * (int)Constants.RegionSize, - (int)neCorner.X * (int)Constants.RegionSize, - (int)swCorner.Y * (int)Constants.RegionSize, - (int)neCorner.Y * (int)Constants.RegionSize); - - neighbours.RemoveAll(delegate(GridRegion r) { return r.RegionID == m_regionInfo.RegionID; }); - - return neighbours; + (int)Util.RegionToWorldLoc((uint)swCorner.X), (int)Util.RegionToWorldLoc((uint)neCorner.X), + (int)Util.RegionToWorldLoc((uint)swCorner.Y), (int)Util.RegionToWorldLoc((uint)neCorner.Y)); } + + // The r.RegionFlags == null check only needs to be made for simulators before 2015-01-14 (pre 0.8.1). + neighbours.RemoveAll( r => r.RegionID == m_regionInfo.RegionID ); + + return neighbours; } /* not in use private List NewNeighbours(List currentNeighbours, List previousNeighbours) @@ -2509,8 +2620,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer /// /// This method locates the new region handle and offsets the prim position for the new region /// - /// the attempted out of region position of the scene object /// the scene object that we're crossing + /// the attempted out of region position of the scene object. This position is + /// relative to the region the object currently is in. + /// if 'true', the deletion of the client from the region is not broadcast to the clients public void Cross(SceneObjectGroup grp, Vector3 attemptedPosition, bool silent) { if (grp == null) @@ -2522,209 +2635,49 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer if (scene == null) return; - - int thisx = (int)scene.RegionInfo.RegionLocX; - int thisy = (int)scene.RegionInfo.RegionLocY; - Vector3 EastCross = new Vector3(0.1f, 0, 0); - Vector3 WestCross = new Vector3(-0.1f, 0, 0); - Vector3 NorthCross = new Vector3(0, 0.1f, 0); - Vector3 SouthCross = new Vector3(0, -0.1f, 0); - - - // use this if no borders were crossed! - ulong newRegionHandle - = Util.UIntsToLong((uint)((thisx) * Constants.RegionSize), - (uint)((thisy) * Constants.RegionSize)); - - Vector3 pos = attemptedPosition; - - int changeX = 1; - int changeY = 1; - - if (scene.TestBorderCross(attemptedPosition + WestCross, Cardinals.W)) - { - if (scene.TestBorderCross(attemptedPosition + SouthCross, Cardinals.S)) - { - - Border crossedBorderx = scene.GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W); - - if (crossedBorderx.BorderLine.Z > 0) - { - pos.X = ((pos.X + crossedBorderx.BorderLine.Z)); - changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize); - } - else - pos.X = ((pos.X + Constants.RegionSize)); - - Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); - //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) - - if (crossedBordery.BorderLine.Z > 0) - { - pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); - changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); - } - else - pos.Y = ((pos.Y + Constants.RegionSize)); - - - - newRegionHandle - = Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize), - (uint)((thisy - changeY) * Constants.RegionSize)); - // x - 1 - // y - 1 - } - else if (scene.TestBorderCross(attemptedPosition + NorthCross, Cardinals.N)) - { - Border crossedBorderx = scene.GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W); - - if (crossedBorderx.BorderLine.Z > 0) - { - pos.X = ((pos.X + crossedBorderx.BorderLine.Z)); - changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize); - } - else - pos.X = ((pos.X + Constants.RegionSize)); - - - Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); - //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) - - if (crossedBordery.BorderLine.Z > 0) - { - pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); - changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); - } - else - pos.Y = ((pos.Y + Constants.RegionSize)); - - newRegionHandle - = Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize), - (uint)((thisy + changeY) * Constants.RegionSize)); - // x - 1 - // y + 1 - } - else - { - Border crossedBorderx = scene.GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W); - - if (crossedBorderx.BorderLine.Z > 0) - { - pos.X = ((pos.X + crossedBorderx.BorderLine.Z)); - changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize); - } - else - pos.X = ((pos.X + Constants.RegionSize)); - - newRegionHandle - = Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize), - (uint)(thisy * Constants.RegionSize)); - // x - 1 - } - } - else if (scene.TestBorderCross(attemptedPosition + EastCross, Cardinals.E)) - { - if (scene.TestBorderCross(attemptedPosition + SouthCross, Cardinals.S)) - { - - pos.X = ((pos.X - Constants.RegionSize)); - Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); - //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) - - if (crossedBordery.BorderLine.Z > 0) - { - pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); - changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); - } - else - pos.Y = ((pos.Y + Constants.RegionSize)); - - - newRegionHandle - = Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize), - (uint)((thisy - changeY) * Constants.RegionSize)); - // x + 1 - // y - 1 - } - else if (scene.TestBorderCross(attemptedPosition + NorthCross, Cardinals.N)) - { - pos.X = ((pos.X - Constants.RegionSize)); - pos.Y = ((pos.Y - Constants.RegionSize)); - newRegionHandle - = Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize), - (uint)((thisy + changeY) * Constants.RegionSize)); - // x + 1 - // y + 1 - } - else - { - pos.X = ((pos.X - Constants.RegionSize)); - newRegionHandle - = Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize), - (uint)(thisy * Constants.RegionSize)); - // x + 1 - } - } - else if (scene.TestBorderCross(attemptedPosition + SouthCross, Cardinals.S)) - { - Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); - //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) - - if (crossedBordery.BorderLine.Z > 0) - { - pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); - changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); - } - else - pos.Y = ((pos.Y + Constants.RegionSize)); - - newRegionHandle - = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy - changeY) * Constants.RegionSize)); - // y - 1 - } - else if (scene.TestBorderCross(attemptedPosition + NorthCross, Cardinals.N)) - { - - pos.Y = ((pos.Y - Constants.RegionSize)); - newRegionHandle - = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy + changeY) * Constants.RegionSize)); - // y + 1 - } - - // Offset the positions for the new region across the border + // Remember the old group position in case the region lookup fails so position can be restored. Vector3 oldGroupPosition = grp.RootPart.GroupPosition; - // If we fail to cross the border, then reset the position of the scene object on that border. - uint x = 0, y = 0; - Utils.LongToUInts(newRegionHandle, out x, out y); - GridRegion destination = scene.GridService.GetRegionByPosition(scene.RegionInfo.ScopeID, (int)x, (int)y); + // Compute the absolute position of the object. + double objectWorldLocX = (double)scene.RegionInfo.WorldLocX + attemptedPosition.X; + double objectWorldLocY = (double)scene.RegionInfo.WorldLocY + attemptedPosition.Y; + // Ask the grid service for the region that contains the passed address + GridRegion destination = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID, + objectWorldLocX, objectWorldLocY); + + Vector3 pos = Vector3.Zero; if (destination != null) { - if (CrossPrimGroupIntoNewRegion(destination, pos, grp, silent)) - return; // we did it + // Adjust the object's relative position from the old region (attemptedPosition) + // to be relative to the new region (pos). + pos = new Vector3( (float)(objectWorldLocX - (double)destination.RegionLocX), + (float)(objectWorldLocY - (double)destination.RegionLocY), + attemptedPosition.Z); } - // no one or failed lets go back and tell physics to go on - oldGroupPosition.X = Util.Clamp(oldGroupPosition.X, 0.5f, (float)Constants.RegionSize - 0.5f); - oldGroupPosition.Y = Util.Clamp(oldGroupPosition.Y, 0.5f, (float)Constants.RegionSize - 0.5f); -// oldGroupPosition.Z = Util.Clamp(oldGroupPosition.Z, 0.5f, 4096.0f); + if (destination == null || !CrossPrimGroupIntoNewRegion(destination, pos, grp, silent)) + { + m_log.InfoFormat("[ENTITY TRANSFER MODULE] cross region transfer failed for object {0}", grp.UUID); - grp.AbsolutePosition = oldGroupPosition; - grp.Velocity = Vector3.Zero; + // We are going to move the object back to the old position so long as the old position + // is in the region + oldGroupPosition.X = Util.Clamp(oldGroupPosition.X, 1.0f, (float)(scene.RegionInfo.RegionSizeX - 1)); + oldGroupPosition.Y = Util.Clamp(oldGroupPosition.Y, 1.0f, (float)(scene.RegionInfo.RegionSizeY - 1)); + oldGroupPosition.Z = Util.Clamp(oldGroupPosition.Z, 1.0f, Constants.RegionHeight); - if (grp.RootPart.PhysActor != null) - grp.RootPart.PhysActor.CrossingFailure(); + grp.AbsolutePosition = oldGroupPosition; + grp.Velocity = Vector3.Zero; + if (grp.RootPart.PhysActor != null) + grp.RootPart.PhysActor.CrossingFailure(); - if (grp.RootPart.KeyframeMotion != null) - grp.RootPart.KeyframeMotion.CrossingFailure(); + if (grp.RootPart.KeyframeMotion != null) + grp.RootPart.KeyframeMotion.CrossingFailure(); - grp.ScheduleGroupForFullUpdate(); + grp.ScheduleGroupForFullUpdate(); + } } - - /// /// Move the given scene object into a new region /// diff --git a/OpenSim/Region/CoreModules/Scripting/EMailModules/EmailModule.cs b/OpenSim/Region/CoreModules/Scripting/EMailModules/EmailModule.cs index d943b20a2a..4e7ad7546a 100644 --- a/OpenSim/Region/CoreModules/Scripting/EMailModules/EmailModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/EMailModules/EmailModule.cs @@ -213,8 +213,8 @@ namespace OpenSim.Region.CoreModules.Scripting.EmailModules if (part != null) { ObjectRegionName = s.RegionInfo.RegionName; - uint localX = (s.RegionInfo.RegionLocX * (int)Constants.RegionSize); - uint localY = (s.RegionInfo.RegionLocY * (int)Constants.RegionSize); + uint localX = s.RegionInfo.WorldLocX; + uint localY = s.RegionInfo.WorldLocY; ObjectRegionName = ObjectRegionName + " (" + localX + ", " + localY + ")"; return part; } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs index 31ef79baf4..8ccad399e1 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs @@ -268,11 +268,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid caps.AppendFormat("*** Neighbours of {0} ({1}) ***\n", kvp.Value.RegionName, kvp.Key); List regions = kvp.Value.GetNeighbours(); foreach (GridRegion r in regions) - caps.AppendFormat(" {0} @ {1}-{2}\n", r.RegionName, r.RegionLocX / Constants.RegionSize, r.RegionLocY / Constants.RegionSize); + caps.AppendFormat(" {0} @ {1}-{2}\n", r.RegionName, Util.WorldToRegionLoc((uint)r.RegionLocX), Util.WorldToRegionLoc((uint)r.RegionLocY)); } } MainConsole.Instance.Output(caps.ToString()); } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RegionCache.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RegionCache.cs index 9172536742..ae7628859f 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RegionCache.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RegionCache.cs @@ -66,7 +66,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid return; m_log.DebugFormat("[REGION CACHE]: (on region {0}) Region {1} is up @ {2}-{3}", - m_scene.RegionInfo.RegionName, otherRegion.RegionName, otherRegion.RegionLocX / Constants.RegionSize, otherRegion.RegionLocY / Constants.RegionSize); + m_scene.RegionInfo.RegionName, otherRegion.RegionName, Util.WorldToRegionLoc((uint)otherRegion.RegionLocX), Util.WorldToRegionLoc((uint)otherRegion.RegionLocY)); m_neighbours[otherRegion.RegionHandle] = otherRegion; } @@ -82,11 +82,16 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid return new List(m_neighbours.Values); } + // Get a region given its base coordinates (in meters). + // NOTE: this is NOT 'get a region by some point in the region'. The coordinate MUST + // be the base coordinate of the region. + // The snapping is technically unnecessary but is harmless because regions are always + // multiples of the legacy region size (256). public GridRegion GetRegionByPosition(int x, int y) { uint xsnap = (uint)(x / Constants.RegionSize) * Constants.RegionSize; uint ysnap = (uint)(y / Constants.RegionSize) * Constants.RegionSize; - ulong handle = Utils.UIntsToLong(xsnap, ysnap); + ulong handle = Util.RegionWorldLocToHandle(xsnap, ysnap); if (m_neighbours.ContainsKey(handle)) return m_neighbours[handle]; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/Tests/GridConnectorsTests.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/Tests/GridConnectorsTests.cs index 43381337c7..6a49ca7b1d 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/Tests/GridConnectorsTests.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/Tests/GridConnectorsTests.cs @@ -141,7 +141,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid.Tests Assert.IsNotNull(result, "Retrieved GetRegionByUUID is null"); Assert.That(result.RegionID, Is.EqualTo(new UUID(1)), "Retrieved region's UUID does not match"); - result = m_LocalConnector.GetRegionByPosition(UUID.Zero, 1000 * (int)Constants.RegionSize, 1000 * (int)Constants.RegionSize); + result = m_LocalConnector.GetRegionByPosition(UUID.Zero, (int)Util.RegionToWorldLoc(1000), (int)Util.RegionToWorldLoc(1000)); Assert.IsNotNull(result, "Retrieved GetRegionByPosition is null"); Assert.That(result.RegionLocX, Is.EqualTo(1000 * (int)Constants.RegionSize), "Retrieved region's position does not match"); @@ -197,4 +197,4 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid.Tests Assert.That(results.Count, Is.EqualTo(0), "Retrieved linked regions collection is not the number expected"); } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/MapImage/MapImageServiceModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/MapImage/MapImageServiceModule.cs index 96182cdcb1..da74f30dee 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/MapImage/MapImageServiceModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/MapImage/MapImageServiceModule.cs @@ -57,6 +57,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static string LogHeader = "[MAP IMAGE SERVICE MODULE]"; private bool m_enabled = false; private IMapImageService m_MapService; @@ -192,47 +193,94 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage m_lastrefresh = Util.EnvironmentTickCount(); } + public void UploadMapTile(IScene scene, Bitmap mapTile) + { + m_log.DebugFormat("{0} Upload maptile for {1}", LogHeader, scene.Name); + + // mapTile.Save( // DEBUG DEBUG + // String.Format("maptiles/raw-{0}-{1}-{2}.jpg", regionName, scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY), + // ImageFormat.Jpeg); + // If the region/maptile is legacy sized, just upload the one tile like it has always been done + if (mapTile.Width == Constants.RegionSize && mapTile.Height == Constants.RegionSize) + { + ConvertAndUploadMaptile(mapTile, scene, + scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY); + } + else + { + // For larger regions (varregion) we must cut the region image into legacy sized + // pieces since that is how the maptile system works. + // Note the assumption that varregions are always a multiple of legacy size. + for (uint xx = 0; xx < mapTile.Width; xx += Constants.RegionSize) + { + for (uint yy = 0; yy < mapTile.Height; yy += Constants.RegionSize) + { + // Images are addressed from the upper left corner so have to do funny + // math to pick out the sub-tile since regions are numbered from + // the lower left. + Rectangle rect = new Rectangle( + (int)xx, + mapTile.Height - (int)yy - (int)Constants.RegionSize, + (int)Constants.RegionSize, (int)Constants.RegionSize); + using (Bitmap subMapTile = mapTile.Clone(rect, mapTile.PixelFormat)) + { + ConvertAndUploadMaptile(subMapTile, scene, + scene.RegionInfo.RegionLocX + (xx / Constants.RegionSize), + scene.RegionInfo.RegionLocY + (yy / Constants.RegionSize) + ); + } + } + } + } + } + /// /// /// public void UploadMapTile(IScene scene) { - m_log.DebugFormat("[MAP IMAGE SERVICE MODULE]: upload maptile for {0}", scene.RegionInfo.RegionName); + m_log.DebugFormat("{0}: upload maptile for {1}", LogHeader, scene.RegionInfo.RegionName); // Create a JPG map tile and upload it to the AddMapTile API - byte[] jpgData = Utils.EmptyBytes; IMapImageGenerator tileGenerator = scene.RequestModuleInterface(); if (tileGenerator == null) { - m_log.Warn("[MAP IMAGE SERVICE MODULE]: Cannot upload PNG map tile without an ImageGenerator"); + m_log.WarnFormat("{0} Cannot upload map tile without an ImageGenerator", LogHeader); return; } - using (Image mapTile = tileGenerator.CreateMapTile()) + using (Bitmap mapTile = tileGenerator.CreateMapTile()) { // XXX: The MapImageModule will return a null if the user has chosen not to create map tiles and there // is no static map tile. if (mapTile == null) return; - using (MemoryStream stream = new MemoryStream()) + UploadMapTile(scene, mapTile); + } + } + + private void ConvertAndUploadMaptile(Image tileImage, IScene scene, uint locX, uint locY) + { + byte[] jpgData = Utils.EmptyBytes; + + using (MemoryStream stream = new MemoryStream()) + { + tileImage.Save(stream, ImageFormat.Jpeg); + jpgData = stream.ToArray(); + } + if (jpgData != Utils.EmptyBytes) + { + string reason = string.Empty; + if (!m_MapService.AddMapTile((int)locX, (int)locY, jpgData, scene.RegionInfo.ScopeID, out reason)) { - mapTile.Save(stream, ImageFormat.Jpeg); - jpgData = stream.ToArray(); + m_log.DebugFormat("{0} Unable to upload tile image for {1} at {2}-{3}: {4}", LogHeader, + scene.RegionInfo.RegionName, locX, locY, reason); } } - - if (jpgData == Utils.EmptyBytes) + else { - m_log.WarnFormat("[MAP IMAGE SERVICE MODULE]: Tile image generation failed"); - return; - } - - string reason = string.Empty; - if (!m_MapService.AddMapTile((int)scene.RegionInfo.RegionLocX, (int)scene.RegionInfo.RegionLocY, jpgData, scene.RegionInfo.ScopeID, out reason)) - { - m_log.DebugFormat("[MAP IMAGE SERVICE MODULE]: Unable to upload tile image for {0} at {1}-{2}: {3}", - scene.RegionInfo.RegionName, scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY, reason); + m_log.WarnFormat("{0} Tile image generation failed for region {1}", LogHeader, scene.RegionInfo.RegionName); } } } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Neighbour/LocalNeighbourServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Neighbour/LocalNeighbourServiceConnector.cs index fd89428c8c..bda354fd94 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Neighbour/LocalNeighbourServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Neighbour/LocalNeighbourServiceConnector.cs @@ -125,14 +125,14 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Neighbour public OpenSim.Services.Interfaces.GridRegion HelloNeighbour(ulong regionHandle, RegionInfo thisRegion) { uint x, y; - Utils.LongToUInts(regionHandle, out x, out y); + Util.RegionHandleToRegionLoc(regionHandle, out x, out y); foreach (Scene s in m_Scenes) { if (s.RegionInfo.RegionHandle == regionHandle) { m_log.DebugFormat("[LOCAL NEIGHBOUR SERVICE CONNECTOR]: HelloNeighbour from region {0} to neighbour {1} at {2}-{3}", - thisRegion.RegionName, s.Name, x / Constants.RegionSize, y / Constants.RegionSize); + thisRegion.RegionName, s.Name, x, y ); //m_log.Debug("[NEIGHBOUR CONNECTOR]: Found region to SendHelloNeighbour"); return s.IncomingHelloNeighbour(thisRegion); diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs index 7a844f49f4..cd95ee9784 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs @@ -533,7 +533,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver if (isMegaregion) size = rcMod.GetSizeOfMegaregion(scene.RegionInfo.RegionID); else - size = new Vector2((float)Constants.RegionSize, (float)Constants.RegionSize); + size = new Vector2((float)scene.RegionInfo.RegionSizeX, (float)scene.RegionInfo.RegionSizeY); xtw.WriteElementString("is_megaregion", isMegaregion.ToString()); xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y)); diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs index 92790664ce..4aee6a52c8 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs @@ -64,6 +64,12 @@ namespace OpenSim.Region.CoreModules.World.Land public class LandManagementModule : INonSharedRegionModule { 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/"; @@ -74,15 +80,10 @@ namespace OpenSim.Region.CoreModules.World.Land protected IPrimCountModule m_primCountModule; protected IDialogModule m_Dialog; - // Minimum for parcels to work is 64m even if we don't actually use them. - #pragma warning disable 0429 - private const int landArrayMax = ((int)((int)Constants.RegionSize / 4) >= 64) ? (int)((int)Constants.RegionSize / 4) : 64; - #pragma warning restore 0429 - /// /// Local land ids at specified region co-ordinates (region size / 4) /// - private readonly int[,] m_landIDList = new int[landArrayMax, landArrayMax]; + private int[,] m_landIDList; /// /// Land objects keyed by local id @@ -123,7 +124,7 @@ namespace OpenSim.Region.CoreModules.World.Land public void AddRegion(Scene scene) { m_scene = scene; - m_landIDList.Initialize(); + m_landIDList = new int[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit]; landChannel = new LandChannel(scene, this); parcelInfoCache = new Cache(); @@ -235,7 +236,7 @@ namespace OpenSim.Region.CoreModules.World.Land public void UpdateLandObject(int local_id, LandData data) { LandData newData = data.Copy(); - newData.LocalID = local_id; + newData.LocalID = local_id; ILandObject land; lock (m_landList) @@ -264,7 +265,7 @@ namespace OpenSim.Region.CoreModules.World.Land { m_landList.Clear(); m_lastLandLocalID = LandChannel.START_LAND_LOCAL_ID - 1; - m_landIDList.Initialize(); + m_landIDList = new int[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit]; } } @@ -274,11 +275,11 @@ namespace OpenSim.Region.CoreModules.World.Land /// The parcel created. protected ILandObject CreateDefaultParcel() { - m_log.DebugFormat( - "[LAND MANAGEMENT MODULE]: Creating default parcel for region {0}", m_scene.RegionInfo.RegionName); + m_log.DebugFormat("{0} Creating default parcel for region {1}", LogHeader, m_scene.RegionInfo.RegionName); ILandObject fullSimParcel = new LandObject(UUID.Zero, false, m_scene); - fullSimParcel.SetLandBitmap(fullSimParcel.GetSquareLandBitmap(0, 0, (int)Constants.RegionSize, (int)Constants.RegionSize)); + fullSimParcel.SetLandBitmap(fullSimParcel.GetSquareLandBitmap(0, 0, + (int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY)); fullSimParcel.LandData.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner; fullSimParcel.LandData.ClaimDate = Util.UnixTimeSinceEpoch(); @@ -569,9 +570,9 @@ namespace OpenSim.Region.CoreModules.World.Land new_land.LandData.LocalID = newLandLocalID; bool[,] landBitmap = new_land.GetLandBitmap(); - for (int x = 0; x < landArrayMax; x++) + for (int x = 0; x < landBitmap.GetLength(0); x++) { - for (int y = 0; y < landArrayMax; y++) + for (int y = 0; y < landBitmap.GetLength(1); y++) { if (landBitmap[x, y]) { @@ -601,9 +602,9 @@ namespace OpenSim.Region.CoreModules.World.Land ILandObject land; lock (m_landList) { - for (int x = 0; x < 64; x++) + for (int x = 0; x < m_landIDList.GetLength(0); x++) { - for (int y = 0; y < 64; y++) + for (int y = 0; y < m_landIDList.GetLength(1); y++) { if (m_landIDList[x, y] == local_id) { @@ -656,9 +657,9 @@ namespace OpenSim.Region.CoreModules.World.Land bool[,] landBitmapSlave = slave.GetLandBitmap(); lock (m_landList) { - for (int x = 0; x < 64; x++) + for (int x = 0; x < landBitmapSlave.GetLength(0); x++) { - for (int y = 0; y < 64; y++) + for (int y = 0; y < landBitmapSlave.GetLength(1); y++) { if (landBitmapSlave[x, y]) { @@ -695,20 +696,23 @@ namespace OpenSim.Region.CoreModules.World.Land int x; int y; - if (x_float > Constants.RegionSize || x_float < 0 || y_float > Constants.RegionSize || y_float < 0) + if (x_float >= m_scene.RegionInfo.RegionSizeX || x_float < 0 || y_float >= m_scene.RegionInfo.RegionSizeX || y_float < 0) return null; try { - x = Convert.ToInt32(Math.Floor(Convert.ToDouble(x_float) / 4.0)); - y = Convert.ToInt32(Math.Floor(Convert.ToDouble(y_float) / 4.0)); + x = Convert.ToInt32(Math.Floor(Convert.ToDouble(x_float) / (float)landUnit)); + y = Convert.ToInt32(Math.Floor(Convert.ToDouble(y_float) / (float)landUnit)); } catch (OverflowException) { return null; } - if (x >= 64 || y >= 64 || x < 0 || y < 0) + if (x >= (m_scene.RegionInfo.RegionSizeX / landUnit) + || y >= (m_scene.RegionInfo.RegionSizeY / landUnit) + || x < 0 + || y < 0) { return null; } @@ -740,20 +744,20 @@ namespace OpenSim.Region.CoreModules.World.Land int avx = (int)x; if (avx < 0) avx = 0; - else if (avx >= (int)Constants.RegionSize) + else if (avx >= m_scene.RegionInfo.RegionSizeX) avx = (int)Constants.RegionSize - 1; int avy = (int)y; if (avy < 0) avy = 0; - else if (avy >= (int)Constants.RegionSize) + else if (avy >= m_scene.RegionInfo.RegionSizeY) avy = (int)Constants.RegionSize - 1; lock (m_landIDList) { try { - return m_landList[m_landIDList[avx / 4, avy / 4]]; + return m_landList[m_landIDList[avx / landUnit, avy / landUnit]]; } catch (IndexOutOfRangeException) { @@ -764,7 +768,7 @@ namespace OpenSim.Region.CoreModules.World.Land public ILandObject GetLandObject(int x, int y) { - if (x >= Convert.ToInt32(Constants.RegionSize) || y >= Convert.ToInt32(Constants.RegionSize) || x < 0 || y < 0) + if (x >= m_scene.RegionInfo.RegionSizeX || y >= m_scene.RegionInfo.RegionSizeY || x < 0 || y < 0) { // These exceptions here will cause a lot of complaints from the users specifically because // they happen every time at border crossings @@ -1057,9 +1061,10 @@ namespace OpenSim.Region.CoreModules.World.Land int byteArrayCount = 0; int sequenceID = 0; - for (int y = 0; y < Constants.RegionSize; y += 4) + // Layer data is in landUnit (4m) chunks + for (int y = 0; y < m_scene.RegionInfo.RegionSizeY; y += landUnit) { - for (int x = 0; x < Constants.RegionSize; x += 4) + for (int x = 0; x < m_scene.RegionInfo.RegionSizeX; x += landUnit) { byte tempByte = 0; //This represents the byte for the current 4x4 @@ -1769,7 +1774,7 @@ namespace OpenSim.Region.CoreModules.World.Land { // most likely still cached from building the extLandData entry uint x = 0, y = 0; - Utils.LongToUInts(data.RegionHandle, out x, out y); + Util.RegionHandleToWorldLoc(data.RegionHandle, out x, out y); info = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, (int)x, (int)y); } // we need to transfer the fake parcelID, not the one in landData, so the viewer can match it to the landmark. diff --git a/OpenSim/Region/CoreModules/World/Land/LandObject.cs b/OpenSim/Region/CoreModules/World/Land/LandObject.cs index a3cd4a5673..5858d6c9ad 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandObject.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandObject.cs @@ -45,15 +45,13 @@ namespace OpenSim.Region.CoreModules.World.Land #region Member Variables private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - #pragma warning disable 0429 - private const int landArrayMax = ((int)((int)Constants.RegionSize / 4) >= 64) ? (int)((int)Constants.RegionSize / 4) : 64; - #pragma warning restore 0429 - private bool[,] m_landBitmap = new bool[landArrayMax,landArrayMax]; + private static readonly string LogHeader = "[LAND OBJECT]"; + + private readonly int landUnit = 4; private int m_lastSeqId = 0; private int m_expiryCounter = 0; - protected LandData m_landData = new LandData(); protected Scene m_scene; protected List primsOverMe = new List(); protected Dictionary m_listTransactions = new Dictionary(); @@ -61,6 +59,7 @@ namespace OpenSim.Region.CoreModules.World.Land protected ExpiringCache m_groupMemberCache = new ExpiringCache(); protected TimeSpan m_groupMemberCacheTimeout = TimeSpan.FromSeconds(30); // cache invalidation after 30 seconds + private bool[,] m_landBitmap; public bool[,] LandBitmap { get { return m_landBitmap; } @@ -76,6 +75,7 @@ namespace OpenSim.Region.CoreModules.World.Land return free; } + protected LandData m_landData; public LandData LandData { get { return m_landData; } @@ -94,12 +94,12 @@ namespace OpenSim.Region.CoreModules.World.Land { get { - for (int y = 0; y < landArrayMax; y++) + for (int y = 0; y < LandBitmap.GetLength(1); y++) { - for (int x = 0; x < landArrayMax; x++) + for (int x = 0; x < LandBitmap.GetLength(0); x++) { if (LandBitmap[x, y]) - return new Vector3(x * 4, y * 4, 0); + return new Vector3(x * landUnit, y * landUnit, 0); } } @@ -111,13 +111,13 @@ namespace OpenSim.Region.CoreModules.World.Land { get { - for (int y = landArrayMax - 1; y >= 0; y--) + for (int y = LandBitmap.GetLength(1) - 1; y >= 0; y--) { - for (int x = landArrayMax - 1; x >= 0; x--) + for (int x = LandBitmap.GetLength(0) - 1; x >= 0; x--) { if (LandBitmap[x, y]) { - return new Vector3(x * 4 + 4, y * 4 + 4, 0); + return new Vector3(x * landUnit + landUnit, y * landUnit + landUnit, 0); } } } @@ -128,9 +128,21 @@ namespace OpenSim.Region.CoreModules.World.Land #region Constructors + public LandObject(LandData landData, Scene scene) + { + LandData = landData.Copy(); + m_scene = scene; + } + public LandObject(UUID owner_id, bool is_group_owned, Scene scene) { m_scene = scene; + if (m_scene == null) + LandBitmap = new bool[Constants.RegionSize / landUnit, Constants.RegionSize / landUnit]; + else + LandBitmap = new bool[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit]; + + LandData = new LandData(); LandData.OwnerID = owner_id; if (is_group_owned) LandData.GroupID = owner_id; @@ -155,9 +167,9 @@ namespace OpenSim.Region.CoreModules.World.Land /// Returns true if the piece of land contains the specified point public bool ContainsPoint(int x, int y) { - if (x >= 0 && y >= 0 && x < Constants.RegionSize && y < Constants.RegionSize) + if (x >= 0 && y >= 0 && x < m_scene.RegionInfo.RegionSizeX && y < m_scene.RegionInfo.RegionSizeY) { - return (LandBitmap[x / 4, y / 4] == true); + return LandBitmap[x / landUnit, y / landUnit]; } else { @@ -197,10 +209,10 @@ namespace OpenSim.Region.CoreModules.World.Land else { // Normal Calculations - int parcelMax = (int)((long)LandData.Area + int parcelMax = (int)( (long)LandData.Area * (long)m_scene.RegionInfo.ObjectCapacity * (long)m_scene.RegionInfo.RegionSettings.ObjectBonus - / 65536L); + / (long)(m_scene.RegionInfo.RegionSizeX * m_scene.RegionInfo.RegionSizeY) ); //m_log.DebugFormat("Area: {0}, Capacity {1}, Bonus {2}, Parcel {3}", LandData.Area, m_scene.RegionInfo.ObjectCapacity, m_scene.RegionInfo.RegionSettings.ObjectBonus, parcelMax); return parcelMax; } @@ -231,8 +243,9 @@ namespace OpenSim.Region.CoreModules.World.Land else { //Normal Calculations - int simMax = (int)((long)LandData.SimwideArea - * (long)m_scene.RegionInfo.ObjectCapacity / 65536L); + int simMax = (int)( (long)LandData.SimwideArea + * (long)m_scene.RegionInfo.ObjectCapacity + / (long)(m_scene.RegionInfo.RegionSizeX * m_scene.RegionInfo.RegionSizeY) ); // m_log.DebugFormat("Simwide Area: {0}, Capacity {1}, SimMax {2}", LandData.SimwideArea, m_scene.RegionInfo.ObjectCapacity, simMax); return simMax; } @@ -597,8 +610,8 @@ namespace OpenSim.Region.CoreModules.World.Land try { over = - m_scene.LandChannel.GetLandObject(Util.Clamp((int)Math.Round(avatar.AbsolutePosition.X), 0, ((int)Constants.RegionSize - 1)), - Util.Clamp((int)Math.Round(avatar.AbsolutePosition.Y), 0, ((int)Constants.RegionSize - 1))); + m_scene.LandChannel.GetLandObject(Util.Clamp((int)Math.Round(avatar.AbsolutePosition.X), 0, ((int)m_scene.RegionInfo.RegionSizeX - 1)), + Util.Clamp((int)Math.Round(avatar.AbsolutePosition.Y), 0, ((int)m_scene.RegionInfo.RegionSizeY - 1))); } catch (Exception) { @@ -752,9 +765,9 @@ namespace OpenSim.Region.CoreModules.World.Land int max_y = Int32.MinValue; int tempArea = 0; int x, y; - for (x = 0; x < 64; x++) + for (x = 0; x < LandBitmap.GetLength(0); x++) { - for (y = 0; y < 64; y++) + for (y = 0; y < LandBitmap.GetLength(1); y++) { if (LandBitmap[x, y] == true) { @@ -766,23 +779,25 @@ namespace OpenSim.Region.CoreModules.World.Land max_x = x; if (max_y < y) max_y = y; - tempArea += 16; //16sqm peice of land + tempArea += landUnit * landUnit; //16sqm peice of land } } } + int tx = min_x * landUnit; + if (tx > ((int)m_scene.RegionInfo.RegionSizeX - 1)) + tx = ((int)m_scene.RegionInfo.RegionSizeX - 1); - int tx = min_x * 4; int htx; - if (tx == ((int)Constants.RegionSize)) - htx = tx - 1; + if (tx >= ((int)m_scene.RegionInfo.RegionSizeX)) + htx = (int)m_scene.RegionInfo.RegionSizeX - 1; else htx = tx; - int ty = min_y * 4; + int ty = min_y * landUnit; int hty; - if (ty == ((int)Constants.RegionSize)) - hty = ty - 1; + if (ty >= ((int)m_scene.RegionInfo.RegionSizeY)) + hty = (int)m_scene.RegionInfo.RegionSizeY - 1; else hty = ty; @@ -791,17 +806,17 @@ namespace OpenSim.Region.CoreModules.World.Land (float)(tx), (float)(ty), m_scene != null ? (float)m_scene.Heightmap[htx, hty] : 0); max_x++; - tx = max_x * 4; - if (tx == ((int)Constants.RegionSize)) - htx = tx - 1; + tx = max_x * landUnit; + if (tx >= ((int)m_scene.RegionInfo.RegionSizeX)) + htx = (int)m_scene.RegionInfo.RegionSizeX - 1; else htx = tx; max_y++; ty = max_y * 4; - if (ty == ((int)Constants.RegionSize)) - hty = ty - 1; + if (ty >= ((int)m_scene.RegionInfo.RegionSizeY)) + hty = (int)m_scene.RegionInfo.RegionSizeY - 1; else hty = ty; @@ -819,20 +834,11 @@ namespace OpenSim.Region.CoreModules.World.Land /// /// Sets the land's bitmap manually /// - /// 64x64 block representing where this land is on a map + /// block representing where this land is on a map mapped in a 4x4 meter grid 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(); - } + LandBitmap = bitmap; + ForceUpdateLandInfo(); } /// @@ -846,14 +852,16 @@ namespace OpenSim.Region.CoreModules.World.Land public bool[,] BasicFullRegionLandBitmap() { - return GetSquareLandBitmap(0, 0, (int) Constants.RegionSize, (int) Constants.RegionSize); + return GetSquareLandBitmap(0, 0, (int)m_scene.RegionInfo.RegionSizeX, (int) m_scene.RegionInfo.RegionSizeY); } public bool[,] GetSquareLandBitmap(int start_x, int start_y, int end_x, int end_y) { - bool[,] tempBitmap = new bool[64,64]; + // Empty bitmap for the whole region + bool[,] tempBitmap = new bool[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit]; tempBitmap.Initialize(); + // Fill the bitmap square area specified by state and end tempBitmap = ModifyLandBitmapSquare(tempBitmap, start_x, start_y, end_x, end_y, true); return tempBitmap; } @@ -871,19 +879,13 @@ namespace OpenSim.Region.CoreModules.World.Land public 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 (y = 0; y < land_bitmap.GetLength(1); y++) { - for (x = 0; x < 64; x++) + for (x = 0; x < land_bitmap.GetLength(0); x++) { - if (x >= start_x / 4 && x < end_x / 4 - && y >= start_y / 4 && y < end_y / 4) + if (x >= start_x / landUnit && x < end_x / landUnit + && y >= start_y / landUnit && y < end_y / landUnit) { land_bitmap[x, y] = set_value; } @@ -900,21 +902,21 @@ namespace OpenSim.Region.CoreModules.World.Land /// public bool[,] MergeLandBitmaps(bool[,] bitmap_base, bool[,] bitmap_add) { - if (bitmap_base.GetLength(0) != 64 || bitmap_base.GetLength(1) != 64 || bitmap_base.Rank != 2) + if (bitmap_base.GetLength(0) != bitmap_add.GetLength(0) + || bitmap_base.GetLength(1) != bitmap_add.GetLength(1) + || bitmap_add.Rank != 2 + || 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"); + throw new Exception( + String.Format("{0} MergeLandBitmaps. merging maps not same size. baseSizeXY=<{1},{2}>, addSizeXY=<{3},{4}>", + LogHeader, bitmap_base.GetLength(0), bitmap_base.GetLength(1), bitmap_add.GetLength(0), bitmap_add.GetLength(1)) + ); } int x, y; - for (y = 0; y < 64; y++) + for (y = 0; y < bitmap_base.GetLength(1); y++) { - for (x = 0; x < 64; x++) + for (x = 0; x < bitmap_add.GetLength(0); x++) { if (bitmap_add[x, y]) { @@ -931,14 +933,14 @@ namespace OpenSim.Region.CoreModules.World.Land /// private byte[] ConvertLandBitmapToBytes() { - byte[] tempConvertArr = new byte[512]; + byte[] tempConvertArr = new byte[LandBitmap.GetLength(0) * LandBitmap.GetLength(1) / 8]; int tempByte = 0; - int x, y, i, byteNum = 0; + int i, byteNum = 0; int mask = 1; i = 0; - for (y = 0; y < 64; y++) + for (int y = 0; y < LandBitmap.GetLength(1); y++) { - for (x = 0; x < 64; x++) + for (int x = 0; x < LandBitmap.GetLength(0); x++) { if (LandBitmap[x, y]) tempByte |= mask; @@ -971,25 +973,45 @@ namespace OpenSim.Region.CoreModules.World.Land private bool[,] ConvertBytesToLandBitmap() { - bool[,] tempConvertMap = new bool[landArrayMax, landArrayMax]; + bool[,] tempConvertMap = new bool[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit]; tempConvertMap.Initialize(); byte tempByte = 0; - int x = 0, y = 0, i = 0, bitNum = 0; - for (i = 0; i < 512; i++) + // Math.Min overcomes an old bug that might have made it into the database. Only use the bytes that fit into convertMap. + int bitmapLen = Math.Min(LandData.Bitmap.Length, tempConvertMap.GetLength(0) * tempConvertMap.GetLength(1) / 8); + int xLen = (int)(m_scene.RegionInfo.RegionSizeX / landUnit); + + if (bitmapLen == 512) + { + // Legacy bitmap being passed in. Use the legacy region size + // and only set the lower area of the larger region. + xLen = (int)(Constants.RegionSize / landUnit); + } + // m_log.DebugFormat("{0} ConvertBytesToLandBitmap: bitmapLen={1}, xLen={2}", LogHeader, bitmapLen, xLen); + + int x = 0, y = 0; + for (int i = 0; i < bitmapLen; i++) { tempByte = LandData.Bitmap[i]; - for (bitNum = 0; bitNum < 8; bitNum++) + for (int bitNum = 0; bitNum < 8; bitNum++) { bool bit = Convert.ToBoolean(Convert.ToByte(tempByte >> bitNum) & (byte) 1); - tempConvertMap[x, y] = bit; + try + { + tempConvertMap[x, y] = bit; + } + catch (Exception) + { + m_log.DebugFormat("{0} ConvertBytestoLandBitmap: i={1}, x={2}, y={3}", LogHeader, i, x, y); + } x++; - if (x > 63) + if (x >= xLen) { x = 0; y++; } } } + return tempConvertMap; } diff --git a/OpenSim/Region/CoreModules/World/LegacyMap/MapImageModule.cs b/OpenSim/Region/CoreModules/World/LegacyMap/MapImageModule.cs index bc52a4361e..c7ffeafae9 100644 --- a/OpenSim/Region/CoreModules/World/LegacyMap/MapImageModule.cs +++ b/OpenSim/Region/CoreModules/World/LegacyMap/MapImageModule.cs @@ -102,7 +102,8 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap terrainRenderer.Initialise(m_scene, m_config); - mapbmp = new Bitmap((int)Constants.RegionSize, (int)Constants.RegionSize, System.Drawing.Imaging.PixelFormat.Format24bppRgb); + mapbmp = new Bitmap((int)m_scene.Heightmap.Width, (int)m_scene.Heightmap.Height, + System.Drawing.Imaging.PixelFormat.Format24bppRgb); //long t = System.Environment.TickCount; //for (int i = 0; i < 10; ++i) { terrainRenderer.TerrainToBitmap(mapbmp); @@ -277,7 +278,7 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap private Bitmap DrawObjectVolume(Scene whichScene, Bitmap mapbmp) { int tc = 0; - double[,] hm = whichScene.Heightmap.GetDoubles(); + ITerrainChannel hm = whichScene.Heightmap; tc = Environment.TickCount; m_log.Debug("[MAPTILE]: Generating Maptile Step 2: Object Volume Profile"); EntityBase[] objs = whichScene.GetEntities(); @@ -363,7 +364,7 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap Vector3 pos = part.GetWorldPosition(); // skip prim outside of retion - if (pos.X < 0f || pos.X > 256f || pos.Y < 0f || pos.Y > 256f) + if (!m_scene.PositionIsInCurrentRegion(pos)) continue; // skip prim in non-finite position @@ -388,7 +389,7 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap Vector3 lscale = new Vector3(part.Shape.Scale.X, part.Shape.Scale.Y, part.Shape.Scale.Z); Vector3 scale = new Vector3(); Vector3 tScale = new Vector3(); - Vector3 axPos = new Vector3(pos.X,pos.Y,pos.Z); + Vector3 axPos = new Vector3(pos.X, pos.Y, pos.Z); Quaternion llrot = part.GetWorldRotation(); Quaternion rot = new Quaternion(llrot.W, llrot.X, llrot.Y, llrot.Z); @@ -406,9 +407,14 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap int mapdrawendY = (int)(pos.Y + scale.Y); // If object is beyond the edge of the map, don't draw it to avoid errors - if (mapdrawstartX < 0 || mapdrawstartX > ((int)Constants.RegionSize - 1) || mapdrawendX < 0 || mapdrawendX > ((int)Constants.RegionSize - 1) - || mapdrawstartY < 0 || mapdrawstartY > ((int)Constants.RegionSize - 1) || mapdrawendY < 0 - || mapdrawendY > ((int)Constants.RegionSize - 1)) + if (mapdrawstartX < 0 + || mapdrawstartX > (hm.Width - 1) + || mapdrawendX < 0 + || mapdrawendX > (hm.Width - 1) + || mapdrawstartY < 0 + || mapdrawstartY > (hm.Height - 1) + || mapdrawendY < 0 + || mapdrawendY > (hm.Height - 1)) continue; #region obb face reconstruction part duex @@ -530,11 +536,11 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap for (int i = 0; i < FaceA.Length; i++) { Point[] working = new Point[5]; - working[0] = project(FaceA[i], axPos); - working[1] = project(FaceB[i], axPos); - working[2] = project(FaceD[i], axPos); - working[3] = project(FaceC[i], axPos); - working[4] = project(FaceA[i], axPos); + working[0] = project(hm, FaceA[i], axPos); + working[1] = project(hm, FaceB[i], axPos); + working[2] = project(hm, FaceD[i], axPos); + working[3] = project(hm, FaceC[i], axPos); + working[4] = project(hm, FaceA[i], axPos); face workingface = new face(); workingface.pts = working; @@ -609,17 +615,17 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap return mapbmp; } - private Point project(Vector3 point3d, Vector3 originpos) + private Point project(ITerrainChannel hm, Vector3 point3d, Vector3 originpos) { Point returnpt = new Point(); //originpos = point3d; //int d = (int)(256f / 1.5f); //Vector3 topos = new Vector3(0, 0, 0); - // float z = -point3d.z - topos.z; + // float z = -point3d.z - topos.z; returnpt.X = (int)point3d.X;//(int)((topos.x - point3d.x) / z * d); - returnpt.Y = (int)(((int)Constants.RegionSize - 1) - point3d.Y);//(int)(255 - (((topos.y - point3d.y) / z * d))); + returnpt.Y = (int)((hm.Width - 1) - point3d.Y);//(int)(255 - (((topos.y - point3d.y) / z * d))); return returnpt; } diff --git a/OpenSim/Region/CoreModules/World/LegacyMap/ShadedMapTileRenderer.cs b/OpenSim/Region/CoreModules/World/LegacyMap/ShadedMapTileRenderer.cs index cb06fd4490..708286c906 100644 --- a/OpenSim/Region/CoreModules/World/LegacyMap/ShadedMapTileRenderer.cs +++ b/OpenSim/Region/CoreModules/World/LegacyMap/ShadedMapTileRenderer.cs @@ -31,6 +31,7 @@ using System.Reflection; using log4net; using Nini.Config; using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; namespace OpenSim.Region.CoreModules.World.LegacyMap @@ -39,8 +40,8 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap { private static readonly Color WATER_COLOR = Color.FromArgb(29, 71, 95); - private static readonly ILog m_log = - LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[SHADED MAPTILE RENDERER]"; private Scene m_scene; //private IConfigSource m_config; // not used currently @@ -53,19 +54,26 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap public void TerrainToBitmap(Bitmap mapbmp) { + m_log.DebugFormat("{0} Generating Maptile Step 1: Terrain", LogHeader); int tc = Environment.TickCount; - m_log.Debug("[SHADED MAP TILE RENDERER]: Generating Maptile Step 1: Terrain"); - double[,] hm = m_scene.Heightmap.GetDoubles(); + ITerrainChannel hm = m_scene.Heightmap; + + if (mapbmp.Width != hm.Width || mapbmp.Height != hm.Height) + { + m_log.ErrorFormat("{0} TerrainToBitmap. Passed bitmap wrong dimensions. passed=<{1},{2}>, size=<{3},{4}>", + LogHeader, mapbmp.Width, mapbmp.Height, hm.Width, hm.Height); + } + bool ShadowDebugContinue = true; bool terraincorruptedwarningsaid = false; float low = 255; float high = 0; - for (int x = 0; x < (int)Constants.RegionSize; x++) + for (int x = 0; x < hm.Width; x++) { - for (int y = 0; y < (int)Constants.RegionSize; y++) + for (int y = 0; y < hm.Height; y++) { float hmval = (float)hm[x, y]; if (hmval < low) @@ -77,12 +85,12 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight; - for (int x = 0; x < (int)Constants.RegionSize; x++) + for (int x = 0; x < hm.Width; x++) { - for (int y = 0; y < (int)Constants.RegionSize; y++) + for (int y = 0; y < hm.Height; y++) { // Y flip the cordinates for the bitmap: hf origin is lower left, bm origin is upper left - int yr = ((int)Constants.RegionSize - 1) - y; + int yr = ((int)hm.Height - 1) - y; float heightvalue = (float)hm[x, y]; @@ -109,12 +117,12 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap // . // // Shade the terrain for shadows - if (x < ((int)Constants.RegionSize - 1) && yr < ((int)Constants.RegionSize - 1)) + if (x < (hm.Width - 1) && yr < (hm.Height - 1)) { float hfvalue = (float)hm[x, y]; float hfvaluecompare = 0f; - if ((x + 1 < (int)Constants.RegionSize) && (y + 1 < (int)Constants.RegionSize)) + if ((x + 1 < hm.Width) && (y + 1 < hm.Height)) { hfvaluecompare = (float)hm[x + 1, y + 1]; // light from north-east => look at land height there } @@ -179,7 +187,7 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap if (ShadowDebugContinue) { - if ((x - 1 > 0) && (yr + 1 < (int)Constants.RegionSize)) + if ((x - 1 > 0) && (yr + 1 < hm.Height)) { color = mapbmp.GetPixel(x - 1, yr + 1); int r = color.R; @@ -233,7 +241,7 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap terraincorruptedwarningsaid = true; } Color black = Color.Black; - mapbmp.SetPixel(x, ((int)Constants.RegionSize - y) - 1, black); + mapbmp.SetPixel(x, (hm.Width - y) - 1, black); } } } @@ -242,4 +250,4 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap m_log.Debug("[SHADED MAP TILE RENDERER]: Generating Maptile Step 1: Done in " + (Environment.TickCount - tc) + " ms"); } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/CoreModules/World/Objects/BuySell/BuySellModule.cs b/OpenSim/Region/CoreModules/World/Objects/BuySell/BuySellModule.cs index d4e4c252cc..014c84575e 100644 --- a/OpenSim/Region/CoreModules/World/Objects/BuySell/BuySellModule.cs +++ b/OpenSim/Region/CoreModules/World/Objects/BuySell/BuySellModule.cs @@ -151,14 +151,9 @@ namespace OpenSim.Region.CoreModules.World.Objects.BuySell break; case 2: // Sell a copy - Vector3 inventoryStoredPosition = new Vector3 - (((group.AbsolutePosition.X > (int)Constants.RegionSize) - ? 250 - : group.AbsolutePosition.X) - , - (group.AbsolutePosition.X > (int)Constants.RegionSize) - ? 250 - : group.AbsolutePosition.X, + Vector3 inventoryStoredPosition = new Vector3( + Math.Min(group.AbsolutePosition.X, m_scene.RegionInfo.RegionSizeX - 6), + Math.Min(group.AbsolutePosition.Y, m_scene.RegionInfo.RegionSizeY - 6), group.AbsolutePosition.Z); Vector3 originalPosition = group.AbsolutePosition; diff --git a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs index 616fe98082..928755dcc7 100644 --- a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs +++ b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs @@ -1571,10 +1571,10 @@ namespace OpenSim.Region.CoreModules.World.Permissions float X = position.X; float Y = position.Y; - if (X > ((int)Constants.RegionSize - 1)) - X = ((int)Constants.RegionSize - 1); - if (Y > ((int)Constants.RegionSize - 1)) - Y = ((int)Constants.RegionSize - 1); + if (X > ((int)m_scene.RegionInfo.RegionSizeX - 1)) + X = ((int)m_scene.RegionInfo.RegionSizeX - 1); + if (Y > ((int)m_scene.RegionInfo.RegionSizeY - 1)) + Y = ((int)m_scene.RegionInfo.RegionSizeY - 1); if (X < 0) X = 0; if (Y < 0) diff --git a/OpenSim/Region/CoreModules/World/Terrain/Effects/DefaultTerrainGenerator.cs b/OpenSim/Region/CoreModules/World/Terrain/Effects/DefaultTerrainGenerator.cs index 7186dd7d55..80396c41cc 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/Effects/DefaultTerrainGenerator.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/Effects/DefaultTerrainGenerator.cs @@ -42,7 +42,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Effects for (y = 0; y < map.Height; y++) { map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 3, 0.25) * 10; - double spherFac = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2, Constants.RegionSize / 2, 50) * 0.01; + double spherFac = TerrainUtil.SphericalFactor(x, y, map.Width / 2, map.Height / 2, 50) * 0.01; if (map[x, y] < spherFac) { map[x, y] = spherFac; @@ -53,4 +53,4 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Effects #endregion } -} \ No newline at end of file +} diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs index d78ade5fa7..d5c77ec3e1 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs @@ -67,7 +67,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders { using (Bitmap bitmap = new Bitmap(filename)) { - ITerrainChannel retval = new TerrainChannel(true); + ITerrainChannel retval = new TerrainChannel(w, h); for (int x = 0; x < retval.Width; x++) { diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs index b5c7d336cb..a7e4d9fd79 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs @@ -154,10 +154,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders public ITerrainChannel LoadStream(Stream s) { - + // Set to default size int w = (int)Constants.RegionSize; int h = (int)Constants.RegionSize; + // create a dummy channel (in case data is bad) TerrainChannel retval = new TerrainChannel(w, h); BinaryReader bs = new BinaryReader(s); @@ -165,8 +166,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders bool eof = false; if (Encoding.ASCII.GetString(bs.ReadBytes(16)) == "TERRAGENTERRAIN ") { -// int fileWidth = w; -// int fileHeight = h; // Terragen file while (eof == false) @@ -175,31 +174,29 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders switch (tmp) { case "SIZE": -// int sztmp = bs.ReadInt16() + 1; -// fileWidth = sztmp; -// fileHeight = sztmp; - bs.ReadInt16(); + w = bs.ReadInt16() + 1; + h = w; bs.ReadInt16(); break; case "XPTS": -// fileWidth = bs.ReadInt16(); - bs.ReadInt16(); + w = bs.ReadInt16(); bs.ReadInt16(); break; case "YPTS": -// fileHeight = bs.ReadInt16(); - bs.ReadInt16(); + h = bs.ReadInt16(); bs.ReadInt16(); break; case "ALTW": eof = true; - Int16 heightScale = bs.ReadInt16(); - Int16 baseHeight = bs.ReadInt16(); + // create new channel of proper size (now that we know it) + retval = new TerrainChannel(w, h); + double heightScale = (double)bs.ReadInt16() / 65536.0; + double baseHeight = (double)bs.ReadInt16(); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { - retval[x, y] = baseHeight + bs.ReadInt16() * (double)heightScale / 65536.0; + retval[x, y] = baseHeight + (double)bs.ReadInt16() * heightScale; } } break; @@ -257,17 +254,17 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders bs.Write(enc.GetBytes("TERRAGENTERRAIN ")); bs.Write(enc.GetBytes("SIZE")); - bs.Write(Convert.ToInt16(Constants.RegionSize)); + bs.Write(Convert.ToInt16(map.Width)); bs.Write(Convert.ToInt16(0)); // necessary padding //The XPTS and YPTS chunks are not needed for square regions //but L3DT won't load the terrain file properly without them. bs.Write(enc.GetBytes("XPTS")); - bs.Write(Convert.ToInt16(Constants.RegionSize)); + bs.Write(Convert.ToInt16(map.Width)); bs.Write(Convert.ToInt16(0)); // necessary padding bs.Write(enc.GetBytes("YPTS")); - bs.Write(Convert.ToInt16(Constants.RegionSize)); + bs.Write(Convert.ToInt16(map.Height)); bs.Write(Convert.ToInt16(0)); // necessary padding bs.Write(enc.GetBytes("SCAL")); @@ -283,11 +280,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders bs.Write(Convert.ToInt16(horizontalScale)); // range between max and min bs.Write(Convert.ToInt16(baseHeight)); // base height or mid point + double factor = 65536.0 / horizontalScale; // avoid computing this on each iteration + for (int y = 0; y < map.Height; y++) { for (int x = 0; x < map.Width; x++) { - float elevation = (float)((map[x,y] - baseHeight) * 65536 ) / (float)horizontalScale; // see LoadStream for inverse + float elevation = (float)((map[x,y] - baseHeight) * factor); // see LoadStream for inverse // clamp rounding issues if (elevation > Int16.MaxValue) diff --git a/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/NoiseArea.cs b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/NoiseArea.cs index 630473e162..d3e2533628 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/NoiseArea.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/NoiseArea.cs @@ -45,7 +45,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FloodBrushes { if (fillArea[x, y]) { - double noise = TerrainUtil.PerlinNoise2D((double) x / Constants.RegionSize, (double) y / Constants.RegionSize, 8, 1.0); + double noise = TerrainUtil.PerlinNoise2D((double) x / map.Width, (double) y / map.Height, 8, 1.0); map[x, y] += noise * strength; } @@ -55,4 +55,4 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FloodBrushes #endregion } -} \ No newline at end of file +} diff --git a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/NoiseSphere.cs b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/NoiseSphere.cs index 989b7d8019..e7df3f8d4c 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/NoiseSphere.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/NoiseSphere.cs @@ -53,7 +53,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.PaintBrushes z *= z; z -= ((x - rx) * (x - rx)) + ((y - ry) * (y - ry)); - double noise = TerrainUtil.PerlinNoise2D(x / (double) Constants.RegionSize, y / (double) Constants.RegionSize, 8, 1.0); + double noise = TerrainUtil.PerlinNoise2D(x / (double) map.Width, y / (double) map.Height, 8, 1.0); if (z > 0.0) map[x, y] += noise * z * duration; diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs index 4d738a58ec..9a88804a72 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs @@ -71,6 +71,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); +#pragma warning disable 414 + private static readonly string LogHeader = "[TERRAIN MODULE]"; +#pragma warning restore 414 + private readonly Commander m_commander = new Commander("terrain"); private readonly Dictionary m_floodeffects = @@ -81,8 +85,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain private readonly Dictionary m_painteffects = new Dictionary(); - private ITerrainChannel m_channel; private Dictionary m_plugineffects; + private ITerrainChannel m_channel; private ITerrainChannel m_revert; private Scene m_scene; private volatile bool m_tainted; @@ -90,6 +94,85 @@ namespace OpenSim.Region.CoreModules.World.Terrain private String m_InitialTerrain = "pinhead-island"; + // If true, send terrain patch updates to clients based on their view distance + private bool m_sendTerrainUpdatesByViewDistance = true; + + // Class to keep the per client collection of terrain patches that must be sent. + // A patch is set to 'true' meaning it should be sent to the client. Once the + // patch packet is queued to the client, the bit for that patch is set to 'false'. + private class PatchUpdates + { + private bool[,] updated; // for each patch, whether it needs to be sent to this client + private int updateCount; // number of patches that need to be sent + public ScenePresence Presence; // a reference to the client to send to + public PatchUpdates(TerrainData terrData, ScenePresence pPresence) + { + updated = new bool[terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize]; + updateCount = 0; + Presence = pPresence; + // Initially, send all patches to the client + SetAll(true); + } + // Returns 'true' if there are any patches marked for sending + public bool HasUpdates() + { + return (updateCount > 0); + } + public void SetByXY(int x, int y, bool state) + { + this.SetByPatch(x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, state); + } + public bool GetByPatch(int patchX, int patchY) + { + return updated[patchX, patchY]; + } + public void SetByPatch(int patchX, int patchY, bool state) + { + bool prevState = updated[patchX, patchY]; + if (!prevState && state) + updateCount++; + if (prevState && !state) + updateCount--; + updated[patchX, patchY] = state; + } + public void SetAll(bool state) + { + updateCount = 0; + for (int xx = 0; xx < updated.GetLength(0); xx++) + for (int yy = 0; yy < updated.GetLength(1); yy++) + updated[xx, yy] = state; + if (state) + updateCount = updated.GetLength(0) * updated.GetLength(1); + } + // Logically OR's the terrain data's patch taint map into this client's update map. + public void SetAll(TerrainData terrData) + { + if (updated.GetLength(0) != (terrData.SizeX / Constants.TerrainPatchSize) + || updated.GetLength(1) != (terrData.SizeY / Constants.TerrainPatchSize)) + { + throw new Exception( + String.Format("{0} PatchUpdates.SetAll: patch array not same size as terrain. arr=<{1},{2}>, terr=<{3},{4}>", + LogHeader, updated.GetLength(0), updated.GetLength(1), + terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize) + ); + } + for (int xx = 0; xx < terrData.SizeX; xx += Constants.TerrainPatchSize) + { + for (int yy = 0; yy < terrData.SizeY; yy += Constants.TerrainPatchSize) + { + // Only set tainted. The patch bit may be set if the patch was to be sent later. + if (terrData.IsTaintedAt(xx, yy, false)) + { + this.SetByXY(xx, yy, true); + } + } + } + } + } + + // The flags of which terrain patches to send for each of the ScenePresence's + private Dictionary m_perClientPatchUpdates = new Dictionary(); + /// /// Human readable list of terrain file extensions that are supported. /// @@ -118,7 +201,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain { IConfig terrainConfig = config.Configs["Terrain"]; if (terrainConfig != null) + { m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain); + m_sendTerrainUpdatesByViewDistance = terrainConfig.GetBoolean("SendTerrainUpdatesByViewDistance", m_sendTerrainUpdatesByViewDistance); + } } public void AddRegion(Scene scene) @@ -130,22 +216,24 @@ namespace OpenSim.Region.CoreModules.World.Terrain { if (m_scene.Heightmap == null) { - m_channel = new TerrainChannel(m_InitialTerrain); + m_channel = new TerrainChannel(m_InitialTerrain, (int)m_scene.RegionInfo.RegionSizeX, + (int)m_scene.RegionInfo.RegionSizeY, + (int)m_scene.RegionInfo.RegionSizeZ); m_scene.Heightmap = m_channel; - m_revert = new TerrainChannel(); UpdateRevertMap(); } else { m_channel = m_scene.Heightmap; - m_revert = new TerrainChannel(); UpdateRevertMap(); } m_scene.RegisterModuleInterface(this); m_scene.EventManager.OnNewClient += EventManager_OnNewClient; + m_scene.EventManager.OnClientClosed += EventManager_OnClientClosed; m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick; + m_scene.EventManager.OnFrame += EventManager_OnFrame; } InstallDefaultEffects(); @@ -184,8 +272,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain // remove the commands m_scene.UnregisterModuleCommander(m_commander.Name); // remove the event-handlers + m_scene.EventManager.OnFrame -= EventManager_OnFrame; m_scene.EventManager.OnTerrainTick -= EventManager_OnTerrainTick; m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole; + m_scene.EventManager.OnClientClosed -= EventManager_OnClientClosed; m_scene.EventManager.OnNewClient -= EventManager_OnNewClient; // remove the interface m_scene.UnregisterModuleInterface(this); @@ -230,11 +320,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain try { ITerrainChannel channel = loader.Value.LoadFile(filename); - if (channel.Width != Constants.RegionSize || channel.Height != Constants.RegionSize) + if (channel.Width != m_scene.RegionInfo.RegionSizeX || channel.Height != m_scene.RegionInfo.RegionSizeY) { // TerrainChannel expects a RegionSize x RegionSize map, currently throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}", - Constants.RegionSize, Constants.RegionSize)); + m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY)); } m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height); m_scene.Heightmap = channel; @@ -261,7 +351,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain String.Format("Unable to load heightmap: {0}", e.Message)); } } - CheckForTerrainUpdates(); m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); return; } @@ -309,12 +398,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain LoadFromStream(filename, URIFetch(pathToTerrainHeightmap)); } + public void LoadFromStream(string filename, Stream stream) + { + LoadFromStream(filename, Vector3.Zero, 0f, Vector2.Zero, stream); + } + /// /// Loads a terrain file from a stream and installs it in the scene. /// /// Filename to terrain file. Type is determined by extension. /// - public void LoadFromStream(string filename, Stream stream) + public void LoadFromStream(string filename, Vector3 displacement, + float radianRotation, Vector2 rotationDisplacement, Stream stream) { foreach (KeyValuePair loader in m_loaders) { @@ -325,8 +420,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain try { ITerrainChannel channel = loader.Value.LoadStream(stream); - m_scene.Heightmap = channel; - m_channel = channel; + m_channel.Merge(channel, displacement, radianRotation, rotationDisplacement); UpdateRevertMap(); } catch (NotImplementedException) @@ -337,7 +431,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain } } - CheckForTerrainUpdates(); m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); return; } @@ -406,9 +499,46 @@ namespace OpenSim.Region.CoreModules.World.Terrain } } + // Someone diddled terrain outside the normal code paths. Set the taintedness for all clients. + // ITerrainModule.TaintTerrain() public void TaintTerrain () { - CheckForTerrainUpdates(); + lock (m_perClientPatchUpdates) + { + // Set the flags for all clients so the tainted patches will be sent out + foreach (PatchUpdates pups in m_perClientPatchUpdates.Values) + { + pups.SetAll(m_scene.Heightmap.GetTerrainData()); + } + } + } + + // ITerrainModule.PushTerrain() + public void PushTerrain(IClientAPI pClient) + { + if (m_sendTerrainUpdatesByViewDistance) + { + ScenePresence presence = m_scene.GetScenePresence(pClient.AgentId); + if (presence != null) + { + lock (m_perClientPatchUpdates) + { + PatchUpdates pups; + if (!m_perClientPatchUpdates.TryGetValue(pClient.AgentId, out pups)) + { + // There is a ScenePresence without a send patch map. Create one. + pups = new PatchUpdates(m_scene.Heightmap.GetTerrainData(), presence); + m_perClientPatchUpdates.Add(presence.UUID, pups); + } + pups.SetAll(true); + } + } + } + else + { + // The traditional way is to call into the protocol stack to send them all. + pClient.SendLayerData(new float[10]); + } } #region Plugin Loading Methods @@ -532,6 +662,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain /// public void UpdateRevertMap() { + /* int x; for (x = 0; x < m_channel.Width; x++) { @@ -541,6 +672,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_revert[x, y] = m_channel[x, y]; } } + */ + m_revert = m_channel.MakeCopy(); } /// @@ -567,8 +700,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain { ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY, fileWidth, fileHeight, - (int) Constants.RegionSize, - (int) Constants.RegionSize); + (int) m_scene.RegionInfo.RegionSizeX, + (int) m_scene.RegionInfo.RegionSizeY); m_scene.Heightmap = channel; m_channel = channel; UpdateRevertMap(); @@ -615,8 +748,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain { loader.Value.SaveFile(m_channel, filename, offsetX, offsetY, fileWidth, fileHeight, - (int)Constants.RegionSize, - (int)Constants.RegionSize); + (int)m_scene.RegionInfo.RegionSizeX, + (int)m_scene.RegionInfo.RegionSizeY); MainConsole.Instance.OutputFormat( "Saved terrain from ({0},{1}) to ({2},{3}) from {4} to {5}", @@ -633,8 +766,45 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_scene.RegionInfo.RegionName, filename, m_supportFileExtensionsForTileSave); } + /// + /// Called before processing of every simulation frame. + /// This is used to check to see of any of the terrain is tainted and, if so, schedule + /// updates for all the presences. + /// This also checks to see if there are updates that need to be sent for each presence. + /// This is where the logic is to send terrain updates to clients. + /// + private void EventManager_OnFrame() + { + TerrainData terrData = m_channel.GetTerrainData(); + + bool shouldTaint = false; + for (int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize) + { + for (int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize) + { + if (terrData.IsTaintedAt(x, y)) + { + // Found a patch that was modified. Push this flag into the clients. + SendToClients(terrData, x, y); + shouldTaint = true; + } + } + } + + // This event also causes changes to be sent to the clients + CheckSendingPatchesToClients(); + + // If things changes, generate some events + if (shouldTaint) + { + m_scene.EventManager.TriggerTerrainTainted(); + m_tainted = true; + } + } + /// /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections + /// Called infrequently (like every 5 seconds or so). Best used for storing terrain. /// private void EventManager_OnTerrainTick() { @@ -644,8 +814,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_scene.PhysicsScene.SetTerrain(m_channel.GetFloatsSerialised()); m_scene.SaveTerrain(); - m_scene.EventManager.TriggerTerrainUpdate(); - // Clients who look at the map will never see changes after they looked at the map, so i've commented this out. //m_scene.CreateTerrainTexture(true); } @@ -687,54 +855,48 @@ namespace OpenSim.Region.CoreModules.World.Terrain } /// - /// Checks to see if the terrain has been modified since last check - /// but won't attempt to limit those changes to the limits specified in the estate settings - /// currently invoked by the command line operations in the region server only + /// Installs terrain brush hook to IClientAPI /// - private void CheckForTerrainUpdates() + /// + private void EventManager_OnClientClosed(UUID client, Scene scene) { - CheckForTerrainUpdates(false); - } - - /// - /// Checks to see if the terrain has been modified since last check. - /// If it has been modified, every all the terrain patches are sent to the client. - /// If the call is asked to respect the estate settings for terrain_raise_limit and - /// terrain_lower_limit, it will clamp terrain updates between these values - /// currently invoked by client_OnModifyTerrain only and not the Commander interfaces - /// should height map deltas be limited to the estate settings limits - /// - private void CheckForTerrainUpdates(bool respectEstateSettings) - { - bool shouldTaint = false; - float[] serialised = m_channel.GetFloatsSerialised(); - int x; - for (x = 0; x < m_channel.Width; x += Constants.TerrainPatchSize) + ScenePresence presence = scene.GetScenePresence(client); + if (presence != null) { - int y; - for (y = 0; y < m_channel.Height; y += Constants.TerrainPatchSize) - { - if (m_channel.Tainted(x, y)) - { - // if we should respect the estate settings then - // fixup and height deltas that don't respect them - if (respectEstateSettings && LimitChannelChanges(x, y)) - { - // this has been vetoed, so update - // what we are going to send to the client - serialised = m_channel.GetFloatsSerialised(); - } + presence.ControllingClient.OnModifyTerrain -= client_OnModifyTerrain; + presence.ControllingClient.OnBakeTerrain -= client_OnBakeTerrain; + presence.ControllingClient.OnLandUndo -= client_OnLandUndo; + presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain; + } - SendToClients(serialised, x, y); - shouldTaint = true; + lock (m_perClientPatchUpdates) + m_perClientPatchUpdates.Remove(client); + } + + /// + /// Scan over changes in the terrain and limit height changes. This enforces the + /// non-estate owner limits on rate of terrain editting. + /// Returns 'true' if any heights were limited. + /// + private bool EnforceEstateLimits() + { + TerrainData terrData = m_channel.GetTerrainData(); + + bool wasLimited = false; + for (int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize) + { + for (int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize) + { + if (terrData.IsTaintedAt(x, y, false /* clearOnTest */)) + { + // If we should respect the estate settings then + // fixup and height deltas that don't respect them. + // Note that LimitChannelChanges() modifies the TerrainChannel with the limited height values. + wasLimited |= LimitChannelChanges(terrData, x, y); } } } - if (shouldTaint) - { - m_scene.EventManager.TriggerTerrainTainted(); - m_tainted = true; - } + return wasLimited; } /// @@ -742,11 +904,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain /// are all within the current estate limits /// true if changes were limited, false otherwise /// - private bool LimitChannelChanges(int xStart, int yStart) + private bool LimitChannelChanges(TerrainData terrData, int xStart, int yStart) { bool changesLimited = false; - double minDelta = m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; - double maxDelta = m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; + float minDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; + float maxDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; // loop through the height map for this patch and compare it against // the revert map @@ -754,19 +916,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain { for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++) { - - double requestedHeight = m_channel[x, y]; - double bakedHeight = m_revert[x, y]; - double requestedDelta = requestedHeight - bakedHeight; + float requestedHeight = terrData[x, y]; + float bakedHeight = (float)m_revert[x, y]; + float requestedDelta = requestedHeight - bakedHeight; if (requestedDelta > maxDelta) { - m_channel[x, y] = bakedHeight + maxDelta; + terrData[x, y] = bakedHeight + maxDelta; changesLimited = true; } else if (requestedDelta < minDelta) { - m_channel[x, y] = bakedHeight + minDelta; //as lower is a -ve delta + terrData[x, y] = bakedHeight + minDelta; //as lower is a -ve delta changesLimited = true; } } @@ -794,14 +955,154 @@ namespace OpenSim.Region.CoreModules.World.Terrain /// A copy of the terrain as a 1D float array of size w*h /// The patch corner to send /// The patch corner to send - private void SendToClients(float[] serialised, int x, int y) + private void SendToClients(TerrainData terrData, int x, int y) { - m_scene.ForEachClient( - delegate(IClientAPI controller) - { controller.SendLayerData( - x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, serialised); + if (m_sendTerrainUpdatesByViewDistance) + { + // Add that this patch needs to be sent to the accounting for each client. + lock (m_perClientPatchUpdates) + { + m_scene.ForEachScenePresence(presence => + { + PatchUpdates thisClientUpdates; + if (!m_perClientPatchUpdates.TryGetValue(presence.UUID, out thisClientUpdates)) + { + // There is a ScenePresence without a send patch map. Create one. + thisClientUpdates = new PatchUpdates(terrData, presence); + m_perClientPatchUpdates.Add(presence.UUID, thisClientUpdates); + } + thisClientUpdates.SetByXY(x, y, true); + } + ); + } + } + else + { + // Legacy update sending where the update is sent out as soon as noticed + // We know the actual terrain data passed is ignored. This kludge saves changing IClientAPI. + //float[] heightMap = terrData.GetFloatsSerialized(); + float[] heightMap = new float[10]; + m_scene.ForEachClient( + delegate(IClientAPI controller) + { + controller.SendLayerData(x / Constants.TerrainPatchSize, + y / Constants.TerrainPatchSize, + heightMap); } - ); + ); + } + } + + private class PatchesToSend : IComparable + { + public int PatchX; + public int PatchY; + public float Dist; + public PatchesToSend(int pX, int pY, float pDist) + { + PatchX = pX; + PatchY = pY; + Dist = pDist; + } + public int CompareTo(PatchesToSend other) + { + return Dist.CompareTo(other.Dist); + } + } + + // Called each frame time to see if there are any patches to send to any of the + // ScenePresences. + // Loop through all the per-client info and send any patches necessary. + private void CheckSendingPatchesToClients() + { + lock (m_perClientPatchUpdates) + { + foreach (PatchUpdates pups in m_perClientPatchUpdates.Values) + { + if (pups.HasUpdates()) + { + // There is something that could be sent to this client. + List toSend = GetModifiedPatchesInViewDistance(pups); + if (toSend.Count > 0) + { + // m_log.DebugFormat("{0} CheckSendingPatchesToClient: sending {1} patches to {2} in region {3}", + // LogHeader, toSend.Count, pups.Presence.Name, m_scene.RegionInfo.RegionName); + // Sort the patches to send by the distance from the presence + toSend.Sort(); + /* + foreach (PatchesToSend pts in toSend) + { + pups.Presence.ControllingClient.SendLayerData(pts.PatchX, pts.PatchY, null); + // presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), null, TerrainPatch.LayerType.Land); + } + */ + + int[] xPieces = new int[toSend.Count]; + int[] yPieces = new int[toSend.Count]; + float[] patchPieces = new float[toSend.Count * 2]; + int pieceIndex = 0; + foreach (PatchesToSend pts in toSend) + { + patchPieces[pieceIndex++] = pts.PatchX; + patchPieces[pieceIndex++] = pts.PatchY; + } + pups.Presence.ControllingClient.SendLayerData(-toSend.Count, 0, patchPieces); + } + } + } + } + } + + private List GetModifiedPatchesInViewDistance(PatchUpdates pups) + { + List ret = new List(); + + ScenePresence presence = pups.Presence; + if (presence == null) + return ret; + + // Compute the area of patches within our draw distance + int startX = (((int) (presence.AbsolutePosition.X - presence.DrawDistance))/Constants.TerrainPatchSize) - 2; + startX = Math.Max(startX, 0); + startX = Math.Min(startX, (int)m_scene.RegionInfo.RegionSizeX/Constants.TerrainPatchSize); + int startY = (((int) (presence.AbsolutePosition.Y - presence.DrawDistance))/Constants.TerrainPatchSize) - 2; + startY = Math.Max(startY, 0); + startY = Math.Min(startY, (int)m_scene.RegionInfo.RegionSizeY/Constants.TerrainPatchSize); + int endX = (((int) (presence.AbsolutePosition.X + presence.DrawDistance))/Constants.TerrainPatchSize) + 2; + endX = Math.Max(endX, 0); + endX = Math.Min(endX, (int)m_scene.RegionInfo.RegionSizeX/Constants.TerrainPatchSize); + int endY = (((int) (presence.AbsolutePosition.Y + presence.DrawDistance))/Constants.TerrainPatchSize) + 2; + endY = Math.Max(endY, 0); + endY = Math.Min(endY, (int)m_scene.RegionInfo.RegionSizeY/Constants.TerrainPatchSize); + // m_log.DebugFormat("{0} GetModifiedPatchesInViewDistance. rName={1}, ddist={2}, apos={3}, start=<{4},{5}>, end=<{6},{7}>", + // LogHeader, m_scene.RegionInfo.RegionName, + // presence.DrawDistance, presence.AbsolutePosition, + // startX, startY, endX, endY); + for (int x = startX; x < endX; x++) + { + for (int y = startY; y < endY; y++) + { + //Need to make sure we don't send the same ones over and over + Vector3 presencePos = presence.AbsolutePosition; + Vector3 patchPos = new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, presencePos.Z); + if (pups.GetByPatch(x, y)) + { + //Check which has less distance, camera or avatar position, both have to be done. + //Its not a radius, its a diameter and we add 50 so that it doesn't look like it cuts off + if (Util.DistanceLessThan(presencePos, patchPos, presence.DrawDistance + 50) + || Util.DistanceLessThan(presence.CameraPosition, patchPos, presence.DrawDistance + 50)) + { + //They can see it, send it to them + pups.SetByPatch(x, y, false); + float dist = Vector3.DistanceSquared(presencePos, patchPos); + ret.Add(new PatchesToSend(x, y, dist)); + //Wait and send them all at once + // pups.client.SendLayerData(x, y, null); + } + } + } + } + return ret; } private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action, @@ -846,7 +1147,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_painteffects[(StandardTerrainEffects) action].PaintEffect( m_channel, allowMask, west, south, height, size, seconds); - CheckForTerrainUpdates(!god); //revert changes outside estate limits + //revert changes outside estate limits + if (!god) + EnforceEstateLimits(); } } else @@ -884,10 +1187,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain if (allowed) { StoreUndoState(); - m_floodeffects[(StandardTerrainEffects) action].FloodEffect( - m_channel, fillArea, size); + m_floodeffects[(StandardTerrainEffects) action].FloodEffect(m_channel, fillArea, size); - CheckForTerrainUpdates(!god); //revert changes outside estate limits + //revert changes outside estate limits + if (!god) + EnforceEstateLimits(); } } else @@ -911,7 +1215,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain protected void client_OnUnackedTerrain(IClientAPI client, int patchX, int patchY) { //m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY); - client.SendLayerData(patchX, patchY, m_scene.Heightmap.GetFloatsSerialised()); + // SendLayerData does not use the heightmap parameter. This kludge is so as to not change IClientAPI. + float[] heightMap = new float[10]; + client.SendLayerData(patchX, patchY, heightMap); } private void StoreUndoState() @@ -938,7 +1244,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain private void InterfaceLoadFile(Object[] args) { LoadFromFile((string) args[0]); - CheckForTerrainUpdates(); } private void InterfaceLoadTileFile(Object[] args) @@ -948,7 +1253,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain (int) args[2], (int) args[3], (int) args[4]); - CheckForTerrainUpdates(); } private void InterfaceSaveFile(Object[] args) @@ -977,7 +1281,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain for (y = 0; y < m_channel.Height; y++) m_channel[x, y] = m_revert[x, y]; - CheckForTerrainUpdates(); } private void InterfaceFlipTerrain(Object[] args) @@ -986,28 +1289,28 @@ namespace OpenSim.Region.CoreModules.World.Terrain if (direction.ToLower().StartsWith("y")) { - for (int x = 0; x < Constants.RegionSize; x++) + for (int x = 0; x < m_channel.Width; x++) { - for (int y = 0; y < Constants.RegionSize / 2; y++) + for (int y = 0; y < m_channel.Height / 2; y++) { double height = m_channel[x, y]; - double flippedHeight = m_channel[x, (int)Constants.RegionSize - 1 - y]; + double flippedHeight = m_channel[x, (int)m_channel.Height - 1 - y]; m_channel[x, y] = flippedHeight; - m_channel[x, (int)Constants.RegionSize - 1 - y] = height; + m_channel[x, (int)m_channel.Height - 1 - y] = height; } } } else if (direction.ToLower().StartsWith("x")) { - for (int y = 0; y < Constants.RegionSize; y++) + for (int y = 0; y < m_channel.Height; y++) { - for (int x = 0; x < Constants.RegionSize / 2; x++) + for (int x = 0; x < m_channel.Width / 2; x++) { double height = m_channel[x, y]; - double flippedHeight = m_channel[(int)Constants.RegionSize - 1 - x, y]; + double flippedHeight = m_channel[(int)m_channel.Width - 1 - x, y]; m_channel[x, y] = flippedHeight; - m_channel[(int)Constants.RegionSize - 1 - x, y] = height; + m_channel[(int)m_channel.Width - 1 - x, y] = height; } } @@ -1016,9 +1319,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain { m_log.Error("Unrecognised direction - need x or y"); } - - - CheckForTerrainUpdates(); } private void InterfaceRescaleTerrain(Object[] args) @@ -1076,7 +1376,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain } } - CheckForTerrainUpdates(); } } @@ -1087,7 +1386,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain for (x = 0; x < m_channel.Width; x++) for (y = 0; y < m_channel.Height; y++) m_channel[x, y] += (double) args[0]; - CheckForTerrainUpdates(); } private void InterfaceMultiplyTerrain(Object[] args) @@ -1096,7 +1394,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain for (x = 0; x < m_channel.Width; x++) for (y = 0; y < m_channel.Height; y++) m_channel[x, y] *= (double) args[0]; - CheckForTerrainUpdates(); } private void InterfaceLowerTerrain(Object[] args) @@ -1105,17 +1402,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain for (x = 0; x < m_channel.Width; x++) for (y = 0; y < m_channel.Height; y++) m_channel[x, y] -= (double) args[0]; - CheckForTerrainUpdates(); } - private void InterfaceFillTerrain(Object[] args) + public void InterfaceFillTerrain(Object[] args) { int x, y; for (x = 0; x < m_channel.Width; x++) for (y = 0; y < m_channel.Height; y++) m_channel[x, y] = (double) args[0]; - CheckForTerrainUpdates(); } private void InterfaceMinTerrain(Object[] args) @@ -1128,7 +1423,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]); } } - CheckForTerrainUpdates(); } private void InterfaceMaxTerrain(Object[] args) @@ -1141,7 +1435,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]); } } - CheckForTerrainUpdates(); } private void InterfaceShowDebugStats(Object[] args) @@ -1204,7 +1497,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain if (m_plugineffects.ContainsKey(firstArg)) { m_plugineffects[firstArg].RunEffect(m_channel); - CheckForTerrainUpdates(); } else { diff --git a/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs b/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs index be719ea2d3..062d7fff2d 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs @@ -40,10 +40,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests [Test] public void BrushTest() { + int midRegion = (int)Constants.RegionSize / 2; + + // Create a mask that covers only the left half of the region bool[,] allowMask = new bool[(int)Constants.RegionSize, 256]; int x; int y; - for (x = 0; x < (int)((int)Constants.RegionSize * 0.5f); x++) + for (x = 0; x < midRegion; x++) { for (y = 0; y < (int)Constants.RegionSize; y++) { @@ -57,13 +60,12 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests TerrainChannel map = new TerrainChannel((int)Constants.RegionSize, (int)Constants.RegionSize); ITerrainPaintableEffect effect = new RaiseSphere(); - effect.PaintEffect(map, allowMask, (int)Constants.RegionSize * 0.5f, (int)Constants.RegionSize * 0.5f, -1.0, 2, 0.1); - Assert.That(map[127, (int)((int)Constants.RegionSize * 0.5f)] > 0.0, "Raise brush should raising value at this point (127,128)."); - Assert.That(map[124, (int)((int)Constants.RegionSize * 0.5f)] > 0.0, "Raise brush should raising value at this point (124,128)."); - Assert.That(map[123, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Raise brush should not change value at this point (123,128)."); - Assert.That(map[128, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Raise brush should not change value at this point (128,128)."); - Assert.That(map[0, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Raise brush should not change value at this point (0,128)."); - + effect.PaintEffect(map, allowMask, midRegion, midRegion, -1.0, 2, 6.0); + Assert.That(map[127, midRegion] > 0.0, "Raise brush should raising value at this point (127,128)."); + Assert.That(map[125, midRegion] > 0.0, "Raise brush should raising value at this point (124,128)."); + Assert.That(map[120, midRegion] == 0.0, "Raise brush should not change value at this point (120,128)."); + Assert.That(map[128, midRegion] == 0.0, "Raise brush should not change value at this point (128,128)."); + Assert.That(map[0, midRegion] == 0.0, "Raise brush should not change value at this point (0,128)."); // // Test LowerSphere // @@ -77,13 +79,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests } effect = new LowerSphere(); - effect.PaintEffect(map, allowMask, ((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), -1.0, 2, 6.0); - Assert.That(map[127, (int)((int)Constants.RegionSize * 0.5f)] >= 0.0, "Lower should not lowering value below 0.0 at this point (127,128)."); - Assert.That(map[127, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Lower brush should lowering value to 0.0 at this point (127,128)."); - Assert.That(map[124, (int)((int)Constants.RegionSize * 0.5f)] < 1.0, "Lower brush should lowering value at this point (124,128)."); - Assert.That(map[123, (int)((int)Constants.RegionSize * 0.5f)] == 1.0, "Lower brush should not change value at this point (123,128)."); - Assert.That(map[128, (int)((int)Constants.RegionSize * 0.5f)] == 1.0, "Lower brush should not change value at this point (128,128)."); - Assert.That(map[0, (int)((int)Constants.RegionSize * 0.5f)] == 1.0, "Lower brush should not change value at this point (0,128)."); + effect.PaintEffect(map, allowMask, midRegion, midRegion, -1.0, 2, 6.0); + Assert.That(map[127, midRegion] >= 0.0, "Lower should not lowering value below 0.0 at this point (127,128)."); + Assert.That(map[127, midRegion] == 0.0, "Lower brush should lowering value to 0.0 at this point (127,128)."); + Assert.That(map[125, midRegion] < 1.0, "Lower brush should lowering value at this point (124,128)."); + Assert.That(map[120, midRegion] == 1.0, "Lower brush should not change value at this point (120,128)."); + Assert.That(map[128, midRegion] == 1.0, "Lower brush should not change value at this point (128,128)."); + Assert.That(map[0, midRegion] == 1.0, "Lower brush should not change value at this point (0,128)."); } [Test] diff --git a/OpenSim/Region/Framework/Interfaces/IRegionCombinerModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionCombinerModule.cs index e03ac5a26f..c6f531edd2 100644 --- a/OpenSim/Region/Framework/Interfaces/IRegionCombinerModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IRegionCombinerModule.cs @@ -55,5 +55,10 @@ namespace OpenSim.Region.Framework.Interfaces /// Currently, will throw an exception if this does not match a root region. /// Vector2 GetSizeOfMegaregion(UUID regionId); + + /// + /// Tests to see of position (relative to the region) is within the megaregion + /// + bool PositionIsInMegaregion(UUID currentRegion, int xx, int yy); } } \ No newline at end of file diff --git a/OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs b/OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs index 3e97a7ae3d..13358cb360 100644 --- a/OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs +++ b/OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs @@ -68,13 +68,22 @@ namespace OpenSim.Region.Framework.Interfaces /// /// HeightField data /// region UUID + void StoreTerrain(TerrainData terrain, UUID regionID); + + // Legacy version kept for downward compabibility void StoreTerrain(double[,] terrain, UUID regionID); /// /// Load the latest terrain revision from region storage /// /// the region UUID + /// the X dimension of the region being filled + /// the Y dimension of the region being filled + /// the Z dimension of the region being filled /// Heightfield data + TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ); + + // Legacy version kept for downward compabibility double[,] LoadTerrain(UUID regionID); void StoreLandObject(ILandObject Parcel); diff --git a/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs b/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs index 17bd48be67..e09f775642 100644 --- a/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs +++ b/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs @@ -79,13 +79,22 @@ namespace OpenSim.Region.Framework.Interfaces /// /// HeightField data /// region UUID + void StoreTerrain(TerrainData terrain, UUID regionID); + + // Legacy version kept for downward compabibility void StoreTerrain(double[,] terrain, UUID regionID); /// /// Load the latest terrain revision from region storage /// /// the region UUID + /// the X dimension of the terrain being filled + /// the Y dimension of the terrain being filled + /// the Z dimension of the terrain being filled /// Heightfield data + TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ); + + // Legacy version kept for downward compabibility double[,] LoadTerrain(UUID regionID); void StoreLandObject(ILandObject Parcel); @@ -136,4 +145,5 @@ namespace OpenSim.Region.Framework.Interfaces void Shutdown(); } + } diff --git a/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs b/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs index e467701351..f660b8dbe0 100644 --- a/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs +++ b/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs @@ -25,13 +25,23 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using OpenSim.Framework; +using OpenMetaverse; + namespace OpenSim.Region.Framework.Interfaces { public interface ITerrainChannel { - int Height { get; } + int Width { get;} // X dimension + int Height { get;} // Y dimension + int Altitude { get;} // Z dimension + double this[int x, int y] { get; set; } - int Width { get; } + + float GetHeightAtXYZ(float x, float y, float z); + + // Return the packaged terrain data for passing into lower levels of communication + TerrainData GetTerrainData(); /// /// Squash the entire heightmap into a single dimensioned array @@ -40,9 +50,14 @@ namespace OpenSim.Region.Framework.Interfaces float[] GetFloatsSerialised(); double[,] GetDoubles(); + + // Check if a location has been updated. Clears the taint flag as a side effect. bool Tainted(int x, int y); + ITerrainChannel MakeCopy(); string SaveToXmlString(); void LoadFromXmlString(string data); + // Merge some terrain into this channel + void Merge(ITerrainChannel newTerrain, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement); } } diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index b45cc4d900..4ab5a4adee 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -2221,14 +2221,9 @@ namespace OpenSim.Region.Framework.Scenes itemID = UUID.Zero; if (grp != null) { - Vector3 inventoryStoredPosition = new Vector3 - (((grp.AbsolutePosition.X > (int)Constants.RegionSize) - ? 250 - : grp.AbsolutePosition.X) - , - (grp.AbsolutePosition.X > (int)Constants.RegionSize) - ? 250 - : grp.AbsolutePosition.X, + Vector3 inventoryStoredPosition = new Vector3( + Math.Min(grp.AbsolutePosition.X, RegionInfo.RegionSizeX - 6), + Math.Min(grp.AbsolutePosition.Y, RegionInfo.RegionSizeY - 6), grp.AbsolutePosition.Z); Vector3 originalPosition = grp.AbsolutePosition; diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index eb34f557aa..46c904858c 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -6,7 +6,7 @@ * 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 copyrightD + * * 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 @@ -103,7 +103,29 @@ namespace OpenSim.Region.Framework.Scenes /// /// If false then physical objects are disabled, though collisions will continue as normal. /// - public bool PhysicsEnabled { get; set; } + public bool PhysicsEnabled + { + get + { + return m_physicsEnabled; + } + + set + { + m_physicsEnabled = value; + + if (PhysicsScene != null) + { + IPhysicsParameters physScene = PhysicsScene as IPhysicsParameters; + + if (physScene != null) + physScene.SetPhysicsParameter( + "Active", m_physicsEnabled.ToString(), PhysParameterEntry.APPLY_TO_NONE); + } + } + } + + private bool m_physicsEnabled; /// /// If false then scripts are not enabled on the smiulator @@ -160,11 +182,6 @@ namespace OpenSim.Region.Framework.Scenes /// public SimStatsReporter StatsReporter { get; private set; } - public List NorthBorders = new List(); - public List EastBorders = new List(); - public List SouthBorders = new List(); - public List WestBorders = new List(); - /// /// Controls whether physics can be applied to prims. Even if false, prims still have entries in a /// PhysicsScene in order to perform collision detection @@ -204,15 +221,16 @@ namespace OpenSim.Region.Framework.Scenes /// public int m_linksetCapacity = 0; + public bool m_clampPrimSize; + public bool m_trustBinaries; + public bool m_allowScriptCrossings = true; + /// /// Max prims an Physical object will hold /// /// public int m_linksetPhysCapacity = 0; - public bool m_clampPrimSize; - public bool m_trustBinaries; - public bool m_allowScriptCrossings; public bool m_useFlySlow; public bool m_useTrashOnDelete = true; @@ -364,7 +382,6 @@ namespace OpenSim.Region.Framework.Scenes // TODO: Possibly stop other classes being able to manipulate this directly. private SceneGraph m_sceneGraph; - private volatile int m_bordersLocked; private readonly Timer m_restartTimer = new Timer(15000); // Wait before firing private volatile bool m_backingup; private Dictionary m_returns = new Dictionary(); @@ -446,18 +463,6 @@ namespace OpenSim.Region.Framework.Scenes set { m_splitRegionID = value; } } - public bool BordersLocked - { - get { return m_bordersLocked == 1; } - set - { - if (value == true) - m_bordersLocked = 1; - else - m_bordersLocked = 0; - } - } - public new float TimeDilation { get { return m_sceneGraph.PhysicsScene.TimeDilation; } @@ -1075,28 +1080,6 @@ namespace OpenSim.Region.Framework.Scenes PeriodicBackup = true; UseBackup = true; - BordersLocked = true; - Border northBorder = new Border(); - northBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue, RegionInfo.RegionSizeY); //<--- - northBorder.CrossDirection = Cardinals.N; - NorthBorders.Add(northBorder); - - Border southBorder = new Border(); - southBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue,0); //---> - southBorder.CrossDirection = Cardinals.S; - SouthBorders.Add(southBorder); - - Border eastBorder = new Border(); - eastBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue, RegionInfo.RegionSizeX); //<--- - eastBorder.CrossDirection = Cardinals.E; - EastBorders.Add(eastBorder); - - Border westBorder = new Border(); - westBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue,0); //---> - westBorder.CrossDirection = Cardinals.W; - WestBorders.Add(westBorder); - BordersLocked = false; - m_eventManager = new EventManager(); m_permissions = new ScenePermissions(this); @@ -1975,7 +1958,7 @@ namespace OpenSim.Region.Framework.Scenes { try { - double[,] map = SimulationDataService.LoadTerrain(RegionInfo.RegionID); + TerrainData map = SimulationDataService.LoadTerrain(RegionInfo.RegionID, (int)RegionInfo.RegionSizeX, (int)RegionInfo.RegionSizeY, (int)RegionInfo.RegionSizeZ); if (map == null) { // This should be in the Terrain module, but it isn't because @@ -1986,7 +1969,7 @@ namespace OpenSim.Region.Framework.Scenes m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain); m_log.InfoFormat("[TERRAIN]: No default terrain. Generating a new terrain {0}.", m_InitialTerrain); - Heightmap = new TerrainChannel(m_InitialTerrain); + Heightmap = new TerrainChannel(m_InitialTerrain, (int)RegionInfo.RegionSizeX, (int)RegionInfo.RegionSizeY, (int)RegionInfo.RegionSizeZ); SimulationDataService.StoreTerrain(Heightmap.GetDoubles(), RegionInfo.RegionID); } @@ -2611,185 +2594,35 @@ namespace OpenSim.Region.Framework.Scenes EntityTransferModule.Cross(grp, attemptedPosition, silent); } - public Border GetCrossedBorder(Vector3 position, Cardinals gridline) + // Simple test to see if a position is in the current region. + // This test is mostly used to see if a region crossing is necessary. + // Assuming the position is relative to the region so anything outside its bounds. + // Return 'true' if position inside region. + public bool PositionIsInCurrentRegion(Vector3 pos) { - if (BordersLocked) + bool ret = false; + int xx = (int)Math.Floor(pos.X); + int yy = (int)Math.Floor(pos.Y); + if (xx < 0 || yy < 0) + return false; + + IRegionCombinerModule regionCombinerModule = RequestModuleInterface(); + if (regionCombinerModule == null) { - switch (gridline) - { - case Cardinals.N: - lock (NorthBorders) - { - foreach (Border b in NorthBorders) - { - if (b.TestCross(position)) - return b; - } - } - break; - case Cardinals.S: - lock (SouthBorders) - { - foreach (Border b in SouthBorders) - { - if (b.TestCross(position)) - return b; - } - } - - break; - case Cardinals.E: - lock (EastBorders) - { - foreach (Border b in EastBorders) - { - if (b.TestCross(position)) - return b; - } - } - - break; - case Cardinals.W: - - lock (WestBorders) - { - foreach (Border b in WestBorders) - { - if (b.TestCross(position)) - return b; - } - } - break; - - } + // Regular region. Just check for region size + if (xx < RegionInfo.RegionSizeX && yy < RegionInfo.RegionSizeY ) + ret = true; } else { - switch (gridline) - { - case Cardinals.N: - foreach (Border b in NorthBorders) - { - if (b.TestCross(position)) - return b; - } - - break; - case Cardinals.S: - foreach (Border b in SouthBorders) - { - if (b.TestCross(position)) - return b; - } - break; - case Cardinals.E: - foreach (Border b in EastBorders) - { - if (b.TestCross(position)) - return b; - } - - break; - case Cardinals.W: - foreach (Border b in WestBorders) - { - if (b.TestCross(position)) - return b; - } - break; - - } + // We're in a mega-region so see if we are still in that larger region + ret = regionCombinerModule.PositionIsInMegaregion(this.RegionInfo.RegionID, xx, yy); } - return null; + return ret; + } - public bool TestBorderCross(Vector3 position, Cardinals border) - { - if (BordersLocked) - { - switch (border) - { - case Cardinals.N: - lock (NorthBorders) - { - foreach (Border b in NorthBorders) - { - if (b.TestCross(position)) - return true; - } - } - break; - case Cardinals.E: - lock (EastBorders) - { - foreach (Border b in EastBorders) - { - if (b.TestCross(position)) - return true; - } - } - break; - case Cardinals.S: - lock (SouthBorders) - { - foreach (Border b in SouthBorders) - { - if (b.TestCross(position)) - return true; - } - } - break; - case Cardinals.W: - lock (WestBorders) - { - foreach (Border b in WestBorders) - { - if (b.TestCross(position)) - return true; - } - } - break; - } - } - else - { - switch (border) - { - case Cardinals.N: - foreach (Border b in NorthBorders) - { - if (b.TestCross(position)) - return true; - } - break; - case Cardinals.E: - foreach (Border b in EastBorders) - { - if (b.TestCross(position)) - return true; - } - break; - case Cardinals.S: - foreach (Border b in SouthBorders) - { - if (b.TestCross(position)) - return true; - } - break; - case Cardinals.W: - foreach (Border b in WestBorders) - { - if (b.TestCross(position)) - return true; - } - break; - } - } - return false; - } - - /// /// Called when objects or attachments cross the border, or teleport, between regions. /// @@ -4116,60 +3949,11 @@ namespace OpenSim.Region.Framework.Scenes { // CleanDroppedAttachments(); - if (TestBorderCross(acd.startpos, Cardinals.E)) - { - Border crossedBorder = GetCrossedBorder(acd.startpos, Cardinals.E); - acd.startpos.X = crossedBorder.BorderLine.Z - 1; - } - - if (TestBorderCross(acd.startpos, Cardinals.N)) - { - Border crossedBorder = GetCrossedBorder(acd.startpos, Cardinals.N); - acd.startpos.Y = crossedBorder.BorderLine.Z - 1; - } - - //Mitigate http://opensimulator.org/mantis/view.php?id=3522 - // Check if start position is outside of region - // If it is, check the Z start position also.. if not, leave it alone. - if (BordersLocked) - { - lock (EastBorders) - { - if (acd.startpos.X > EastBorders[0].BorderLine.Z) - { - m_log.Warn("FIX AGENT POSITION"); - acd.startpos.X = EastBorders[0].BorderLine.Z * 0.5f; - if (acd.startpos.Z > 720) - acd.startpos.Z = 720; - } - } - lock (NorthBorders) - { - if (acd.startpos.Y > NorthBorders[0].BorderLine.Z) - { - m_log.Warn("FIX Agent POSITION"); - acd.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f; - if (acd.startpos.Z > 720) - acd.startpos.Z = 720; - } - } - } else - { - if (acd.startpos.X > EastBorders[0].BorderLine.Z) - { - m_log.Warn("FIX AGENT POSITION"); - acd.startpos.X = EastBorders[0].BorderLine.Z * 0.5f; - if (acd.startpos.Z > 720) - acd.startpos.Z = 720; - } - if (acd.startpos.Y > NorthBorders[0].BorderLine.Z) - { - m_log.Warn("FIX Agent POSITION"); - acd.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f; - if (acd.startpos.Z > 720) - acd.startpos.Z = 720; - } - } + // Make sure avatar position is in the region (why it wouldn't be is a mystery but do sanity checking) + if (acd.startpos.X < 0) acd.startpos.X = 1f; + if (acd.startpos.X >= RegionInfo.RegionSizeX) acd.startpos.X = RegionInfo.RegionSizeX - 1f; + if (acd.startpos.Y < 0) acd.startpos.Y = 1f; + if (acd.startpos.Y >= RegionInfo.RegionSizeY) acd.startpos.Y = RegionInfo.RegionSizeY - 1f; // m_log.DebugFormat( // "[SCENE]: Found telehub object {0} for new user connection {1} to {2}", @@ -4883,44 +4667,6 @@ namespace OpenSim.Region.Framework.Scenes ScenePresence sp = GetScenePresence(remoteClient.AgentId); if (sp != null) { - uint regionX = RegionInfo.RegionLocX; - uint regionY = RegionInfo.RegionLocY; - - Utils.LongToUInts(regionHandle, out regionX, out regionY); - - int shiftx = (int) regionX - (int) RegionInfo.RegionLocX * (int)Constants.RegionSize; - int shifty = (int) regionY - (int) RegionInfo.RegionLocY * (int)Constants.RegionSize; - - position.X += shiftx; - position.Y += shifty; - - bool result = false; - - if (TestBorderCross(position,Cardinals.N)) - result = true; - - if (TestBorderCross(position, Cardinals.S)) - result = true; - - if (TestBorderCross(position, Cardinals.E)) - result = true; - - if (TestBorderCross(position, Cardinals.W)) - result = true; - - // bordercross if position is outside of region - - if (!result) - { - regionHandle = RegionInfo.RegionHandle; - } - else - { - // not in this region, undo the shift! - position.X -= shiftx; - position.Y -= shifty; - } - if (EntityTransferModule != null) { EntityTransferModule.Teleport(sp, regionHandle, position, lookAt, teleportFlags); diff --git a/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs b/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs index 52f46f2f4f..a2625c4a01 100644 --- a/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs +++ b/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs @@ -52,6 +52,7 @@ namespace OpenSim.Region.Framework.Scenes public class SceneCommunicationService //one instance per region { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static string LogHeader = "[SCENE COMMUNICATION SERVICE]"; protected RegionInfo m_regionInfo; protected Scene m_scene; @@ -100,7 +101,7 @@ namespace OpenSim.Region.Framework.Scenes { m_log.WarnFormat( "[SCENE COMMUNICATION SERVICE]: Region {0} failed to inform neighbour at {1}-{2} that it is up.", - m_scene.Name, x / Constants.RegionSize, y / Constants.RegionSize); + m_scene.Name, Util.WorldToRegionLoc(x), Util.WorldToRegionLoc(y)); } } @@ -166,7 +167,7 @@ namespace OpenSim.Region.Framework.Scenes // we only want to send one update to each simulator; the simulator will // hand it off to the regions where a child agent exists, this does assume // that the region position is cached or performance will degrade - Utils.LongToUInts(regionHandle, out x, out y); + Util.RegionHandleToWorldLoc(regionHandle, out x, out y); GridRegion dest = m_scene.GridService.GetRegionByPosition(UUID.Zero, (int)x, (int)y); if (dest == null) continue; @@ -206,7 +207,7 @@ namespace OpenSim.Region.Framework.Scenes //m_commsProvider.InterRegion.TellRegionToCloseChildConnection(regionHandle, agentID); uint x = 0, y = 0; - Utils.LongToUInts(regionHandle, out x, out y); + Util.RegionHandleToWorldLoc(regionHandle, out x, out y); GridRegion destination = m_scene.GridService.GetRegionByPosition(m_regionInfo.ScopeID, (int)x, (int)y); @@ -226,6 +227,8 @@ namespace OpenSim.Region.Framework.Scenes { foreach (ulong handle in regionslst) { + // We must take a copy here since handle acts like a reference when used in an iterator. + // This leads to race conditions if directly passed to SendCloseChildAgent with more than one neighbour region. ulong handleCopy = handle; Util.FireAndForget((o) => { SendCloseChildAgent(agentID, handleCopy, auth_code); }); } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 89c7a1a3e1..cb2f377e32 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -412,7 +412,7 @@ namespace OpenSim.Region.Framework.Scenes { get { - Vector3 minScale = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionSize); + Vector3 minScale = new Vector3(Constants.MaximumRegionSize, Constants.MaximumRegionSize, Constants.MaximumRegionSize); Vector3 maxScale = Vector3.Zero; Vector3 finalScale = new Vector3(0.5f, 0.5f, 0.5f); @@ -529,12 +529,10 @@ namespace OpenSim.Region.Framework.Scenes set { Vector3 val = value; - if (Scene != null && !IsAttachmentCheckFull() - && !Scene.LoadingPrims && - (Scene.TestBorderCross(val, Cardinals.E) || - Scene.TestBorderCross(val, Cardinals.W) || - Scene.TestBorderCross(val, Cardinals.N) || - Scene.TestBorderCross(val, Cardinals.S)) + if (Scene != null + && Scene.PositionIsInCurrentRegion(val) + && !IsAttachmentCheckFull() + && !Scene.LoadingPrims ) { if (!inTransit) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 91293c4ad8..897965913d 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2979,10 +2979,7 @@ namespace OpenSim.Region.Framework.Scenes { Vector3 newpos = new Vector3(pa.Position.GetBytes(), 0); - if (ParentGroup.Scene.TestBorderCross(newpos, Cardinals.N) - || ParentGroup.Scene.TestBorderCross(newpos, Cardinals.S) - || ParentGroup.Scene.TestBorderCross(newpos, Cardinals.E) - || ParentGroup.Scene.TestBorderCross(newpos, Cardinals.W)) + if (!ParentGroup.Scene.PositionIsInCurrentRegion(newpos)) { ParentGroup.AbsolutePosition = newpos; return; diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index dff582b8c3..3e278a917b 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -842,9 +842,8 @@ namespace OpenSim.Region.Framework.Scenes foreach (ulong handle in seeds.Keys) { uint x, y; - Utils.LongToUInts(handle, out x, out y); - x = x / Constants.RegionSize; - y = y / Constants.RegionSize; + Util.RegionHandleToRegionLoc(handle, out x, out y); + if (Util.IsOutsideView(DrawDistance, x, Scene.RegionInfo.RegionLocX, y, Scene.RegionInfo.RegionLocY)) { old.Add(handle); @@ -866,9 +865,7 @@ namespace OpenSim.Region.Framework.Scenes foreach (KeyValuePair kvp in KnownRegions) { uint x, y; - Utils.LongToUInts(kvp.Key, out x, out y); - x = x / Constants.RegionSize; - y = y / Constants.RegionSize; + Util.RegionHandleToRegionLoc(kvp.Key, out x, out y); m_log.Info(" >> "+x+", "+y+": "+kvp.Value); } } @@ -1170,18 +1167,6 @@ namespace OpenSim.Region.Framework.Scenes if (ParentID == 0) { - if (m_scene.TestBorderCross(pos, Cardinals.E)) - { - Border crossedBorder = m_scene.GetCrossedBorder(pos, Cardinals.E); - pos.X = crossedBorder.BorderLine.Z - 1; - } - - if (m_scene.TestBorderCross(pos, Cardinals.N)) - { - Border crossedBorder = m_scene.GetCrossedBorder(pos, Cardinals.N); - pos.Y = crossedBorder.BorderLine.Z - 1; - } - CheckAndAdjustLandingPoint(ref pos); if (pos.X < 0f || pos.Y < 0f || pos.Z < 0f) @@ -1201,7 +1186,7 @@ namespace OpenSim.Region.Framework.Scenes float posZLimit = 0; - if (pos.X < Constants.RegionSize && pos.Y < Constants.RegionSize) + if (pos.X < m_scene.RegionInfo.RegionSizeX && pos.Y < m_scene.RegionInfo.RegionSizeY) posZLimit = (float)m_scene.Heightmap[(int)pos.X, (int)pos.Y]; float newPosZ = posZLimit + localAVHeight / 2; @@ -2612,7 +2597,7 @@ namespace OpenSim.Region.Framework.Scenes if (regionCombinerModule != null) regionSize = regionCombinerModule.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID); else - regionSize = new Vector2(Constants.RegionSize); + regionSize = new Vector2(m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY); if (pos.X < 0 || pos.X >= regionSize.X || pos.Y < 0 || pos.Y >= regionSize.Y @@ -2630,8 +2615,8 @@ namespace OpenSim.Region.Framework.Scenes // } // Get terrain height for sub-region in a megaregion if necessary - int X = (int)((m_scene.RegionInfo.RegionLocX * Constants.RegionSize) + pos.X); - int Y = (int)((m_scene.RegionInfo.RegionLocY * Constants.RegionSize) + pos.Y); + int X = (int)((m_scene.RegionInfo.WorldLocX) + pos.X); + int Y = (int)((m_scene.RegionInfo.WorldLocY) + pos.Y); GridRegion target_region = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, X, Y); // If X and Y is NaN, target_region will be null if (target_region == null) @@ -2642,7 +2627,7 @@ namespace OpenSim.Region.Framework.Scenes if (!SceneManager.Instance.TryGetScene(target_regionID, out targetScene)) targetScene = m_scene; - float terrainHeight = (float)targetScene.Heightmap[(int)(pos.X % Constants.RegionSize), (int)(pos.Y % Constants.RegionSize)]; + float terrainHeight = (float)targetScene.Heightmap[(int)(pos.X % regionSize.X), (int)(pos.Y % regionSize.Y)]; // dont try to land underground terrainHeight += Appearance.AvatarHeight / 2; pos.Z = Math.Max(terrainHeight, pos.Z); @@ -3872,32 +3857,28 @@ namespace OpenSim.Region.Framework.Scenes // m_log.DebugFormat( // "[SCENE PRESENCE]: Testing border check for projected position {0} of {1} in {2}", // pos2, Name, Scene.Name); - - if( Scene.TestBorderCross(pos2, Cardinals.E) || - Scene.TestBorderCross(pos2, Cardinals.W) || - Scene.TestBorderCross(pos2, Cardinals.N) || - Scene.TestBorderCross(pos2, Cardinals.S) - ) + + if (Scene.PositionIsInCurrentRegion(pos2)) + return; + + if (!CrossToNewRegion() && m_requestedSitTargetUUID == UUID.Zero) { - if (!CrossToNewRegion() && m_requestedSitTargetUUID == UUID.Zero) - { - // we don't have entity transfer module - Vector3 pos = AbsolutePosition; - float px = pos.X; - if (px < 0) - pos.X += Velocity.X * 2; - else if (px > m_scene.RegionInfo.RegionSizeX) - pos.X -= Velocity.X * 2; + // we don't have entity transfer module + Vector3 pos = AbsolutePosition; + float px = pos.X; + if (px < 0) + pos.X += Velocity.X * 2; + else if (px > m_scene.RegionInfo.RegionSizeX) + pos.X -= Velocity.X * 2; - float py = pos.Y; - if (py < 0) - pos.Y += Velocity.Y * 2; - else if (py > m_scene.RegionInfo.RegionSizeY) - pos.Y -= Velocity.Y * 2; + float py = pos.Y; + if (py < 0) + pos.Y += Velocity.Y * 2; + else if (py > m_scene.RegionInfo.RegionSizeY) + pos.Y -= Velocity.Y * 2; - Velocity = Vector3.Zero; - AbsolutePosition = pos; - } + Velocity = Vector3.Zero; + AbsolutePosition = pos; } } @@ -3962,7 +3943,7 @@ namespace OpenSim.Region.Framework.Scenes // Put the child agent back at the center AbsolutePosition - = new Vector3(((float)Constants.RegionSize * 0.5f), ((float)Constants.RegionSize * 0.5f), 70); + = new Vector3(((float)m_scene.RegionInfo.RegionSizeX * 0.5f), ((float)m_scene.RegionInfo.RegionSizeY * 0.5f), 70); Animator.ResetAnimations(); } @@ -3989,9 +3970,7 @@ namespace OpenSim.Region.Framework.Scenes if (handle != Scene.RegionInfo.RegionHandle) { uint x, y; - Utils.LongToUInts(handle, out x, out y); - x = x / Constants.RegionSize; - y = y / Constants.RegionSize; + Util.RegionHandleToRegionLoc(handle, out x, out y); // m_log.Debug("---> x: " + x + "; newx:" + newRegionX + "; Abs:" + (int)Math.Abs((int)(x - newRegionX))); // m_log.Debug("---> y: " + y + "; newy:" + newRegionY + "; Abs:" + (int)Math.Abs((int)(y - newRegionY))); diff --git a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs index b6e0a97fce..3d563a6523 100644 --- a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs +++ b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs @@ -25,13 +25,20 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using System; +using System.IO; +using System.Text; +using System.Reflection; +using System.Xml; +using System.Xml.Serialization; + +using OpenSim.Data; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; -using System; -using System.Text; -using System.Xml; -using System.IO; -using System.Xml.Serialization; + +using OpenMetaverse; + +using log4net; namespace OpenSim.Region.Framework.Scenes { @@ -40,140 +47,136 @@ namespace OpenSim.Region.Framework.Scenes /// public class TerrainChannel : ITerrainChannel { - private readonly bool[,] taint; - private double[,] map; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static string LogHeader = "[TERRAIN CHANNEL]"; + protected TerrainData m_terrainData; + + public int Width { get { return m_terrainData.SizeX; } } // X dimension + // Unfortunately, for historical reasons, in this module 'Width' is X and 'Height' is Y + public int Height { get { return m_terrainData.SizeY; } } // Y dimension + public int Altitude { get { return m_terrainData.SizeZ; } } // Y dimension + + // Default, not-often-used builder public TerrainChannel() { - map = new double[Constants.RegionSize, Constants.RegionSize]; - taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16]; - - PinHeadIsland(); + m_terrainData = new HeightmapTerrainData((int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); + FlatLand(); + // PinHeadIsland(); } - public TerrainChannel(String type) + // Create terrain of given size + public TerrainChannel(int pX, int pY) { - map = new double[Constants.RegionSize, Constants.RegionSize]; - taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16]; + m_terrainData = new HeightmapTerrainData(pX, pY, (int)Constants.RegionHeight); + } + // Create terrain of specified size and initialize with specified terrain. + // TODO: join this with the terrain initializers. + public TerrainChannel(String type, int pX, int pY, int pZ) + { + m_terrainData = new HeightmapTerrainData(pX, pY, pZ); if (type.Equals("flat")) FlatLand(); else PinHeadIsland(); } - public TerrainChannel(double[,] import) + // Create channel passed a heightmap and expected dimensions of the region. + // The heightmap might not fit the passed size so accomodations must be made. + public TerrainChannel(double[,] pM, int pSizeX, int pSizeY, int pAltitude) { - map = import; - taint = new bool[import.GetLength(0),import.GetLength(1)]; + int hmSizeX = pM.GetLength(0); + int hmSizeY = pM.GetLength(1); + + m_terrainData = new HeightmapTerrainData(pSizeX, pSizeY, pAltitude); + + for (int xx = 0; xx < pSizeX; xx++) + for (int yy = 0; yy < pSizeY; yy++) + if (xx > hmSizeX || yy > hmSizeY) + m_terrainData[xx, yy] = TerrainData.DefaultTerrainHeight; + else + m_terrainData[xx, yy] = (float)pM[xx, yy]; } - public TerrainChannel(bool createMap) + public TerrainChannel(TerrainData pTerrData) { - if (createMap) - { - map = new double[Constants.RegionSize,Constants.RegionSize]; - taint = new bool[Constants.RegionSize / 16,Constants.RegionSize / 16]; - } - } - - public TerrainChannel(int w, int h) - { - map = new double[w,h]; - taint = new bool[w / 16,h / 16]; + m_terrainData = pTerrData; } #region ITerrainChannel Members - public int Width - { - get { return map.GetLength(0); } - } - - public int Height - { - get { return map.GetLength(1); } - } - + // ITerrainChannel.MakeCopy() public ITerrainChannel MakeCopy() { - TerrainChannel copy = new TerrainChannel(false); - copy.map = (double[,]) map.Clone(); - - return copy; + return this.Copy(); } + // ITerrainChannel.GetTerrainData() + public TerrainData GetTerrainData() + { + return m_terrainData; + } + + // ITerrainChannel.GetFloatsSerialized() + // This one dimensional version is ordered so height = map[y*sizeX+x]; + // DEPRECATED: don't use this function as it does not retain the dimensions of the terrain + // and the caller will probably do the wrong thing if the terrain is not the legacy 256x256. public float[] GetFloatsSerialised() { - // Move the member variables into local variables, calling - // member variables 256*256 times gets expensive - int w = Width; - int h = Height; - float[] heights = new float[w * h]; + return m_terrainData.GetFloatsSerialized(); + } + + // ITerrainChannel.GetDoubles() + public double[,] GetDoubles() + { + double[,] heights = new double[Width, Height]; - int i, j; // map coordinates int idx = 0; // index into serialized array - for (i = 0; i < h; i++) + for (int ii = 0; ii < Width; ii++) { - for (j = 0; j < w; j++) + for (int jj = 0; jj < Height; jj++) { - heights[idx++] = (float)map[j, i]; + heights[ii, jj] = (double)m_terrainData[ii, jj]; + idx++; } } return heights; } - public double[,] GetDoubles() - { - return map; - } - + // ITerrainChannel.this[x,y] public double this[int x, int y] { - get - { - if (x < 0) x = 0; - if (y < 0) y = 0; - if (x >= (int)Constants.RegionSize) x = (int)Constants.RegionSize - 1; - if (y >= (int)Constants.RegionSize) y = (int)Constants.RegionSize - 1; - - return map[x, y]; + get { + if (x < 0 || x >= Width || y < 0 || y >= Height) + return 0; + return (double)m_terrainData[x, y]; } set { - // Will "fix" terrain hole problems. Although not fantastically. if (Double.IsNaN(value) || Double.IsInfinity(value)) return; - if (map[x, y] != value) - { - taint[x / 16, y / 16] = true; - map[x, y] = value; - } + m_terrainData[x, y] = (float)value; } } + // ITerrainChannel.GetHieghtAtXYZ(x, y, z) + public float GetHeightAtXYZ(float x, float y, float z) + { + if (x < 0 || x >= Width || y < 0 || y >= Height) + return 0; + return m_terrainData[(int)x, (int)y]; + } + + // ITerrainChannel.Tainted() public bool Tainted(int x, int y) { - if (taint[x / 16, y / 16]) - { - taint[x / 16, y / 16] = false; - return true; - } - return false; - } - - #endregion - - public TerrainChannel Copy() - { - TerrainChannel copy = new TerrainChannel(false); - copy.map = (double[,]) map.Clone(); - - return copy; + return m_terrainData.IsTaintedAt(x, y); } + // ITerrainChannel.SaveToXmlString() public string SaveToXmlString() { XmlWriterSettings settings = new XmlWriterSettings(); @@ -189,13 +192,7 @@ namespace OpenSim.Region.Framework.Scenes } } - private void WriteXml(XmlWriter writer) - { - writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty); - ToXml(writer); - writer.WriteEndElement(); - } - + // ITerrainChannel.LoadFromXmlString() public void LoadFromXmlString(string data) { StringReader sr = new StringReader(data); @@ -207,12 +204,124 @@ namespace OpenSim.Region.Framework.Scenes sr.Close(); } - private void ReadXml(XmlReader reader) + // ITerrainChannel.Merge + public void Merge(ITerrainChannel newTerrain, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement) { - reader.ReadStartElement("TerrainMap"); - FromXml(reader); + m_log.DebugFormat("{0} Merge. inSize=<{1},{2}>, disp={3}, rot={4}, rotDisp={5}, outSize=<{6},{7}>", LogHeader, + newTerrain.Width, newTerrain.Height, + displacement, radianRotation, rotationDisplacement, + m_terrainData.SizeX, m_terrainData.SizeY); + for (int xx = 0; xx < newTerrain.Width; xx++) + { + for (int yy = 0; yy < newTerrain.Height; yy++) + { + int dispX = (int)displacement.X; + int dispY = (int)displacement.Y; + float newHeight = (float)newTerrain[xx, yy] + displacement.Z; + if (radianRotation == 0) + { + // If no rotation, place the new height in the specified location + dispX += xx; + dispY += yy; + if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY) + { + m_terrainData[dispX, dispY] = newHeight; + } + } + else + { + // If rotating, we have to smooth the result because the conversion + // to ints will mean heightmap entries will not get changed + // First compute the rotation location for the new height. + dispX += (int)(rotationDisplacement.X + + ((float)xx - rotationDisplacement.X) * Math.Cos(radianRotation) + - ((float)yy - rotationDisplacement.Y) * Math.Sin(radianRotation) ); + + dispY += (int)(rotationDisplacement.Y + + ((float)xx - rotationDisplacement.X) * Math.Sin(radianRotation) + + ((float)yy - rotationDisplacement.Y) * Math.Cos(radianRotation) ); + + if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY) + { + float oldHeight = m_terrainData[dispX, dispY]; + // Smooth the heights around this location if the old height is far from this one + for (int sxx = dispX - 2; sxx < dispX + 2; sxx++) + { + for (int syy = dispY - 2; syy < dispY + 2; syy++) + { + if (sxx >= 0 && sxx < m_terrainData.SizeX && syy >= 0 && syy < m_terrainData.SizeY) + { + if (sxx == dispX && syy == dispY) + { + // Set height for the exact rotated point + m_terrainData[dispX, dispY] = newHeight; + } + else + { + if (Math.Abs(m_terrainData[sxx, syy] - newHeight) > 1f) + { + // If the adjacent height is far off, force it to this height + m_terrainData[sxx, syy] = newHeight; + } + } + } + } + } + } + + if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY) + { + m_terrainData[dispX, dispY] = (float)newTerrain[xx, yy]; + } + } + } + } } + #endregion + + public TerrainChannel Copy() + { + TerrainChannel copy = new TerrainChannel(); + copy.m_terrainData = m_terrainData.Clone(); + return copy; + } + + private void WriteXml(XmlWriter writer) + { + if (Width == Constants.RegionSize && Height == Constants.RegionSize) + { + // Downward compatibility for legacy region terrain maps. + // If region is exactly legacy size, return the old format XML. + writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty); + ToXml(writer); + writer.WriteEndElement(); + } + else + { + // New format XML that includes width and length. + writer.WriteStartElement(String.Empty, "TerrainMap2", String.Empty); + ToXml2(writer); + writer.WriteEndElement(); + } + } + + private void ReadXml(XmlReader reader) + { + // Check the first element. If legacy element, use the legacy reader. + if (reader.IsStartElement("TerrainMap")) + { + reader.ReadStartElement("TerrainMap"); + FromXml(reader); + } + else + { + reader.ReadStartElement("TerrainMap2"); + FromXml2(reader); + } + } + + // Write legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array. private void ToXml(XmlWriter xmlWriter) { float[] mapData = GetFloatsSerialised(); @@ -226,12 +335,15 @@ namespace OpenSim.Region.Framework.Scenes serializer.Serialize(xmlWriter, buffer); } + // Read legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array. private void FromXml(XmlReader xmlReader) { XmlSerializer serializer = new XmlSerializer(typeof(byte[])); byte[] dataArray = (byte[])serializer.Deserialize(xmlReader); int index = 0; + m_terrainData = new HeightmapTerrainData(Height, Width, (int)Constants.RegionHeight); + for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) @@ -244,35 +356,63 @@ namespace OpenSim.Region.Framework.Scenes } } + private class TerrainChannelXMLPackage + { + public int Version; + public int SizeX; + public int SizeY; + public int SizeZ; + public float CompressionFactor; + public int[] Map; + public TerrainChannelXMLPackage(int pX, int pY, int pZ, float pCompressionFactor, int[] pMap) + { + Version = 1; + SizeX = pX; + SizeY = pY; + SizeZ = pZ; + CompressionFactor = pCompressionFactor; + Map = pMap; + } + } + + // New terrain serialization format that includes the width and length. + private void ToXml2(XmlWriter xmlWriter) + { + TerrainChannelXMLPackage package = new TerrainChannelXMLPackage(Width, Height, Altitude, m_terrainData.CompressionFactor, + m_terrainData.GetCompressedMap()); + XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage)); + serializer.Serialize(xmlWriter, package); + } + + // New terrain serialization format that includes the width and length. + private void FromXml2(XmlReader xmlReader) + { + XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage)); + TerrainChannelXMLPackage package = (TerrainChannelXMLPackage)serializer.Deserialize(xmlReader); + m_terrainData = new HeightmapTerrainData(package.Map, package.CompressionFactor, package.SizeX, package.SizeY, package.SizeZ); + } + + // Fill the heightmap with the center bump terrain private void PinHeadIsland() { - int x; - for (x = 0; x < Constants.RegionSize; x++) + for (int x = 0; x < Width; x++) { - int y; - for (y = 0; y < Constants.RegionSize; y++) + for (int y = 0; y < Height; y++) { - map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10; - double spherFacA = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 50) * 0.01; - double spherFacB = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 100) * 0.001; - if (map[x, y] < spherFacA) - map[x, y] = spherFacA; - if (map[x, y] < spherFacB) - map[x, y] = spherFacB; + m_terrainData[x, y] = (float)TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10; + float spherFacA = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 50) * 0.01d); + float spherFacB = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 100) * 0.001d); + if (m_terrainData[x, y]< spherFacA) + m_terrainData[x, y]= spherFacA; + if (m_terrainData[x, y]< spherFacB) + m_terrainData[x, y] = spherFacB; } } } private void FlatLand() { - int x; - for (x = 0; x < Constants.RegionSize; x++) - { - int y; - for (y = 0; y < Constants.RegionSize; y++) - map[x, y] = 21; - } + m_terrainData.ClearLand(); } - } } diff --git a/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs b/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs new file mode 100644 index 0000000000..fc8f8cd2c5 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs @@ -0,0 +1,948 @@ +/* + * 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. + */ + +/* Freely adapted from the Aurora version of the terrain compressor. + * Copyright (c) Contributors, http://aurora-sim.org/, http://opensimulator.org/ + */ + +using System; +using System.Reflection; + +using log4net; + +using OpenSim.Framework; +using OpenSim.Region.Framework; +using OpenSim.Region.Framework.Scenes; + +using OpenMetaverse; +using OpenMetaverse.Packets; + +namespace OpenSim.Region.ClientStack.LindenUDP +{ + public static class OpenSimTerrainCompressor + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + +#pragma warning disable 414 + private static string LogHeader = "[TERRAIN COMPRESSOR]"; +#pragma warning restore 414 + + public const int END_OF_PATCHES = 97; + + private const float OO_SQRT2 = 0.7071067811865475244008443621049f; + private const int STRIDE = 264; + + private const int ZERO_CODE = 0x0; + private const int ZERO_EOB = 0x2; + private const int POSITIVE_VALUE = 0x6; + private const int NEGATIVE_VALUE = 0x7; + + private static readonly float[] DequantizeTable16 = + new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + + private static readonly float[] DequantizeTable32 = + new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + + private static readonly float[] CosineTable16 = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + //private static readonly float[] CosineTable32 = new float[Constants.TerrainPatchSize * Constants.TerrainPatchSize]; + private static readonly int[] CopyMatrix16 = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + private static readonly int[] CopyMatrix32 = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + + private static readonly float[] QuantizeTable16 = + new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + + static OpenSimTerrainCompressor() + { + // Initialize the decompression tables + BuildDequantizeTable16(); + SetupCosines16(); + BuildCopyMatrix16(); + BuildQuantizeTable16(); + } + + // Used to send cloud and wind patches + public static LayerDataPacket CreateLayerDataPacket(TerrainPatch[] patches, byte type, int pRegionSizeX, + int pRegionSizeY) + { + LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}}; + + TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader + {Stride = STRIDE, PatchSize = Constants.TerrainPatchSize}; + + // Should be enough to fit even the most poorly packed data + byte[] data = new byte[patches.Length*Constants.TerrainPatchSize*Constants.TerrainPatchSize*2]; + BitPack bitpack = new BitPack(data, 0); + bitpack.PackBits(header.Stride, 16); + bitpack.PackBits(header.PatchSize, 8); + bitpack.PackBits(type, 8); + + foreach (TerrainPatch t in patches) + CreatePatch(bitpack, t.Data, t.X, t.Y, pRegionSizeX, pRegionSizeY); + + bitpack.PackBits(END_OF_PATCHES, 8); + + layer.LayerData.Data = new byte[bitpack.BytePos + 1]; + Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1); + + return layer; + } + + // Create a land packet for a single patch. + public static LayerDataPacket CreateLandPacket(TerrainData terrData, int patchX, int patchY) + { + int[] xPieces = new int[1]; + int[] yPieces = new int[1]; + xPieces[0] = patchX; // patch X dimension + yPieces[0] = patchY; + + return CreateLandPacket(terrData, xPieces, yPieces); + } + + public static LayerDataPacket CreateLandPacket(TerrainData terrData, int[] xPieces, int[] yPieces) + { + byte landPacketType = (byte)TerrainPatch.LayerType.Land; + if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize) + { + landPacketType = (byte)TerrainPatch.LayerType.LandExtended; + } + + return CreateLandPacket(terrData, xPieces, yPieces, landPacketType); + } + + /// + /// Creates a LayerData packet for compressed land data given a full + /// simulator heightmap and an array of indices of patches to compress + /// + /// + /// Terrain data that can result in a meter square heightmap. + /// + /// + /// Array of indexes in the grid of patches + /// for this simulator. + /// If creating a packet for multiple patches, there will be entries in + /// both the X and Y arrays for each of the patches. + /// For example if patches 1 and 17 are to be sent, + /// x[] = {1,1} and y[] = {0,1} which specifies the patches at + /// indexes <1,0> and <1,1> (presuming the terrain size is 16x16 patches). + /// + /// + /// Array of indexes in the grid of patches. + /// + /// + /// + public static LayerDataPacket CreateLandPacket(TerrainData terrData, int[] x, int[] y, byte type) + { + LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}}; + + TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader + {Stride = STRIDE, PatchSize = Constants.TerrainPatchSize}; + + byte[] data = new byte[x.Length * Constants.TerrainPatchSize * Constants.TerrainPatchSize * 2]; + BitPack bitpack = new BitPack(data, 0); + bitpack.PackBits(header.Stride, 16); + bitpack.PackBits(header.PatchSize, 8); + bitpack.PackBits(type, 8); + + for (int i = 0; i < x.Length; i++) + CreatePatchFromHeightmap(bitpack, terrData, x[i], y[i]); + + bitpack.PackBits(END_OF_PATCHES, 8); + + layer.LayerData.Data = new byte[bitpack.BytePos + 1]; + Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1); + + return layer; + } + + // Unused: left for historical reference. + public static void CreatePatch(BitPack output, float[] patchData, int x, int y, int pRegionSizeX, int pRegionSizeY) + { + TerrainPatch.Header header = PrescanPatch(patchData); + header.QuantWBits = 136; + if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize) + { + header.PatchIDs = (y & 0xFFFF); + header.PatchIDs += (x << 16); + } + else + { + header.PatchIDs = (y & 0x1F); + header.PatchIDs += (x << 5); + } + + // NOTE: No idea what prequant and postquant should be or what they do + + int wbits; + int[] patch = CompressPatch(patchData, header, 10, out wbits); + wbits = EncodePatchHeader(output, header, patch, Constants.RegionSize, Constants.RegionSize, wbits); + EncodePatch(output, patch, 0, wbits); + } + + /// + /// Add a patch of terrain to a BitPacker + /// + /// BitPacker to write the patch to + /// + /// Heightmap of the simulator. Presumed to be an sizeX*sizeY array. + /// + /// + /// X offset of the patch to create. + /// + /// + /// Y offset of the patch to create. + /// + /// + /// + public static void CreatePatchFromHeightmap(BitPack output, TerrainData terrData, int patchX, int patchY) + { + TerrainPatch.Header header = PrescanPatch(terrData, patchX, patchY); + header.QuantWBits = 136; + + // If larger than legacy region size, pack patch X and Y info differently. + if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize) + { + header.PatchIDs = (patchY & 0xFFFF); + header.PatchIDs += (patchX << 16); + } + else + { + header.PatchIDs = (patchY & 0x1F); + header.PatchIDs += (patchX << 5); + } + + // m_log.DebugFormat("{0} CreatePatchFromHeightmap. patchX={1}, patchY={2}, DCOffset={3}, range={4}", + // LogHeader, patchX, patchY, header.DCOffset, header.Range); + + // NOTE: No idea what prequant and postquant should be or what they do + int wbits; + int[] patch = CompressPatch(terrData, patchX, patchY, header, 10, out wbits); + wbits = EncodePatchHeader(output, header, patch, (uint)terrData.SizeX, (uint)terrData.SizeY, wbits); + EncodePatch(output, patch, 0, wbits); + } + + private static TerrainPatch.Header PrescanPatch(float[] patch) + { + TerrainPatch.Header header = new TerrainPatch.Header(); + float zmax = -99999999.0f; + float zmin = 99999999.0f; + + for (int i = 0; i < Constants.TerrainPatchSize*Constants.TerrainPatchSize; i++) + { + float val = patch[i]; + if (val > zmax) zmax = val; + if (val < zmin) zmin = val; + } + + header.DCOffset = zmin; + header.Range = (int) ((zmax - zmin) + 1.0f); + + return header; + } + + // Scan the height info we're returning and return a patch packet header for this patch. + private static TerrainPatch.Header PrescanPatch(TerrainData terrData, int patchX, int patchY) + { + TerrainPatch.Header header = new TerrainPatch.Header(); + float zmax = -99999999.0f; + float zmin = 99999999.0f; + + for (int j = patchY*Constants.TerrainPatchSize; j < (patchY + 1)*Constants.TerrainPatchSize; j++) + { + for (int i = patchX*Constants.TerrainPatchSize; i < (patchX + 1)*Constants.TerrainPatchSize; i++) + { + float val = terrData[i, j]; + if (val > zmax) zmax = val; + if (val < zmin) zmin = val; + } + } + + header.DCOffset = zmin; + header.Range = (int)((zmax - zmin) + 1.0f); + + return header; + } + + public static TerrainPatch.Header DecodePatchHeader(BitPack bitpack) + { + TerrainPatch.Header header = new TerrainPatch.Header {QuantWBits = bitpack.UnpackBits(8)}; + + // Quantized word bits + if (header.QuantWBits == END_OF_PATCHES) + return header; + + // DC offset + header.DCOffset = bitpack.UnpackFloat(); + + // Range + header.Range = bitpack.UnpackBits(16); + + // Patch IDs (10 bits) + header.PatchIDs = bitpack.UnpackBits(10); + + // Word bits + header.WordBits = (uint) ((header.QuantWBits & 0x0f) + 2); + + return header; + } + + private static int EncodePatchHeader(BitPack output, TerrainPatch.Header header, int[] patch, uint pRegionSizeX, + uint pRegionSizeY, int wbits) + { + /* + int temp; + int wbits = (header.QuantWBits & 0x0f) + 2; + uint maxWbits = (uint)wbits + 5; + uint minWbits = ((uint)wbits >> 1); + int wbitsMaxValue; + */ + // goal is to determ minimum number of bits to use so all data fits + /* + wbits = (int)minWbits; + wbitsMaxValue = (1 << wbits); + + for (int i = 0; i < patch.Length; i++) + { + temp = patch[i]; + if (temp != 0) + { + // Get the absolute value + if (temp < 0) temp *= -1; + + no coments.. + + for (int j = (int)maxWbits; j > (int)minWbits; j--) + { + if ((temp & (1 << j)) != 0) + { + if (j > wbits) wbits = j; + break; + } + } + + while (temp > wbitsMaxValue) + { + wbits++; + if (wbits == maxWbits) + goto Done; + wbitsMaxValue = 1 << wbits; + } + } + } + + Done: + + // wbits += 1; + */ + // better check + if (wbits > 17) + wbits = 16; + else if (wbits < 3) + wbits = 3; + + header.QuantWBits &= 0xf0; + + header.QuantWBits |= (wbits - 2); + + output.PackBits(header.QuantWBits, 8); + output.PackFloat(header.DCOffset); + output.PackBits(header.Range, 16); + if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize) + output.PackBits(header.PatchIDs, 32); + else + output.PackBits(header.PatchIDs, 10); + + return wbits; + } + + private static void IDCTColumn16(float[] linein, float[] lineout, int column) + { + for (int n = 0; n < Constants.TerrainPatchSize; n++) + { + float total = OO_SQRT2*linein[column]; + + for (int u = 1; u < Constants.TerrainPatchSize; u++) + { + int usize = u*Constants.TerrainPatchSize; + total += linein[usize + column]*CosineTable16[usize + n]; + } + + lineout[Constants.TerrainPatchSize*n + column] = total; + } + } + + private static void IDCTLine16(float[] linein, float[] lineout, int line) + { + const float oosob = 2.0f/Constants.TerrainPatchSize; + int lineSize = line*Constants.TerrainPatchSize; + + for (int n = 0; n < Constants.TerrainPatchSize; n++) + { + float total = OO_SQRT2*linein[lineSize]; + + for (int u = 1; u < Constants.TerrainPatchSize; u++) + { + total += linein[lineSize + u]*CosineTable16[u*Constants.TerrainPatchSize + n]; + } + + lineout[lineSize + n] = total*oosob; + } + } + +/* + private static void DCTLine16(float[] linein, float[] lineout, int line) + { + float total = 0.0f; + int lineSize = line * Constants.TerrainPatchSize; + + for (int n = 0; n < Constants.TerrainPatchSize; n++) + { + total += linein[lineSize + n]; + } + + lineout[lineSize] = OO_SQRT2 * total; + + int uptr = 0; + for (int u = 1; u < Constants.TerrainPatchSize; u++) + { + total = 0.0f; + uptr += Constants.TerrainPatchSize; + + for (int n = 0; n < Constants.TerrainPatchSize; n++) + { + total += linein[lineSize + n] * CosineTable16[uptr + n]; + } + + lineout[lineSize + u] = total; + } + } +*/ + + private static void DCTLine16(float[] linein, float[] lineout, int line) + { + // outputs transpose data (lines exchanged with coluns ) + // so to save a bit of cpu when doing coluns + float total = 0.0f; + int lineSize = line*Constants.TerrainPatchSize; + + for (int n = 0; n < Constants.TerrainPatchSize; n++) + { + total += linein[lineSize + n]; + } + + lineout[line] = OO_SQRT2*total; + + for (int u = Constants.TerrainPatchSize; + u < Constants.TerrainPatchSize*Constants.TerrainPatchSize; + u += Constants.TerrainPatchSize) + { + total = 0.0f; + for (int ptrn = lineSize, ptru = u; ptrn < lineSize + Constants.TerrainPatchSize; ptrn++,ptru++) + { + total += linein[ptrn]*CosineTable16[ptru]; + } + + lineout[line + u] = total; + } + } + + + /* + private static void DCTColumn16(float[] linein, int[] lineout, int column) + { + float total = 0.0f; + // const float oosob = 2.0f / Constants.TerrainPatchSize; + + for (int n = 0; n < Constants.TerrainPatchSize; n++) + { + total += linein[Constants.TerrainPatchSize * n + column]; + } + + // lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]); + lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * QuantizeTable16[column]); + + for (int uptr = Constants.TerrainPatchSize; uptr < Constants.TerrainPatchSize * Constants.TerrainPatchSize; uptr += Constants.TerrainPatchSize) + { + total = 0.0f; + + for (int n = 0; n < Constants.TerrainPatchSize; n++) + { + total += linein[Constants.TerrainPatchSize * n + column] * CosineTable16[uptr + n]; + } + + // lineout[CopyMatrix16[Constants.TerrainPatchSize * u + column]] = (int)(total * oosob * QuantizeTable16[Constants.TerrainPatchSize * u + column]); + lineout[CopyMatrix16[uptr + column]] = (int)(total * QuantizeTable16[uptr + column]); + } + } + + private static void DCTColumn16(float[] linein, int[] lineout, int column) + { + // input columns are in fact stored in lines now + + float total = 0.0f; +// const float oosob = 2.0f / Constants.TerrainPatchSize; + int inlinesptr = Constants.TerrainPatchSize*column; + + for (int n = 0; n < Constants.TerrainPatchSize; n++) + { + total += linein[inlinesptr + n]; + } + + // lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]); + lineout[CopyMatrix16[column]] = (int) (OO_SQRT2*total*QuantizeTable16[column]); + + for (int uptr = Constants.TerrainPatchSize; + uptr < Constants.TerrainPatchSize*Constants.TerrainPatchSize; + uptr += Constants.TerrainPatchSize) + { + total = 0.0f; + + for (int n = inlinesptr, ptru = uptr; n < inlinesptr + Constants.TerrainPatchSize; n++, ptru++) + { + total += linein[n]*CosineTable16[ptru]; + } + +// lineout[CopyMatrix16[Constants.TerrainPatchSize * u + column]] = (int)(total * oosob * QuantizeTable16[Constants.TerrainPatchSize * u + column]); + lineout[CopyMatrix16[uptr + column]] = (int) (total*QuantizeTable16[uptr + column]); + } + } + */ + + private static int DCTColumn16Wbits(float[] linein, int[] lineout, int column, int wbits, int maxwbits) + { + // input columns are in fact stored in lines now + + bool dowbits = wbits != maxwbits; + int wbitsMaxValue = 1 << wbits; + + float total = 0.0f; + // const float oosob = 2.0f / Constants.TerrainPatchSize; + int inlinesptr = Constants.TerrainPatchSize*column; + + for (int n = 0; n < Constants.TerrainPatchSize; n++) + { + total += linein[inlinesptr + n]; + } + + // lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]); + int tmp = (int) (OO_SQRT2*total*QuantizeTable16[column]); + lineout[CopyMatrix16[column]] = tmp; + + if (dowbits) + { + if (tmp < 0) tmp *= -1; + while (tmp > wbitsMaxValue) + { + wbits++; + wbitsMaxValue = 1 << wbits; + if (wbits == maxwbits) + { + dowbits = false; + break; + } + } + } + + for (int uptr = Constants.TerrainPatchSize; + uptr < Constants.TerrainPatchSize*Constants.TerrainPatchSize; + uptr += Constants.TerrainPatchSize) + { + total = 0.0f; + + for (int n = inlinesptr, ptru = uptr; n < inlinesptr + Constants.TerrainPatchSize; n++, ptru++) + { + total += linein[n]*CosineTable16[ptru]; + } + + tmp = (int) (total*QuantizeTable16[uptr + column]); + lineout[CopyMatrix16[uptr + column]] = tmp; + + if (dowbits) + { + if (tmp < 0) tmp *= -1; + while (tmp > wbitsMaxValue) + { + wbits++; + wbitsMaxValue = 1 << wbits; + if (wbits == maxwbits) + { + dowbits = false; + break; + } + } + } + } + return wbits; + } + + public static void DecodePatch(int[] patches, BitPack bitpack, TerrainPatch.Header header, int size) + { + for (int n = 0; n < size*size; n++) + { + // ? + int temp = bitpack.UnpackBits(1); + if (temp != 0) + { + // Value or EOB + temp = bitpack.UnpackBits(1); + if (temp != 0) + { + // Value + temp = bitpack.UnpackBits(1); + if (temp != 0) + { + // Negative + temp = bitpack.UnpackBits((int) header.WordBits); + patches[n] = temp*-1; + } + else + { + // Positive + temp = bitpack.UnpackBits((int) header.WordBits); + patches[n] = temp; + } + } + else + { + // Set the rest to zero + // TODO: This might not be necessary + for (int o = n; o < size*size; o++) + { + patches[o] = 0; + } + break; + } + } + else + { + patches[n] = 0; + } + } + } + + private static void EncodePatch(BitPack output, int[] patch, int postquant, int wbits) + { + int maxwbitssize = (1 << wbits) - 1; + + if (postquant > Constants.TerrainPatchSize*Constants.TerrainPatchSize || postquant < 0) + { + Logger.Log("Postquant is outside the range of allowed values in EncodePatch()", Helpers.LogLevel.Error); + return; + } + + if (postquant != 0) patch[Constants.TerrainPatchSize*Constants.TerrainPatchSize - postquant] = 0; + + for (int i = 0; i < Constants.TerrainPatchSize*Constants.TerrainPatchSize; i++) + { + int temp = patch[i]; + + if (temp == 0) + { + bool eob = true; + + for (int j = i; j < Constants.TerrainPatchSize*Constants.TerrainPatchSize - postquant; j++) + { + if (patch[j] != 0) + { + eob = false; + break; + } + } + + if (eob) + { + output.PackBits(ZERO_EOB, 2); + return; + } + output.PackBits(ZERO_CODE, 1); + } + else + { + if (temp < 0) + { + temp *= -1; + + if (temp > maxwbitssize) temp = maxwbitssize; + + output.PackBits(NEGATIVE_VALUE, 3); + output.PackBits(temp, wbits); + } + else + { + if (temp > maxwbitssize) temp = maxwbitssize; + + output.PackBits(POSITIVE_VALUE, 3); + output.PackBits(temp, wbits); + } + } + } + } + + public static float[] DecompressPatch(int[] patches, TerrainPatch.Header header, TerrainPatch.GroupHeader group) + { + float[] block = new float[group.PatchSize*group.PatchSize]; + float[] output = new float[group.PatchSize*group.PatchSize]; + int prequant = (header.QuantWBits >> 4) + 2; + int quantize = 1 << prequant; + float ooq = 1.0f/quantize; + float mult = ooq*header.Range; + float addval = mult*(1 << (prequant - 1)) + header.DCOffset; + + if (group.PatchSize == Constants.TerrainPatchSize) + { + for (int n = 0; n < Constants.TerrainPatchSize*Constants.TerrainPatchSize; n++) + { + block[n] = patches[CopyMatrix16[n]]*DequantizeTable16[n]; + } + + float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + + for (int o = 0; o < Constants.TerrainPatchSize; o++) + IDCTColumn16(block, ftemp, o); + for (int o = 0; o < Constants.TerrainPatchSize; o++) + IDCTLine16(ftemp, block, o); + } + else + { + for (int n = 0; n < Constants.TerrainPatchSize*2*Constants.TerrainPatchSize*2; n++) + { + block[n] = patches[CopyMatrix32[n]]*DequantizeTable32[n]; + } + + Logger.Log("Implement IDCTPatchLarge", Helpers.LogLevel.Error); + } + + for (int j = 0; j < block.Length; j++) + { + output[j] = block[j]*mult + addval; + } + + return output; + } + + private static int[] CompressPatch(float[] patchData, TerrainPatch.Header header, int prequant, out int wbits) + { + float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + int wordsize = (prequant - 2) & 0x0f; + float oozrange = 1.0f/header.Range; + float range = (1 << prequant); + float premult = oozrange*range; + float sub = (1 << (prequant - 1)) + header.DCOffset*premult; + + header.QuantWBits = wordsize; + header.QuantWBits |= wordsize << 4; + + int k = 0; + for (int j = 0; j < Constants.TerrainPatchSize; j++) + { + for (int i = 0; i < Constants.TerrainPatchSize; i++) + block[k++] = patchData[j*Constants.TerrainPatchSize + i]*premult - sub; + } + + float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + + + int maxWbits = prequant + 5; + wbits = (prequant >> 1); + + for (int o = 0; o < Constants.TerrainPatchSize; o++) + DCTLine16(block, ftemp, o); + for (int o = 0; o < Constants.TerrainPatchSize; o++) + wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits); + + return itemp; + } + + private static int[] CompressPatch(float[,] patchData, TerrainPatch.Header header, int prequant, out int wbits) + { + float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + float oozrange = 1.0f/header.Range; + float range = (1 << prequant); + float premult = oozrange*range; + float sub = (1 << (prequant - 1)) + header.DCOffset*premult; + int wordsize = (prequant - 2) & 0x0f; + + header.QuantWBits = wordsize; + header.QuantWBits |= wordsize << 4; + + int k = 0; + for (int j = 0; j < Constants.TerrainPatchSize; j++) + { + for (int i = 0; i < Constants.TerrainPatchSize; i++) + block[k++] = patchData[j, i]*premult - sub; + } + + float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + + int maxWbits = prequant + 5; + wbits = (prequant >> 1); + + for (int o = 0; o < Constants.TerrainPatchSize; o++) + DCTLine16(block, ftemp, o); + for (int o = 0; o < Constants.TerrainPatchSize; o++) + wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits); + + return itemp; + } + + private static int[] CompressPatch(TerrainData terrData, int patchX, int patchY, TerrainPatch.Header header, + int prequant, out int wbits) + { + float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + int wordsize = prequant; + float oozrange = 1.0f/header.Range; + float range = (1 << prequant); + float premult = oozrange*range; + float sub = (1 << (prequant - 1)) + header.DCOffset*premult; + + header.QuantWBits = wordsize - 2; + header.QuantWBits |= (prequant - 2) << 4; + + int k = 0; + + int yPatchLimit = patchY >= (terrData.SizeY / Constants.TerrainPatchSize) ? + (terrData.SizeY - Constants.TerrainPatchSize) / Constants.TerrainPatchSize : patchY; + yPatchLimit = (yPatchLimit + 1) * Constants.TerrainPatchSize; + + int xPatchLimit = patchX >= (terrData.SizeX / Constants.TerrainPatchSize) ? + (terrData.SizeX - Constants.TerrainPatchSize) / Constants.TerrainPatchSize : patchX; + xPatchLimit = (xPatchLimit + 1) * Constants.TerrainPatchSize; + + for (int yy = patchY * Constants.TerrainPatchSize; yy < yPatchLimit; yy++) + { + for (int xx = patchX * Constants.TerrainPatchSize; xx < xPatchLimit; xx++) + { + block[k++] = terrData[xx, yy] * premult - sub; + } + } + + float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; + + int maxWbits = prequant + 5; + wbits = (prequant >> 1); + + for (int o = 0; o < Constants.TerrainPatchSize; o++) + DCTLine16(block, ftemp, o); + for (int o = 0; o < Constants.TerrainPatchSize; o++) + wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits); + + return itemp; + } + + #region Initialization + + private static void BuildDequantizeTable16() + { + for (int j = 0; j < Constants.TerrainPatchSize; j++) + { + for (int i = 0; i < Constants.TerrainPatchSize; i++) + { + DequantizeTable16[j*Constants.TerrainPatchSize + i] = 1.0f + 2.0f*(i + j); + } + } + } + + private static void BuildQuantizeTable16() + { + const float oosob = 2.0f/Constants.TerrainPatchSize; + for (int j = 0; j < Constants.TerrainPatchSize; j++) + { + for (int i = 0; i < Constants.TerrainPatchSize; i++) + { +// QuantizeTable16[j * Constants.TerrainPatchSize + i] = 1.0f / (1.0f + 2.0f * ((float)i + (float)j)); + QuantizeTable16[j*Constants.TerrainPatchSize + i] = oosob/(1.0f + 2.0f*(i + (float) j)); + } + } + } + + private static void SetupCosines16() + { + const float hposz = (float) Math.PI*0.5f/Constants.TerrainPatchSize; + + for (int u = 0; u < Constants.TerrainPatchSize; u++) + { + for (int n = 0; n < Constants.TerrainPatchSize; n++) + { + CosineTable16[u*Constants.TerrainPatchSize + n] = (float) Math.Cos((2.0f*n + 1.0f)*u*hposz); + } + } + } + + private static void BuildCopyMatrix16() + { + bool diag = false; + bool right = true; + int i = 0; + int j = 0; + int count = 0; + + while (i < Constants.TerrainPatchSize && j < Constants.TerrainPatchSize) + { + CopyMatrix16[j*Constants.TerrainPatchSize + i] = count++; + + if (!diag) + { + if (right) + { + if (i < Constants.TerrainPatchSize - 1) i++; + else j++; + + right = false; + diag = true; + } + else + { + if (j < Constants.TerrainPatchSize - 1) j++; + else i++; + + right = true; + diag = true; + } + } + else + { + if (right) + { + i++; + j--; + if (i == Constants.TerrainPatchSize - 1 || j == 0) diag = false; + } + else + { + i--; + j++; + if (j == Constants.TerrainPatchSize - 1 || i == 0) diag = false; + } + } + } + } + + #endregion Initialization + } +} diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 8c0ad75f7e..51ecc8d8f3 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -520,7 +520,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server public Vector3 StartPos { - get { return new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 50); } + get { return new Vector3(m_scene.RegionInfo.RegionSizeX * 0.5f, m_scene.RegionInfo.RegionSizeY * 0.5f, 50f); } set { } } diff --git a/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs b/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs index c5cba8e22c..f5bd44db5f 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs @@ -52,6 +52,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat // Local constants + // This computation is not the real region center if the region is larger than 256. + // This computation isn't fixed because there is not a handle back to the region. private static readonly Vector3 CenterOfRegion = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 20); private static readonly char[] CS_SPACE = { ' ' }; diff --git a/OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs b/OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs index d4fe5e0cde..550500113c 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs @@ -44,6 +44,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + // This computation is not the real region center if the region is larger than 256. + // This computation isn't fixed because there is not a handle back to the region. private static readonly OpenMetaverse.Vector3 CenterOfRegion = new OpenMetaverse.Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 20); private const int DEBUG_CHANNEL = 2147483647; diff --git a/OpenSim/Region/OptionalModules/PrimLimitsModule/PrimLimitsModule.cs b/OpenSim/Region/OptionalModules/PrimLimitsModule/PrimLimitsModule.cs index a375da903c..0bf23f1a5d 100644 --- a/OpenSim/Region/OptionalModules/PrimLimitsModule/PrimLimitsModule.cs +++ b/OpenSim/Region/OptionalModules/PrimLimitsModule/PrimLimitsModule.cs @@ -122,8 +122,8 @@ namespace OpenSim.Region.OptionalModules private bool CanObjectEnter(UUID objectID, bool enteringRegion, Vector3 newPoint, Scene scene) { - if (newPoint.X < -1f || newPoint.X > (float)(Constants.RegionSize + 1) || - newPoint.Y < -1f || newPoint.Y > (float)(Constants.RegionSize + 1)) + if (newPoint.X < -1f || newPoint.X > (scene.RegionInfo.RegionSizeX + 1) || + newPoint.Y < -1f || newPoint.Y > (scene.RegionInfo.RegionSizeY) ) return true; SceneObjectPart obj = scene.GetSceneObjectPart(objectID); diff --git a/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs b/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs index eb386fe423..296ab87274 100644 --- a/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs +++ b/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs @@ -170,7 +170,10 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady c.Channel = m_channelNotify; c.Message += numScriptsFailed.ToString() + "," + message; c.Type = ChatTypeEnum.Region; - c.Position = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 30); + if (m_scene != null) + c.Position = new Vector3((m_scene.RegionInfo.RegionSizeX * 0.5f), (m_scene.RegionInfo.RegionSizeY * 0.5f), 30); + else + c.Position = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 30); c.Sender = null; c.SenderUUID = UUID.Zero; c.Scene = m_scene; diff --git a/OpenSim/Region/OptionalModules/World/SceneCommands/SceneCommandsModule.cs b/OpenSim/Region/OptionalModules/World/SceneCommands/SceneCommandsModule.cs index 29b39e031a..6cbccc034f 100644 --- a/OpenSim/Region/OptionalModules/World/SceneCommands/SceneCommandsModule.cs +++ b/OpenSim/Region/OptionalModules/World/SceneCommands/SceneCommandsModule.cs @@ -116,37 +116,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments + "If teleport is true then some extra teleport debug information is logged.\n" + "If updates is true then any frame which exceeds double the maximum desired frame time is logged.", HandleDebugSceneSetCommand); - - scene.AddCommand( - "Regions", - this, "show borders", "show borders", "Show border information for regions", HandleShowBordersCommand); - } - - private void HandleShowBordersCommand(string module, string[] args) - { - StringBuilder sb = new StringBuilder(); - sb.AppendFormat("Borders for {0}:\n", m_scene.Name); - - ConsoleDisplayTable cdt = new ConsoleDisplayTable(); - cdt.AddColumn("Cross Direction", 15); - cdt.AddColumn("Line", 34); - cdt.AddColumn("Trigger Region", 14); - - foreach (Border b in m_scene.NorthBorders) - cdt.AddRow(b.CrossDirection, b.BorderLine, string.Format("{0}, {1}", b.TriggerRegionX, b.TriggerRegionY)); - - foreach (Border b in m_scene.EastBorders) - cdt.AddRow(b.CrossDirection, b.BorderLine, string.Format("{0}, {1}", b.TriggerRegionX, b.TriggerRegionY)); - - foreach (Border b in m_scene.SouthBorders) - cdt.AddRow(b.CrossDirection, b.BorderLine, string.Format("{0}, {1}", b.TriggerRegionX, b.TriggerRegionY)); - - foreach (Border b in m_scene.WestBorders) - cdt.AddRow(b.CrossDirection, b.BorderLine, string.Format("{0}, {1}", b.TriggerRegionX, b.TriggerRegionY)); - - cdt.AddToStringBuilder(sb); - - MainConsole.Instance.Output(sb.ToString()); } private void HandleDebugSceneGetCommand(string module, string[] args) @@ -263,4 +232,4 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments } } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs b/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs index 81448709cf..e4a33827ab 100644 --- a/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs +++ b/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs @@ -748,8 +748,8 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator position.X = s_tree.AbsolutePosition.X + (float)randX; position.Y = s_tree.AbsolutePosition.Y + (float)randY; - if (position.X <= ((int)Constants.RegionSize - 1) && position.X >= 0 && - position.Y <= ((int)Constants.RegionSize - 1) && position.Y >= 0 && + if (position.X <= (m_scene.RegionInfo.RegionSizeX - 1) && position.X >= 0 && + position.Y <= (m_scene.RegionInfo.RegionSizeY - 1) && position.Y >= 0 && Util.GetDistanceTo(position, copse.m_seed_point) <= copse.m_range) { UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner; diff --git a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs index 0816b7b799..8e405616fa 100644 --- a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs +++ b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs @@ -46,6 +46,7 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin private List _actors = new List(); private List _prims = new List(); private float[] _heightMap; + private Vector3 m_regionExtent; //protected internal string sceneIdentifier; @@ -58,6 +59,12 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin public override void Initialise(IMesher meshmerizer, IConfigSource config) { + throw new Exception("Should not be called."); + } + + public override void Initialise(IMesher meshmerizer, IConfigSource config, Vector3 regionExtent) + { + m_regionExtent = regionExtent; } public override void Dispose() {} @@ -121,23 +128,23 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin { actorPosition.Y = 0.1F; } - else if (actor.Position.Y >= Constants.RegionSize) + else if (actor.Position.Y >= m_regionExtent.Y) { - actorPosition.Y = ((int)Constants.RegionSize - 0.1f); + actorPosition.Y = (m_regionExtent.Y - 0.1f); } if (actor.Position.X < 0) { actorPosition.X = 0.1F; } - else if (actor.Position.X >= Constants.RegionSize) + else if (actor.Position.X >= m_regionExtent.X) { - actorPosition.X = ((int)Constants.RegionSize - 0.1f); + actorPosition.X = (m_regionExtent.X - 0.1f); } float terrainHeight = 0; if (_heightMap != null) - terrainHeight = _heightMap[(int)actor.Position.Y * Constants.RegionSize + (int)actor.Position.X]; + terrainHeight = _heightMap[(int)actor.Position.Y * (int)m_regionExtent.Y + (int)actor.Position.X]; float height = terrainHeight + actor.Size.Z; diff --git a/OpenSim/Region/Physics/Manager/PhysicsPluginManager.cs b/OpenSim/Region/Physics/Manager/PhysicsPluginManager.cs index 8ccfda5bc4..d14edfd770 100644 --- a/OpenSim/Region/Physics/Manager/PhysicsPluginManager.cs +++ b/OpenSim/Region/Physics/Manager/PhysicsPluginManager.cs @@ -32,6 +32,7 @@ using System.Reflection; using Nini.Config; using log4net; using OpenSim.Framework; +using OpenMetaverse; namespace OpenSim.Region.Physics.Manager { @@ -59,6 +60,14 @@ namespace OpenSim.Region.Physics.Manager m_log.Info("[PHYSICS]: Added meshing engine: " + plugHard.GetName()); } + // Legacy method for simulators before extent was passed + public PhysicsScene GetPhysicsScene(string physEngineName, string meshEngineName, + IConfigSource config, string regionName) + { + Vector3 extent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); + return GetPhysicsScene(physEngineName, meshEngineName, config, regionName, extent); + } + /// /// Get a physics scene for the given physics engine and mesher. /// @@ -66,7 +75,8 @@ namespace OpenSim.Region.Physics.Manager /// /// /// - public PhysicsScene GetPhysicsScene(string physEngineName, string meshEngineName, IConfigSource config, string regionName) + public PhysicsScene GetPhysicsScene(string physEngineName, string meshEngineName, + IConfigSource config, string regionName, Vector3 regionExtent) { if (String.IsNullOrEmpty(physEngineName)) { @@ -94,7 +104,7 @@ namespace OpenSim.Region.Physics.Manager { m_log.Info("[PHYSICS]: creating " + physEngineName); PhysicsScene result = _PhysPlugins[physEngineName].GetScene(regionName); - result.Initialise(meshEngine, config); + result.Initialise(meshEngine, config, regionExtent); return result; } else diff --git a/OpenSim/Region/Physics/Manager/PhysicsScene.cs b/OpenSim/Region/Physics/Manager/PhysicsScene.cs index dd9bbc1665..4f4ff0731d 100644 --- a/OpenSim/Region/Physics/Manager/PhysicsScene.cs +++ b/OpenSim/Region/Physics/Manager/PhysicsScene.cs @@ -132,8 +132,17 @@ namespace OpenSim.Region.Physics.Manager } } + // Deprecated. Do not use this for new physics engines. public abstract void Initialise(IMesher meshmerizer, IConfigSource config); + // For older physics engines that do not implement non-legacy region sizes. + // If the physics engine handles the region extent feature, it overrides this function. + public virtual void Initialise(IMesher meshmerizer, IConfigSource config, Vector3 regionExtent) + { + // If not overridden, call the old initialization entry. + Initialise(meshmerizer, config); + } + /// /// Add an avatar /// diff --git a/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs b/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs index 7127c73c18..341c8f8bd6 100644 --- a/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs +++ b/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs @@ -134,6 +134,49 @@ namespace OpenSim.Region.RegionCombinerModule throw new Exception(string.Format("Region with id {0} not found", regionId)); } + // Test to see if this postiion (relative to the region) is within the area covered + // by this megaregion. + public bool PositionIsInMegaregion(UUID currentRegion, int xx, int yy) + { + bool ret = false; + if (xx < 0 || yy < 0) + return ret; + + foreach (RegionConnections rootRegion in m_regions.Values) + { + if (currentRegion == rootRegion.RegionId) + { + // The caller is in the root region so this is an easy test + if (xx < rootRegion.XEnd && yy < rootRegion.YEnd) + { + ret = true; + } + break; + } + else + { + // Maybe the caller is in one of the sub-regions + foreach (RegionData childRegion in rootRegion.ConnectedRegions) + { + if (currentRegion == childRegion.RegionId) + { + // This is a child. Diddle the offsets and check if in + Vector3 positionInMegaregion = childRegion.Offset; + positionInMegaregion.X += xx; + positionInMegaregion.Y += yy; + if (positionInMegaregion.X < rootRegion.XEnd && positionInMegaregion.Y < rootRegion.YEnd) + { + ret = true; + } + break; + } + } + } + } + + return ret; + } + private void NewPresence(ScenePresence presence) { if (presence.IsChildAgent) @@ -220,27 +263,6 @@ namespace OpenSim.Region.RegionCombinerModule // */ - // Give each region a standard set of non-infinite borders - Border northBorder = new Border(); - northBorder.BorderLine = new Vector3(0, (int)Constants.RegionSize, (int)Constants.RegionSize); //<--- - northBorder.CrossDirection = Cardinals.N; - scene.NorthBorders[0] = northBorder; - - Border southBorder = new Border(); - southBorder.BorderLine = new Vector3(0, (int)Constants.RegionSize, 0); //---> - southBorder.CrossDirection = Cardinals.S; - scene.SouthBorders[0] = southBorder; - - Border eastBorder = new Border(); - eastBorder.BorderLine = new Vector3(0, (int)Constants.RegionSize, (int)Constants.RegionSize); //<--- - eastBorder.CrossDirection = Cardinals.E; - scene.EastBorders[0] = eastBorder; - - Border westBorder = new Border(); - westBorder.BorderLine = new Vector3(0, (int)Constants.RegionSize, 0); //---> - westBorder.CrossDirection = Cardinals.W; - scene.WestBorders[0] = westBorder; - RegionConnections newConn = new RegionConnections(); newConn.ConnectedRegions = new List(); newConn.RegionScene = scene; @@ -248,8 +270,8 @@ namespace OpenSim.Region.RegionCombinerModule newConn.RegionId = scene.RegionInfo.originRegionID; newConn.X = scene.RegionInfo.RegionLocX; newConn.Y = scene.RegionInfo.RegionLocY; - newConn.XEnd = (int)Constants.RegionSize; - newConn.YEnd = (int)Constants.RegionSize; + newConn.XEnd = scene.RegionInfo.RegionSizeX; + newConn.YEnd = scene.RegionInfo.RegionSizeX; lock (m_regions) { @@ -415,6 +437,11 @@ namespace OpenSim.Region.RegionCombinerModule */ #endregion + + // Check to see if this new region is adjacent to the root region. + // Note that we expect the regions to be combined from the root region outward + // thus the requirement for the ordering in the configuration files. + // If we're one region over +x y (i.e. root region is to the west) //xxx //xxy @@ -431,7 +458,7 @@ namespace OpenSim.Region.RegionCombinerModule //xxx if (rootConn.PosX >= newConn.PosX && rootConn.PosY + rootConn.YEnd >= newConn.PosY) { - connectedYN = DoWorkForOneRegionOverXPlusY(rootConn, newConn, scene); + connectedYN = DoWorkForOneRegionOverPlusXY(rootConn, newConn, scene); break; } @@ -441,9 +468,8 @@ namespace OpenSim.Region.RegionCombinerModule //xxx if (rootConn.PosX + rootConn.XEnd >= newConn.PosX && rootConn.PosY + rootConn.YEnd >= newConn.PosY) { - connectedYN = DoWorkForOneRegionOverPlusXPlusY(rootConn, newConn, scene); + connectedYN = DoWorkForOneRegionOverPlusXY(rootConn, newConn, scene); break; - } } @@ -453,20 +479,20 @@ namespace OpenSim.Region.RegionCombinerModule DoWorkForRootRegion(newConn, scene); } } - - // Set up infinite borders around the entire AABB of the combined ConnectedRegions - AdjustLargeRegionBounds(); } private bool DoWorkForOneRegionOverPlusXY(RegionConnections rootConn, RegionConnections newConn, Scene scene) { + // Offset (in meters) from the base of this region to the base of the root region. Vector3 offset = Vector3.Zero; offset.X = newConn.PosX - rootConn.PosX; offset.Y = newConn.PosY - rootConn.PosY; + // The new total size of the region (in meters) + // We just extend the X and Y dimensions so the extent might temporarily include areas without regions. Vector3 extents = Vector3.Zero; - extents.Y = rootConn.YEnd; - extents.X = rootConn.XEnd + newConn.XEnd; + extents.X = Math.Max(rootConn.XEnd, offset.X + newConn.RegionScene.RegionInfo.RegionSizeX); + extents.Y = Math.Max(rootConn.YEnd, offset.Y + newConn.RegionScene.RegionInfo.RegionSizeY); rootConn.UpdateExtents(extents); @@ -475,9 +501,6 @@ namespace OpenSim.Region.RegionCombinerModule rootConn.RegionScene.RegionInfo.RegionName, newConn.RegionScene.RegionInfo.RegionName, offset, extents); - scene.BordersLocked = true; - rootConn.RegionScene.BordersLocked = true; - RegionData ConnectedRegion = new RegionData(); ConnectedRegion.Offset = offset; ConnectedRegion.RegionId = scene.RegionInfo.originRegionID; @@ -490,34 +513,10 @@ namespace OpenSim.Region.RegionCombinerModule // Inform Child region that it needs to forward it's terrain to the root region scene.PhysicsScene.Combine(rootConn.RegionScene.PhysicsScene, offset, Vector3.Zero); - // Extend the borders as appropriate - lock (rootConn.RegionScene.EastBorders) - rootConn.RegionScene.EastBorders[0].BorderLine.Z += (int)Constants.RegionSize; - - lock (rootConn.RegionScene.NorthBorders) - rootConn.RegionScene.NorthBorders[0].BorderLine.Y += (int)Constants.RegionSize; - - lock (rootConn.RegionScene.SouthBorders) - rootConn.RegionScene.SouthBorders[0].BorderLine.Y += (int)Constants.RegionSize; - - lock (scene.WestBorders) - { - scene.WestBorders[0].BorderLine.Z = (int)((scene.RegionInfo.RegionLocX - rootConn.RegionScene.RegionInfo.RegionLocX) * (int)Constants.RegionSize); //auto teleport West - - // Trigger auto teleport to root region - scene.WestBorders[0].TriggerRegionX = rootConn.RegionScene.RegionInfo.RegionLocX; - scene.WestBorders[0].TriggerRegionY = rootConn.RegionScene.RegionInfo.RegionLocY; - } - // Reset Terrain.. since terrain loads before we get here, we need to load // it again so it loads in the root region - scene.PhysicsScene.SetTerrain(scene.Heightmap.GetFloatsSerialised()); - // Unlock borders - rootConn.RegionScene.BordersLocked = false; - scene.BordersLocked = false; - // Create a client event forwarder and add this region's events to the root region. if (rootConn.ClientEventForwarder != null) rootConn.ClientEventForwarder.AddSceneToEventForwarding(scene); @@ -525,6 +524,9 @@ namespace OpenSim.Region.RegionCombinerModule return true; } + /* + * 20140215 radams1: The border stuff was removed and the addition of regions to the mega-regions + * was generalized. These functions are not needed for the generalized solution but left for reference. private bool DoWorkForOneRegionOverXPlusY(RegionConnections rootConn, RegionConnections newConn, Scene scene) { Vector3 offset = Vector3.Zero; @@ -536,9 +538,6 @@ namespace OpenSim.Region.RegionCombinerModule extents.X = rootConn.XEnd; rootConn.UpdateExtents(extents); - scene.BordersLocked = true; - rootConn.RegionScene.BordersLocked = true; - RegionData ConnectedRegion = new RegionData(); ConnectedRegion.Offset = offset; ConnectedRegion.RegionId = scene.RegionInfo.originRegionID; @@ -553,30 +552,11 @@ namespace OpenSim.Region.RegionCombinerModule rootConn.RegionScene.PhysicsScene.Combine(null, Vector3.Zero, extents); scene.PhysicsScene.Combine(rootConn.RegionScene.PhysicsScene, offset, Vector3.Zero); - lock (rootConn.RegionScene.NorthBorders) - rootConn.RegionScene.NorthBorders[0].BorderLine.Z += (int)Constants.RegionSize; - - lock (rootConn.RegionScene.EastBorders) - rootConn.RegionScene.EastBorders[0].BorderLine.Y += (int)Constants.RegionSize; - - lock (rootConn.RegionScene.WestBorders) - rootConn.RegionScene.WestBorders[0].BorderLine.Y += (int)Constants.RegionSize; - - lock (scene.SouthBorders) - { - scene.SouthBorders[0].BorderLine.Z = (int)((scene.RegionInfo.RegionLocY - rootConn.RegionScene.RegionInfo.RegionLocY) * (int)Constants.RegionSize); //auto teleport south - scene.SouthBorders[0].TriggerRegionX = rootConn.RegionScene.RegionInfo.RegionLocX; - scene.SouthBorders[0].TriggerRegionY = rootConn.RegionScene.RegionInfo.RegionLocY; - } - // Reset Terrain.. since terrain normally loads first. //conn.RegionScene.PhysicsScene.SetTerrain(conn.RegionScene.Heightmap.GetFloatsSerialised()); scene.PhysicsScene.SetTerrain(scene.Heightmap.GetFloatsSerialised()); //conn.RegionScene.PhysicsScene.SetTerrain(conn.RegionScene.Heightmap.GetFloatsSerialised()); - scene.BordersLocked = false; - rootConn.RegionScene.BordersLocked = false; - if (rootConn.ClientEventForwarder != null) rootConn.ClientEventForwarder.AddSceneToEventForwarding(scene); @@ -600,9 +580,6 @@ namespace OpenSim.Region.RegionCombinerModule extents.Y = rootConn.YEnd; extents.X = rootConn.XEnd; - scene.BordersLocked = true; - rootConn.RegionScene.BordersLocked = true; - RegionData ConnectedRegion = new RegionData(); ConnectedRegion.Offset = offset; ConnectedRegion.RegionId = scene.RegionInfo.originRegionID; @@ -618,67 +595,10 @@ namespace OpenSim.Region.RegionCombinerModule rootConn.RegionScene.PhysicsScene.Combine(null, Vector3.Zero, extents); scene.PhysicsScene.Combine(rootConn.RegionScene.PhysicsScene, offset, Vector3.Zero); - lock (rootConn.RegionScene.NorthBorders) - { - if (rootConn.RegionScene.NorthBorders.Count == 1)// && 2) - { - //compound border - // already locked above - rootConn.RegionScene.NorthBorders[0].BorderLine.Z += (int)Constants.RegionSize; - - lock (rootConn.RegionScene.EastBorders) - rootConn.RegionScene.EastBorders[0].BorderLine.Y += (int)Constants.RegionSize; - - lock (rootConn.RegionScene.WestBorders) - rootConn.RegionScene.WestBorders[0].BorderLine.Y += (int)Constants.RegionSize; - } - } - - lock (scene.SouthBorders) - { - scene.SouthBorders[0].BorderLine.Z = (int)((scene.RegionInfo.RegionLocY - rootConn.RegionScene.RegionInfo.RegionLocY) * (int)Constants.RegionSize); //auto teleport south - scene.SouthBorders[0].TriggerRegionX = rootConn.RegionScene.RegionInfo.RegionLocX; - scene.SouthBorders[0].TriggerRegionY = rootConn.RegionScene.RegionInfo.RegionLocY; - } - - lock (rootConn.RegionScene.EastBorders) - { - if (rootConn.RegionScene.EastBorders.Count == 1)// && conn.RegionScene.EastBorders.Count == 2) - { - rootConn.RegionScene.EastBorders[0].BorderLine.Z += (int)Constants.RegionSize; - - lock (rootConn.RegionScene.NorthBorders) - rootConn.RegionScene.NorthBorders[0].BorderLine.Y += (int)Constants.RegionSize; - - lock (rootConn.RegionScene.SouthBorders) - rootConn.RegionScene.SouthBorders[0].BorderLine.Y += (int)Constants.RegionSize; - } - } - - lock (scene.WestBorders) - { - scene.WestBorders[0].BorderLine.Z = (int)((scene.RegionInfo.RegionLocX - rootConn.RegionScene.RegionInfo.RegionLocX) * (int)Constants.RegionSize); //auto teleport West - scene.WestBorders[0].TriggerRegionX = rootConn.RegionScene.RegionInfo.RegionLocX; - scene.WestBorders[0].TriggerRegionY = rootConn.RegionScene.RegionInfo.RegionLocY; - } - - /* - else - { - conn.RegionScene.NorthBorders[0].BorderLine.Z += (int)Constants.RegionSize; - conn.RegionScene.EastBorders[0].BorderLine.Y += (int)Constants.RegionSize; - conn.RegionScene.WestBorders[0].BorderLine.Y += (int)Constants.RegionSize; - scene.SouthBorders[0].BorderLine.Z += (int)Constants.RegionSize; //auto teleport south - } - */ - - // Reset Terrain.. since terrain normally loads first. //conn.RegionScene.PhysicsScene.SetTerrain(conn.RegionScene.Heightmap.GetFloatsSerialised()); scene.PhysicsScene.SetTerrain(scene.Heightmap.GetFloatsSerialised()); //conn.RegionScene.PhysicsScene.SetTerrain(conn.RegionScene.Heightmap.GetFloatsSerialised()); - scene.BordersLocked = false; - rootConn.RegionScene.BordersLocked = false; if (rootConn.ClientEventForwarder != null) rootConn.ClientEventForwarder.AddSceneToEventForwarding(scene); @@ -687,6 +607,7 @@ namespace OpenSim.Region.RegionCombinerModule //scene.PhysicsScene.Combine(conn.RegionScene.PhysicsScene, offset,extents); } + */ private void DoWorkForRootRegion(RegionConnections rootConn, Scene scene) { @@ -885,125 +806,6 @@ namespace OpenSim.Region.RegionCombinerModule // } // } - // Create a set of infinite borders around the whole aabb of the combined island. - private void AdjustLargeRegionBounds() - { - lock (m_regions) - { - foreach (RegionConnections rconn in m_regions.Values) - { - Vector3 offset = Vector3.Zero; - rconn.RegionScene.BordersLocked = true; - foreach (RegionData rdata in rconn.ConnectedRegions) - { - if (rdata.Offset.X > offset.X) offset.X = rdata.Offset.X; - if (rdata.Offset.Y > offset.Y) offset.Y = rdata.Offset.Y; - } - - lock (rconn.RegionScene.NorthBorders) - { - Border northBorder = null; - // If we don't already have an infinite border, create one. - if (!TryGetInfiniteBorder(rconn.RegionScene.NorthBorders, out northBorder)) - { - northBorder = new Border(); - rconn.RegionScene.NorthBorders.Add(northBorder); - } - - northBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue, - offset.Y + (int) Constants.RegionSize); //<--- - northBorder.CrossDirection = Cardinals.N; - } - - lock (rconn.RegionScene.SouthBorders) - { - Border southBorder = null; - // If we don't already have an infinite border, create one. - if (!TryGetInfiniteBorder(rconn.RegionScene.SouthBorders, out southBorder)) - { - southBorder = new Border(); - rconn.RegionScene.SouthBorders.Add(southBorder); - } - southBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue, 0); //---> - southBorder.CrossDirection = Cardinals.S; - } - - lock (rconn.RegionScene.EastBorders) - { - Border eastBorder = null; - // If we don't already have an infinite border, create one. - if (!TryGetInfiniteBorder(rconn.RegionScene.EastBorders, out eastBorder)) - { - eastBorder = new Border(); - rconn.RegionScene.EastBorders.Add(eastBorder); - } - eastBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue, offset.X + (int)Constants.RegionSize); - //<--- - eastBorder.CrossDirection = Cardinals.E; - } - - lock (rconn.RegionScene.WestBorders) - { - Border westBorder = null; - // If we don't already have an infinite border, create one. - if (!TryGetInfiniteBorder(rconn.RegionScene.WestBorders, out westBorder)) - { - westBorder = new Border(); - rconn.RegionScene.WestBorders.Add(westBorder); - - } - westBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue, 0); //---> - westBorder.CrossDirection = Cardinals.W; - } - - rconn.RegionScene.BordersLocked = false; - } - } - } - - /// - /// Try and get an Infinite border out of a listT of borders - /// - /// - /// - /// - public static bool TryGetInfiniteBorder(List borders, out Border oborder) - { - // Warning! Should be locked before getting here! - foreach (Border b in borders) - { - if (b.BorderLine.X == float.MinValue && b.BorderLine.Y == float.MaxValue) - { - oborder = b; - return true; - } - } - - oborder = null; - return false; - } - - public RegionData GetRegionFromPosition(Vector3 pPosition) - { - pPosition = pPosition/(int) Constants.RegionSize; - int OffsetX = (int) pPosition.X; - int OffsetY = (int) pPosition.Y; - - lock (m_regions) - { - foreach (RegionConnections regConn in m_regions.Values) - { - foreach (RegionData reg in regConn.ConnectedRegions) - { - if (reg.Offset.X == OffsetX && reg.Offset.Y == OffsetY) - return reg; - } - } - } - - return new RegionData(); - } - public void ForwardPermissionRequests(RegionConnections BigRegion, Scene VirtualRegion) { if (BigRegion.PermissionModule == null) diff --git a/OpenSim/Region/RegionCombinerModule/RegionConnections.cs b/OpenSim/Region/RegionCombinerModule/RegionConnections.cs index fba51d2efa..6bf1c4aea2 100644 --- a/OpenSim/Region/RegionCombinerModule/RegionConnections.cs +++ b/OpenSim/Region/RegionCombinerModule/RegionConnections.cs @@ -64,12 +64,12 @@ namespace OpenSim.Region.RegionCombinerModule /// /// The X meters position of this connection. /// - public uint PosX { get { return X * Constants.RegionSize; } } + public uint PosX { get { return Util.RegionToWorldLoc(X); } } /// /// The Y meters co-ordinate of this connection. /// - public uint PosY { get { return Y * Constants.RegionSize; } } + public uint PosY { get { return Util.RegionToWorldLoc(Y); } } /// /// The size of the megaregion in meters. @@ -91,4 +91,4 @@ namespace OpenSim.Region.RegionCombinerModule YEnd = (uint)extents.Y; } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 3c8f54fd06..b5ba4a0d62 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -2352,7 +2352,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // // This workaround is to prevent silent failure of this function. // According to the specification on the SL Wiki, providing a position outside of the - if (pos.x < 0 || pos.x > Constants.RegionSize || pos.y < 0 || pos.y > Constants.RegionSize) + if (pos.x < 0 || pos.x > World.RegionInfo.RegionSizeX || pos.y < 0 || pos.y > World.RegionInfo.RegionSizeY) { return 0; } @@ -2362,9 +2362,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.ParentGroup.IsAttachment || // return FALSE if attachment ( pos.x < -10.0 || // return FALSE if more than 10 meters into a west-adjacent region. - pos.x > (Constants.RegionSize + 10) || // return FALSE if more than 10 meters into a east-adjacent region. + pos.x > (World.RegionInfo.RegionSizeX + 10) || // return FALSE if more than 10 meters into a east-adjacent region. pos.y < -10.0 || // return FALSE if more than 10 meters into a south-adjacent region. - pos.y > (Constants.RegionSize + 10) || // return FALSE if more than 10 meters into a north-adjacent region. + pos.y > (World.RegionInfo.RegionSizeY + 10) || // return FALSE if more than 10 meters into a north-adjacent region. pos.z > Constants.RegionHeight // return FALSE if altitude than 4096m ) ) @@ -4655,10 +4655,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api DataserverPlugin.RegisterRequest(m_host.LocalId, m_item.ItemID, item.AssetID.ToString()); - Vector3 region = new Vector3( - World.RegionInfo.RegionLocX * Constants.RegionSize, - World.RegionInfo.RegionLocY * Constants.RegionSize, - 0); + Vector3 region = new Vector3(World.RegionInfo.WorldLocX, World.RegionInfo.WorldLocY, 0); World.AssetService.Get(item.AssetID.ToString(), this, delegate(string i, object sender, AssetBase a) @@ -5949,7 +5946,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Vector llGetRegionCorner() { m_host.AddScriptLPS(1); - return new LSL_Vector(World.RegionInfo.RegionLocX * Constants.RegionSize, World.RegionInfo.RegionLocY * Constants.RegionSize, 0); + return new LSL_Vector(World.RegionInfo.WorldLocX, World.RegionInfo.WorldLocY, 0); } /// @@ -6104,7 +6101,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api LSL_Float mag; if (dir.x > 0) { - mag = (Constants.RegionSize - pos.x) / dir.x; + mag = (World.RegionInfo.RegionSizeX - pos.x) / dir.x; } else { @@ -6115,7 +6112,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api edge.y = pos.y + (dir.y * mag); - if (edge.y > Constants.RegionSize || edge.y < 0) + if (edge.y > World.RegionInfo.RegionSizeY || edge.y < 0) { // Y goes out of bounds first edge.y = dir.y / Math.Abs(dir.y); diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index d2a59807da..01d90e7592 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -459,7 +459,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); - if (x > ((int)Constants.RegionSize - 1) || x < 0 || y > ((int)Constants.RegionSize - 1) || y < 0) + if (x > (World.RegionInfo.RegionSizeX - 1) || x < 0 || y > (World.RegionInfo.RegionSizeY - 1) || y < 0) OSSLError("osSetTerrainHeight: Coordinate out of bounds"); if (World.Permissions.CanTerraformLand(m_host.OwnerID, new Vector3(x, y, 0))) @@ -489,7 +489,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api private LSL_Float GetTerrainHeight(int x, int y) { m_host.AddScriptLPS(1); - if (x > ((int)Constants.RegionSize - 1) || x < 0 || y > ((int)Constants.RegionSize - 1) || y < 0) + if (x > (World.RegionInfo.RegionSizeX - 1) || x < 0 || y > (World.RegionInfo.RegionSizeY - 1) || y < 0) OSSLError("osGetTerrainHeight: Coordinate out of bounds"); return World.Heightmap[x, y]; @@ -823,7 +823,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api private void TeleportAgent(string agent, int regionX, int regionY, LSL_Types.Vector3 position, LSL_Types.Vector3 lookat, bool relaxRestrictions) { - ulong regionHandle = Util.UIntsToLong(((uint)regionX * (uint)Constants.RegionSize), ((uint)regionY * (uint)Constants.RegionSize)); + ulong regionHandle = Util.RegionLocToHandle((uint)regionX, (uint)regionY); m_host.AddScriptLPS(1); UUID agentId = new UUID(); @@ -3024,7 +3024,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } else { - return new LSL_Vector((float)Constants.RegionSize, (float)Constants.RegionSize, Constants.RegionHeight); + return new LSL_Vector((float)World.RegionInfo.RegionSizeX, + (float)World.RegionInfo.RegionSizeY, + (float)World.RegionInfo.RegionSizeZ ); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs index 9cf7b35ad2..86664210ea 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs @@ -708,18 +708,20 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase } private void Save() { + /* Remove temporarily until we have a handle to the region size if (Position.x > ((int)Constants.RegionSize - 1)) Position.x = ((int)Constants.RegionSize - 1); - if (Position.x < 0) - Position.x = 0; if (Position.y > ((int)Constants.RegionSize - 1)) Position.y = ((int)Constants.RegionSize - 1); + */ + if (Position.x < 0) + Position.x = 0; if (Position.y < 0) Position.y = 0; - if (Position.z > 768) - Position.z = 768; if (Position.z < 0) Position.z = 0; + if (Position.z > Constants.RegionHeight) + Position.z = Constants.RegionHeight; prim.OSSL.llSetPos(Position); } diff --git a/OpenSim/Server/Handlers/Map/MapAddServerConnector.cs b/OpenSim/Server/Handlers/Map/MapAddServerConnector.cs index 7ee347ca4d..d2fbfa65d1 100644 --- a/OpenSim/Server/Handlers/Map/MapAddServerConnector.cs +++ b/OpenSim/Server/Handlers/Map/MapAddServerConnector.cs @@ -133,7 +133,7 @@ namespace OpenSim.Server.Handlers.MapImage if (m_GridService != null) { System.Net.IPAddress ipAddr = GetCallerIP(httpRequest); - GridRegion r = m_GridService.GetRegionByPosition(UUID.Zero, x * (int)Constants.RegionSize, y * (int)Constants.RegionSize); + GridRegion r = m_GridService.GetRegionByPosition(UUID.Zero, (int)Util.RegionToWorldLoc((uint)x), (int)Util.RegionToWorldLoc((uint)y)); if (r != null) { if (r.ExternalEndPoint.Address.ToString() != ipAddr.ToString()) diff --git a/OpenSim/Server/Handlers/Map/MapRemoveServerConnector.cs b/OpenSim/Server/Handlers/Map/MapRemoveServerConnector.cs index 8a31579c7f..88d774a4f6 100644 --- a/OpenSim/Server/Handlers/Map/MapRemoveServerConnector.cs +++ b/OpenSim/Server/Handlers/Map/MapRemoveServerConnector.cs @@ -128,7 +128,7 @@ namespace OpenSim.Server.Handlers.MapImage if (m_GridService != null) { System.Net.IPAddress ipAddr = GetCallerIP(httpRequest); - GridRegion r = m_GridService.GetRegionByPosition(UUID.Zero, x * (int)Constants.RegionSize, y * (int)Constants.RegionSize); + GridRegion r = m_GridService.GetRegionByPosition(UUID.Zero, (int)Util.RegionToWorldLoc((uint)x), (int)Util.RegionToWorldLoc((uint)y)); if (r != null) { if (r.ExternalEndPoint.Address.ToString() != ipAddr.ToString()) diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs index 3cf341630c..312832f08c 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs @@ -112,7 +112,7 @@ namespace OpenSim.Services.Connectors.SimianGrid // m_log.Warn("Registering region " + regionInfo.RegionName + " (" + regionInfo.RegionID + ") that we are not tracking"); Vector3d minPosition = new Vector3d(regionInfo.RegionLocX, regionInfo.RegionLocY, 0.0); - Vector3d maxPosition = minPosition + new Vector3d(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); + Vector3d maxPosition = minPosition + new Vector3d(regionInfo.RegionSizeX, regionInfo.RegionSizeY, Constants.RegionHeight); OSDMap extraData = new OSDMap { @@ -174,8 +174,8 @@ namespace OpenSim.Services.Connectors.SimianGrid if (region != null) { List regions = GetRegionRange(scopeID, - region.RegionLocX - NEIGHBOR_RADIUS, region.RegionLocX + (int)Constants.RegionSize + NEIGHBOR_RADIUS, - region.RegionLocY - NEIGHBOR_RADIUS, region.RegionLocY + (int)Constants.RegionSize + NEIGHBOR_RADIUS); + region.RegionLocX - NEIGHBOR_RADIUS, region.RegionLocX + region.RegionSizeX + NEIGHBOR_RADIUS, + region.RegionLocY - NEIGHBOR_RADIUS, region.RegionLocY + region.RegionSizeY + NEIGHBOR_RADIUS); for (int i = 0; i < regions.Count; i++) { @@ -240,7 +240,7 @@ namespace OpenSim.Services.Connectors.SimianGrid else { // m_log.InfoFormat("[SIMIAN GRID CONNECTOR]: Grid service did not find a match for region at {0},{1}", - // x / Constants.RegionSize, y / Constants.RegionSize); + // Util.WorldToRegionLoc(x), Util.WorldToRegionLoc(y)); return null; } } diff --git a/OpenSim/Services/Connectors/Simulation/SimulationDataService.cs b/OpenSim/Services/Connectors/Simulation/SimulationDataService.cs index 96c02d90fc..475983816c 100644 --- a/OpenSim/Services/Connectors/Simulation/SimulationDataService.cs +++ b/OpenSim/Services/Connectors/Simulation/SimulationDataService.cs @@ -100,6 +100,11 @@ namespace OpenSim.Services.Connectors return m_database.LoadObjects(regionUUID); } + public void StoreTerrain(TerrainData terrain, UUID regionID) + { + m_database.StoreTerrain(terrain, regionID); + } + public void StoreTerrain(double[,] terrain, UUID regionID) { m_database.StoreTerrain(terrain, regionID); @@ -110,6 +115,11 @@ namespace OpenSim.Services.Connectors return m_database.LoadTerrain(regionID); } + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + return m_database.LoadTerrain(regionID, pSizeX, pSizeY, pSizeZ); + } + public void StoreLandObject(ILandObject Parcel) { m_database.StoreLandObject(Parcel); diff --git a/OpenSim/Services/GridService/GridService.cs b/OpenSim/Services/GridService/GridService.cs index 76415ce7c2..eb44dcb217 100644 --- a/OpenSim/Services/GridService/GridService.cs +++ b/OpenSim/Services/GridService/GridService.cs @@ -655,7 +655,7 @@ namespace OpenSim.Services.GridService return; } - RegionData region = m_Database.Get(x * (int)Constants.RegionSize, y * (int)Constants.RegionSize, UUID.Zero); + RegionData region = m_Database.Get((int)Util.RegionToWorldLoc((uint)x), (int)Util.RegionToWorldLoc((uint)y), UUID.Zero); if (region == null) { MainConsole.Instance.OutputFormat("No region found at {0},{1}", x, y); @@ -673,6 +673,7 @@ namespace OpenSim.Services.GridService dispList.AddRow("Region Name", r.RegionName); dispList.AddRow("Region ID", r.RegionID); dispList.AddRow("Location", string.Format("{0},{1}", r.coordX, r.coordY)); + dispList.AddRow("Size", string.Format("{0}x{1}", r.sizeX, r.sizeY)); dispList.AddRow("URI", r.Data["serverURI"]); dispList.AddRow("Owner ID", r.Data["owner_uuid"]); dispList.AddRow("Flags", flags); diff --git a/OpenSim/Services/GridService/HypergridLinker.cs b/OpenSim/Services/GridService/HypergridLinker.cs index 402429597b..0448b54b51 100644 --- a/OpenSim/Services/GridService/HypergridLinker.cs +++ b/OpenSim/Services/GridService/HypergridLinker.cs @@ -183,8 +183,8 @@ namespace OpenSim.Services.GridService public GridRegion LinkRegion(UUID scopeID, string regionDescriptor) { string reason = string.Empty; - int xloc = random.Next(0, Int16.MaxValue) * (int)Constants.RegionSize; - return TryLinkRegionToCoords(scopeID, regionDescriptor, xloc, 0, out reason); + uint xloc = Util.RegionToWorldLoc((uint)random.Next(0, Int16.MaxValue)); + return TryLinkRegionToCoords(scopeID, regionDescriptor, (int)xloc, 0, out reason); } private static Random random = new Random(); @@ -260,7 +260,7 @@ namespace OpenSim.Services.GridService { m_log.DebugFormat("[HYPERGRID LINKER]: Link to {0} {1}, in {2}-{3}", ((serverURI == null) ? (externalHostName + ":" + externalPort) : serverURI), - remoteRegionName, xloc / Constants.RegionSize, yloc / Constants.RegionSize); + remoteRegionName, Util.WorldToRegionLoc((uint)xloc), Util.WorldToRegionLoc((uint)yloc)); reason = string.Empty; Uri uri = null; @@ -311,7 +311,7 @@ namespace OpenSim.Services.GridService if (region != null) { m_log.WarnFormat("[HYPERGRID LINKER]: Coordinates {0}-{1} are already occupied by region {2} with uuid {3}", - regInfo.RegionLocX / Constants.RegionSize, regInfo.RegionLocY / Constants.RegionSize, + Util.WorldToRegionLoc((uint)regInfo.RegionLocX), Util.WorldToRegionLoc((uint)regInfo.RegionLocY), region.RegionName, region.RegionID); reason = "Coordinates are already in use"; return false; @@ -347,7 +347,7 @@ namespace OpenSim.Services.GridService if (region != null) { m_log.DebugFormat("[HYPERGRID LINKER]: Region already exists in coordinates {0} {1}", - region.RegionLocX / Constants.RegionSize, region.RegionLocY / Constants.RegionSize); + Util.WorldToRegionLoc((uint)regInfo.RegionLocX), Util.WorldToRegionLoc((uint)regInfo.RegionLocY)); regInfo = region; return true; } @@ -424,10 +424,10 @@ namespace OpenSim.Services.GridService // { // uint ux = 0, uy = 0; // Utils.LongToUInts(realHandle, out ux, out uy); -// x = ux / Constants.RegionSize; -// y = uy / Constants.RegionSize; +// x = Util.WorldToRegionLoc(ux); +// y = Util.WorldToRegionLoc(uy); // -// const uint limit = (4096 - 1) * Constants.RegionSize; +// const uint limit = Util.RegionToWorldLoc(4096 - 1); // uint xmin = ux - limit; // uint xmax = ux + limit; // uint ymin = uy - limit; @@ -503,8 +503,9 @@ namespace OpenSim.Services.GridService foreach (RegionData r in regions) { MainConsole.Instance.Output(String.Format("{0}\n{2,-32} {1}\n", - r.RegionName, r.RegionID, String.Format("{0},{1} ({2},{3})", r.posX, r.posY, - r.posX / Constants.RegionSize, r.posY / Constants.RegionSize))); + r.RegionName, r.RegionID, + String.Format("{0},{1} ({2},{3})", r.posX, r.posY, + Util.WorldToRegionLoc((uint)r.posX), Util.WorldToRegionLoc((uint)r.posY))) ); } return; } @@ -529,8 +530,8 @@ namespace OpenSim.Services.GridService int xloc, yloc; string serverURI; string remoteName = null; - xloc = Convert.ToInt32(cmdparams[0]) * (int)Constants.RegionSize; - yloc = Convert.ToInt32(cmdparams[1]) * (int)Constants.RegionSize; + xloc = (int)Util.RegionToWorldLoc((uint)Convert.ToInt32(cmdparams[0])); + yloc = (int)Util.RegionToWorldLoc((uint)Convert.ToInt32(cmdparams[1])); serverURI = cmdparams[2]; if (cmdparams.Length > 3) remoteName = string.Join(" ", cmdparams, 3, cmdparams.Length - 3); @@ -601,13 +602,13 @@ namespace OpenSim.Services.GridService { // old format GridRegion regInfo; - int xloc, yloc; + uint xloc, yloc; uint externalPort; string externalHostName; try { - xloc = Convert.ToInt32(cmdparams[0]); - yloc = Convert.ToInt32(cmdparams[1]); + xloc = (uint)Convert.ToInt32(cmdparams[0]); + yloc = (uint)Convert.ToInt32(cmdparams[1]); externalPort = Convert.ToUInt32(cmdparams[3]); externalHostName = cmdparams[2]; //internalPort = Convert.ToUInt32(cmdparams[4]); @@ -621,10 +622,11 @@ namespace OpenSim.Services.GridService } // Convert cell coordinates given by the user to meters - xloc = xloc * (int)Constants.RegionSize; - yloc = yloc * (int)Constants.RegionSize; + xloc = Util.RegionToWorldLoc(xloc); + yloc = Util.RegionToWorldLoc(yloc); string reason = string.Empty; - if (TryCreateLink(UUID.Zero, xloc, yloc, string.Empty, externalPort, externalHostName, UUID.Zero, out regInfo, out reason)) + if (TryCreateLink(UUID.Zero, (int)xloc, (int)yloc, + string.Empty, externalPort, externalHostName, UUID.Zero, out regInfo, out reason)) { // What is this? The GridRegion instance will be discarded anyway, // which effectively ignores any local name given with the command. @@ -704,13 +706,13 @@ namespace OpenSim.Services.GridService private void ReadLinkFromConfig(IConfig config) { GridRegion regInfo; - int xloc, yloc; + uint xloc, yloc; uint externalPort; string externalHostName; uint realXLoc, realYLoc; - xloc = Convert.ToInt32(config.GetString("xloc", "0")); - yloc = Convert.ToInt32(config.GetString("yloc", "0")); + xloc = (uint)Convert.ToInt32(config.GetString("xloc", "0")); + yloc = (uint)Convert.ToInt32(config.GetString("yloc", "0")); externalPort = Convert.ToUInt32(config.GetString("externalPort", "0")); externalHostName = config.GetString("externalHostName", ""); realXLoc = Convert.ToUInt32(config.GetString("real-xloc", "0")); @@ -718,18 +720,19 @@ namespace OpenSim.Services.GridService if (m_enableAutoMapping) { - xloc = (int)((xloc % 100) + m_autoMappingX); - yloc = (int)((yloc % 100) + m_autoMappingY); + xloc = (uint)((xloc % 100) + m_autoMappingX); + yloc = (uint)((yloc % 100) + m_autoMappingY); } if (((realXLoc == 0) && (realYLoc == 0)) || (((realXLoc - xloc < 3896) || (xloc - realXLoc < 3896)) && ((realYLoc - yloc < 3896) || (yloc - realYLoc < 3896)))) { - xloc = xloc * (int)Constants.RegionSize; - yloc = yloc * (int)Constants.RegionSize; + xloc = Util.RegionToWorldLoc(xloc); + yloc = Util.RegionToWorldLoc(yloc); string reason = string.Empty; - if (TryCreateLink(UUID.Zero, xloc, yloc, string.Empty, externalPort, externalHostName, UUID.Zero, out regInfo, out reason)) + if (TryCreateLink(UUID.Zero, (int)xloc, (int)yloc, + string.Empty, externalPort, externalHostName, UUID.Zero, out regInfo, out reason)) { regInfo.RegionName = config.GetString("localName", ""); } diff --git a/OpenSim/Services/Interfaces/IGridService.cs b/OpenSim/Services/Interfaces/IGridService.cs index 19ea0fed1f..1119e48a63 100644 --- a/OpenSim/Services/Interfaces/IGridService.cs +++ b/OpenSim/Services/Interfaces/IGridService.cs @@ -179,14 +179,14 @@ namespace OpenSim.Services.Interfaces protected IPEndPoint m_internalEndPoint; /// - /// The co-ordinate of this region. + /// The co-ordinate of this region in region units. /// - public int RegionCoordX { get { return RegionLocX / (int)Constants.RegionSize; } } + public int RegionCoordX { get { return (int)Util.WorldToRegionLoc((uint)RegionLocX); } } /// - /// The co-ordinate of this region + /// The co-ordinate of this region in region units /// - public int RegionCoordY { get { return RegionLocY / (int)Constants.RegionSize; } } + public int RegionCoordY { get { return (int)Util.WorldToRegionLoc((uint)RegionLocY); } } /// /// The location of this region in meters. @@ -265,8 +265,8 @@ namespace OpenSim.Services.Interfaces public GridRegion(uint xcell, uint ycell) { - m_regionLocX = (int)(xcell * Constants.RegionSize); - m_regionLocY = (int)(ycell * Constants.RegionSize); + m_regionLocX = (int)Util.RegionToWorldLoc(xcell); + m_regionLocY = (int)Util.RegionToWorldLoc(ycell); RegionSizeX = (int)Constants.RegionSize; RegionSizeY = (int)Constants.RegionSize; } @@ -274,8 +274,8 @@ namespace OpenSim.Services.Interfaces public GridRegion(RegionInfo ConvertFrom) { m_regionName = ConvertFrom.RegionName; - m_regionLocX = (int)(ConvertFrom.RegionLocX * Constants.RegionSize); - m_regionLocY = (int)(ConvertFrom.RegionLocY * Constants.RegionSize); + m_regionLocX = (int)(ConvertFrom.WorldLocX); + m_regionLocY = (int)(ConvertFrom.WorldLocY); RegionSizeX = (int)ConvertFrom.RegionSizeX; RegionSizeY = (int)ConvertFrom.RegionSizeY; m_internalEndPoint = ConvertFrom.InternalEndPoint; diff --git a/OpenSim/Services/LLLoginService/LLLoginResponse.cs b/OpenSim/Services/LLLoginService/LLLoginResponse.cs index f641955969..da351b9add 100644 --- a/OpenSim/Services/LLLoginService/LLLoginResponse.cs +++ b/OpenSim/Services/LLLoginService/LLLoginResponse.cs @@ -368,7 +368,8 @@ namespace OpenSim.Services.LLLoginService private void FillOutHomeData(GridUserInfo pinfo, GridRegion home) { - int x = 1000 * (int)Constants.RegionSize, y = 1000 * (int)Constants.RegionSize; + int x = (int)Util.RegionToWorldLoc(1000); + int y = (int)Util.RegionToWorldLoc(1000); if (home != null) { x = home.RegionLocX; @@ -443,10 +444,23 @@ namespace OpenSim.Services.LLLoginService ErrorReason = "key"; welcomeMessage = "Welcome to OpenSim!"; seedCapability = String.Empty; - home = "{'region_handle':[r" + (1000*Constants.RegionSize).ToString() + ",r" + (1000*Constants.RegionSize).ToString() + "], 'position':[r" + - userProfile.homepos.X.ToString() + ",r" + userProfile.homepos.Y.ToString() + ",r" + - userProfile.homepos.Z.ToString() + "], 'look_at':[r" + userProfile.homelookat.X.ToString() + ",r" + - userProfile.homelookat.Y.ToString() + ",r" + userProfile.homelookat.Z.ToString() + "]}"; + home = "{'region_handle':[" + + "r" + Util.RegionToWorldLoc(1000).ToString() + + "," + + "r" + Util.RegionToWorldLoc(1000).ToString() + + "], 'position':[" + + "r" + userProfile.homepos.X.ToString() + + "," + + "r" + userProfile.homepos.Y.ToString() + + "," + + "r" + userProfile.homepos.Z.ToString() + + "], 'look_at':[" + + "r" + userProfile.homelookat.X.ToString() + + "," + + "r" + userProfile.homelookat.Y.ToString() + + "," + + "r" + userProfile.homelookat.Z.ToString() + + "]}"; lookAt = "[r0.99949799999999999756,r0.03166859999999999814,r0]"; RegionX = (uint) 255232; RegionY = (uint) 254976; diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index 1c1c9b0a98..c8331312d1 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -695,7 +695,7 @@ namespace OpenSim.Services.LLLoginService private GridRegion FindAlternativeRegion(UUID scopeID) { List hyperlinks = null; - List regions = m_GridService.GetFallbackRegions(scopeID, 1000 * (int)Constants.RegionSize, 1000 * (int)Constants.RegionSize); + List regions = m_GridService.GetFallbackRegions(scopeID, (int)Util.RegionToWorldLoc(1000), (int)Util.RegionToWorldLoc(1000)); if (regions != null && regions.Count > 0) { hyperlinks = m_GridService.GetHyperlinks(scopeID); diff --git a/OpenSim/Tests/Clients/Grid/GridClient.cs b/OpenSim/Tests/Clients/Grid/GridClient.cs index 8e3337319b..fed7a16922 100644 --- a/OpenSim/Tests/Clients/Grid/GridClient.cs +++ b/OpenSim/Tests/Clients/Grid/GridClient.cs @@ -150,16 +150,16 @@ namespace OpenSim.Tests.Clients.GridClient Console.WriteLine("[GRID CLIENT]: *** GetRegionRange (this should return 2 regions)"); regions = m_Connector.GetRegionRange(UUID.Zero, - 900 * (int)Constants.RegionSize, 1002 * (int) Constants.RegionSize, - 900 * (int)Constants.RegionSize, 1002 * (int) Constants.RegionSize); + (int)Util.RegionToWorldLoc(900), (int)Util.RegionToWorldLoc(1002), + (int)Util.RegionToWorldLoc(900), (int)Util.RegionToWorldLoc(1002) ); if (regions == null) Console.WriteLine("[GRID CLIENT]: GetRegionRange returned null"); else Console.WriteLine("[GRID CLIENT]: GetRegionRange returned " + regions.Count + " regions"); Console.WriteLine("[GRID CLIENT]: *** GetRegionRange (this should return 0 regions)"); regions = m_Connector.GetRegionRange(UUID.Zero, - 900 * (int)Constants.RegionSize, 950 * (int)Constants.RegionSize, - 900 * (int)Constants.RegionSize, 950 * (int)Constants.RegionSize); + (int)Util.RegionToWorldLoc(900), (int)Util.RegionToWorldLoc(950), + (int)Util.RegionToWorldLoc(900), (int)Util.RegionToWorldLoc(950) ); if (regions == null) Console.WriteLine("[GRID CLIENT]: GetRegionRange returned null"); else diff --git a/OpenSim/Tests/Common/Helpers/EntityTransferHelpers.cs b/OpenSim/Tests/Common/Helpers/EntityTransferHelpers.cs index 52a17e7a5f..84de47f5df 100644 --- a/OpenSim/Tests/Common/Helpers/EntityTransferHelpers.cs +++ b/OpenSim/Tests/Common/Helpers/EntityTransferHelpers.cs @@ -69,9 +69,7 @@ namespace OpenSim.Tests.Common tc.OnTestClientInformClientOfNeighbour += (neighbourHandle, neighbourExternalEndPoint) => { uint x, y; - Utils.LongToUInts(neighbourHandle, out x, out y); - x /= Constants.RegionSize; - y /= Constants.RegionSize; + Util.RegionHandleToRegionLoc(neighbourHandle, out x, out y); m_log.DebugFormat( "[TEST CLIENT]: Processing inform client of neighbour located at {0},{1} at {2}", @@ -104,9 +102,7 @@ namespace OpenSim.Tests.Common += (regionHandle, simAccess, regionExternalEndPoint, locationID, flags, capsURL) => { uint x, y; - Utils.LongToUInts(regionHandle, out x, out y); - x /= Constants.RegionSize; - y /= Constants.RegionSize; + Util.RegionHandleToRegionLoc(regionHandle, out x, out y); m_log.DebugFormat( "[TEST CLIENT]: Processing send region teleport for destination at {0},{1} at {2}", @@ -125,4 +121,4 @@ namespace OpenSim.Tests.Common }; } } -} \ No newline at end of file +} diff --git a/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs b/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs index 5c1ec0bebd..3ab90207a5 100644 --- a/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs +++ b/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs @@ -69,11 +69,21 @@ namespace OpenSim.Data.Null m_store.StoreTerrain(terrain, regionID); } + public void StoreTerrain(TerrainData terrain, UUID regionID) + { + m_store.StoreTerrain(terrain, regionID); + } + public double[,] LoadTerrain(UUID regionID) { return m_store.LoadTerrain(regionID); } + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + return m_store.LoadTerrain(regionID, pSizeX, pSizeY, pSizeZ); + } + public void StoreLandObject(ILandObject Parcel) { m_store.StoreLandObject(Parcel); @@ -159,7 +169,7 @@ namespace OpenSim.Data.Null protected Dictionary m_sceneObjectParts = new Dictionary(); protected Dictionary> m_primItems = new Dictionary>(); - protected Dictionary m_terrains = new Dictionary(); + protected Dictionary m_terrains = new Dictionary(); protected Dictionary m_landData = new Dictionary(); public void Initialise(string dbfile) @@ -304,15 +314,28 @@ namespace OpenSim.Data.Null return new List(objects.Values); } - public void StoreTerrain(double[,] ter, UUID regionID) + public void StoreTerrain(TerrainData ter, UUID regionID) { m_terrains[regionID] = ter; } + public void StoreTerrain(double[,] ter, UUID regionID) + { + m_terrains[regionID] = new HeightmapTerrainData(ter); + } + + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + if (m_terrains.ContainsKey(regionID)) + return m_terrains[regionID]; + else + return null; + } + public double[,] LoadTerrain(UUID regionID) { if (m_terrains.ContainsKey(regionID)) - return m_terrains[regionID]; + return m_terrains[regionID].GetDoubles(); else return null; }