diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index e5835902c1..70b0e0abc3 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -820,7 +820,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer agentCircuit.Id0 = currentAgentCircuit.Id0; } - if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) + // if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) + if (NeedsNewAgent(sp.Scene.DefaultDrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) { // brand new agent, let's create a new caps seed agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath(); @@ -894,7 +895,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer IClientIPEndpoint ipepClient; string capsPath = String.Empty; - if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) + if (NeedsNewAgent(sp.Scene.DefaultDrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) { m_log.DebugFormat( "[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for incoming agent {3} from {4}", @@ -1070,7 +1071,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone - if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) + if (NeedsClosing(sp.Scene.DefaultDrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) { if (!sp.Scene.IncomingPreCloseClient(sp)) return; @@ -1140,7 +1141,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer IClientIPEndpoint ipepClient; string capsPath = String.Empty; - if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) + if (NeedsNewAgent(sp.Scene.DefaultDrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) { m_log.DebugFormat( "[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for agent {3} from {4}", @@ -1236,7 +1237,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer sp.MakeChildAgent(); // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone - if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) + if (NeedsClosing(sp.Scene.DefaultDrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) { if (!sp.Scene.IncomingPreCloseClient(sp)) return; @@ -2366,7 +2367,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer { // 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.DrawDistance, + uint dd = Math.Max((uint)avatar.Scene.DefaultDrawDistance, Math.Max(Scene.RegionInfo.RegionSizeX, Scene.RegionInfo.RegionSizeY)); uint startX = Util.RegionToWorldLoc(pRegionLocX) - dd + Constants.RegionSize/2; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs index 3b38c71dfd..d5fca71dff 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs @@ -199,6 +199,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid public GridRegion GetRegionByPosition(UUID scopeID, int x, int y) { GridRegion region = null; + uint regionX = Util.WorldToRegionLoc((uint)x); + uint regionY = Util.WorldToRegionLoc((uint)y); // First see if it's a neighbour, even if it isn't on this sim. // Neighbour data is cached in memory, so this is fast @@ -222,11 +224,15 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid { region = m_GridService.GetRegionByPosition(scopeID, x, y); if (region == null) + { m_log.DebugFormat("{0} GetRegionByPosition. Region not found by grid service. Pos=<{1},{2}>", - LogHeader, x, y); + LogHeader, regionX, regionY); + } else + { m_log.DebugFormat("{0} GetRegionByPosition. Requested region {1} from grid service. Pos=<{2},{3}>", - LogHeader, region.RegionName, x, y); + LogHeader, region.RegionName, regionX, regionY); + } } return region; } diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs index 6e1e8d6120..c22c5b6d7b 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs @@ -96,9 +96,88 @@ namespace OpenSim.Region.CoreModules.World.Terrain private Scene m_scene; private volatile bool m_tainted; private readonly Stack m_undo = new Stack(5); - + private String m_InitialTerrain = "pinhead-island"; + // If true, send terrain patch updates to clients based on their view distance + private bool m_sendTerrainUpdatesByViewDistance = false; + + // 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. /// @@ -127,7 +206,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) @@ -422,9 +504,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 () { - m_channel.GetTerrainData().TaintAllTerrain(); + 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 @@ -676,6 +795,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain } } } + + // This event also causes changes to be sent to the clients + CheckSendingPatchesToClients(); + + // If things changes, generate some events if (shouldTaint) { m_scene.EventManager.TriggerTerrainTainted(); @@ -749,30 +873,12 @@ namespace OpenSim.Region.CoreModules.World.Terrain presence.ControllingClient.OnLandUndo -= client_OnLandUndo; presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain; } + if (m_perClientPatchUpdates.ContainsKey(client)) + { + m_perClientPatchUpdates.Remove(client); + } } - /// - /// 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 - /// - private void CheckForTerrainUpdates() - { - 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) - { - } - /// /// Scan over changes in the terrain and limit height changes. This enforces the /// non-estate owner limits on rate of terrain editting. @@ -857,17 +963,140 @@ namespace OpenSim.Region.CoreModules.World.Terrain /// The patch corner to send private void SendToClients(TerrainData terrData, int x, int y) { - // 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) + 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); + 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) + { + // TODO: one can send multiple patches in a packet. Do that. + pups.Presence.ControllingClient.SendLayerData(pts.PatchX, pts.PatchY, null); + // presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), null, TerrainPatch.LayerType.Land); + } + } + } + } + } + } + + 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, diff --git a/OpenSim/Region/Framework/Interfaces/ITerrainModule.cs b/OpenSim/Region/Framework/Interfaces/ITerrainModule.cs index a6f5d9838e..28f797ab61 100644 --- a/OpenSim/Region/Framework/Interfaces/ITerrainModule.cs +++ b/OpenSim/Region/Framework/Interfaces/ITerrainModule.cs @@ -24,9 +24,10 @@ * (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.IO; + +using OpenSim.Framework; + using OpenMetaverse; namespace OpenSim.Region.Framework.Interfaces @@ -43,6 +44,12 @@ namespace OpenSim.Region.Framework.Interfaces /// void TaintTerrain(); + /// + /// When a client initially connects, all the terrain must be pushed to the viewer. + /// This call causes all the terrain patches to be sent to the client. + /// + void PushTerrain(IClientAPI pClient); + /// /// Load a terrain from a stream. /// diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs index 420866988f..0445268315 100644 --- a/OpenSim/Region/Framework/Scenes/SceneBase.cs +++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs @@ -213,7 +213,12 @@ namespace OpenSim.Region.Framework.Scenes /// Client to send to public virtual void SendLayerData(IClientAPI RemoteClient) { - RemoteClient.SendLayerData(Heightmap.GetFloatsSerialised()); + // RemoteClient.SendLayerData(Heightmap.GetFloatsSerialised()); + ITerrainModule terrModule = RequestModuleInterface(); + if (terrModule != null) + { + terrModule.PushTerrain(RemoteClient); + } } #endregion diff --git a/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs b/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs index c873e40c01..45c33484d7 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 COMMUNIATION SERVICE]"; protected RegionInfo m_regionInfo; protected Scene m_scene; @@ -84,15 +85,12 @@ namespace OpenSim.Region.Framework.Scenes if (neighbourService != null) neighbour = neighbourService.HelloNeighbour(regionhandle, region); else - m_log.DebugFormat( - "[SCENE COMMUNICATION SERVICE]: No neighbour service provided for region {0} to inform neigbhours of status", - m_scene.Name); + m_log.DebugFormat( "{0} neighbour service provided for region {0} to inform neigbhours of status", LogHeader, m_scene.Name); if (neighbour != null) { - m_log.DebugFormat( - "[SCENE COMMUNICATION SERVICE]: Region {0} successfully informed neighbour {1} at {2}-{3} that it is up", - m_scene.Name, neighbour.RegionName, Util.WorldToRegionLoc(x), Util.WorldToRegionLoc(y)); + m_log.DebugFormat( "{0} Region {1} successfully informed neighbour {2} at {3}-{4} that it is up", + LogHeader, m_scene.Name, neighbour.RegionName, Util.WorldToRegionLoc(x), Util.WorldToRegionLoc(y)); m_scene.EventManager.TriggerOnRegionUp(neighbour); } @@ -111,9 +109,7 @@ namespace OpenSim.Region.Framework.Scenes List neighbours = m_scene.GridService.GetNeighbours(m_scene.RegionInfo.ScopeID, m_scene.RegionInfo.RegionID); - m_log.DebugFormat( - "[SCENE COMMUNICATION SERVICE]: Informing {0} neighbours that region {1} is up", - neighbours.Count, m_scene.Name); + m_log.DebugFormat("{0} Informing {1} neighbours that region {2} is up", LogHeader, neighbours.Count, m_scene.Name); foreach (GridRegion n in neighbours) { diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 37353c1e3b..063ac73251 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -873,7 +873,8 @@ namespace OpenSim.Region.Framework.Scenes m_sendCoarseLocationsMethod = SendCoarseLocationsDefault; Animator = new ScenePresenceAnimator(this); PresenceType = type; - DrawDistance = world.DefaultDrawDistance; + // DrawDistance = world.DefaultDrawDistance; + DrawDistance = Constants.RegionSize; RegionHandle = world.RegionInfo.RegionHandle; ControllingClient = client; Firstname = ControllingClient.FirstName; @@ -1918,8 +1919,8 @@ namespace OpenSim.Region.Framework.Scenes // When we get to the point of re-computing neighbors everytime this // changes, then start using the agent's drawdistance rather than the // region's draw distance. - // DrawDistance = agentData.Far; - DrawDistance = Scene.DefaultDrawDistance; + DrawDistance = agentData.Far; + // DrawDistance = Scene.DefaultDrawDistance; m_mouseLook = (flags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0; m_leftButtonDown = (flags & AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN) != 0; @@ -2277,8 +2278,8 @@ namespace OpenSim.Region.Framework.Scenes // When we get to the point of re-computing neighbors everytime this // changes, then start using the agent's drawdistance rather than the // region's draw distance. - // DrawDistance = agentData.Far; - DrawDistance = Scene.DefaultDrawDistance; + DrawDistance = agentData.Far; + // DrawDistance = Scene.DefaultDrawDistance; // Check if Client has camera in 'follow cam' or 'build' mode. Vector3 camdif = (Vector3.One * Rotation - Vector3.One * CameraRotation); @@ -3824,8 +3825,8 @@ namespace OpenSim.Region.Framework.Scenes // When we get to the point of re-computing neighbors everytime this // changes, then start using the agent's drawdistance rather than the // region's draw distance. - // DrawDistance = cAgentData.Far; - DrawDistance = Scene.DefaultDrawDistance; + DrawDistance = cAgentData.Far; + // DrawDistance = Scene.DefaultDrawDistance; if (cAgentData.Position != marker) // UGH!! m_pos = cAgentData.Position + offset; @@ -3935,8 +3936,8 @@ namespace OpenSim.Region.Framework.Scenes // When we get to the point of re-computing neighbors everytime this // changes, then start using the agent's drawdistance rather than the // region's draw distance. - // DrawDistance = cAgent.Far; - DrawDistance = Scene.DefaultDrawDistance; + DrawDistance = cAgent.Far; + // DrawDistance = Scene.DefaultDrawDistance; if ((cAgent.Throttles != null) && cAgent.Throttles.Length > 0) ControllingClient.SetChildAgentThrottle(cAgent.Throttles); diff --git a/OpenSim/Services/GridService/GridService.cs b/OpenSim/Services/GridService/GridService.cs index 81985929e4..36957a74c0 100644 --- a/OpenSim/Services/GridService/GridService.cs +++ b/OpenSim/Services/GridService/GridService.cs @@ -46,6 +46,7 @@ namespace OpenSim.Services.GridService private static readonly ILog m_log = LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); + private string LogHeader = "[GRID SERVICE]"; private bool m_DeleteOnUnregister = true; private static GridService m_RootInstance = null; @@ -328,7 +329,11 @@ namespace OpenSim.Services.GridService } } -// m_log.DebugFormat("[GRID SERVICE]: region {0} has {1} neighbours", region.RegionName, rinfos.Count); + // string rNames = ""; + // foreach (GridRegion gr in rinfos) + // rNames += gr.RegionName + ","; + // m_log.DebugFormat("{0} region {1} has {2} neighbours ({3})", + // LogHeader, region.RegionName, rinfos.Count, rNames); } else { @@ -657,7 +662,7 @@ namespace OpenSim.Services.GridService return; } - RegionData region = m_Database.Get((int)Util.RegionToWorldLoc(x), (int)Util.RegionToWorldLoc(y), UUID.Zero); + RegionData region = m_Database.Get((int)Util.RegionToWorldLoc(x), (int)Util.RegionToWorldLoc(y), UUID.Zero); if (region == null) { MainConsole.Instance.OutputFormat("No region found at {0},{1}", x, y); diff --git a/OpenSim/Services/MapImageService/MapImageService.cs b/OpenSim/Services/MapImageService/MapImageService.cs index 31e147b3bf..0e425f8cd4 100644 --- a/OpenSim/Services/MapImageService/MapImageService.cs +++ b/OpenSim/Services/MapImageService/MapImageService.cs @@ -36,6 +36,7 @@ using System.Drawing.Imaging; using System.IO; using System.Net; using System.Reflection; +using System.Threading; using Nini.Config; using log4net; @@ -53,6 +54,7 @@ namespace OpenSim.Services.MapImageService private static readonly ILog m_log = LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); + private string LogHeader = "[MAP IMAGE SERVICE]"; private const int ZOOM_LEVELS = 8; private const int IMAGE_WIDTH = 256; @@ -114,7 +116,7 @@ namespace OpenSim.Services.MapImageService } } - return UpdateMultiResolutionFiles(x, y, out reason); + return UpdateMultiResolutionFilesAsync(x, y, out reason); } public bool RemoveMapTile(int x, int y, out string reason) @@ -136,33 +138,81 @@ namespace OpenSim.Services.MapImageService } } - return UpdateMultiResolutionFiles(x, y, out reason); + return UpdateMultiResolutionFilesAsync(x, y, out reason); } - private bool UpdateMultiResolutionFiles(int x, int y, out string reason) + // When large varregions start up, they can send piles of new map tiles. This causes + // this multi-resolution routine to be called a zillion times an causes much CPU + // time to be spent creating multi-resolution tiles that will be replaced when + // the next maptile arrives. + private class mapToMultiRez + { + public int xx; + public int yy; + public mapToMultiRez(int pX, int pY) + { + xx = pX; + yy = pY; + } + }; + private Queue multiRezToBuild = new Queue(); + private bool UpdateMultiResolutionFilesAsync(int x, int y, out string reason) { reason = String.Empty; - lock (m_Sync) + lock (multiRezToBuild) { - // Stitch seven more aggregate tiles together - for (uint zoomLevel = 2; zoomLevel <= ZOOM_LEVELS; zoomLevel++) - { - // Calculate the width (in full resolution tiles) and bottom-left - // corner of the current zoom level - int width = (int)Math.Pow(2, (double)(zoomLevel - 1)); - int x1 = x - (x % width); - int y1 = y - (y % width); + // m_log.DebugFormat("{0} UpdateMultiResolutionFilesAsync: scheduling update for <{1},{2}>", LogHeader, x, y); + multiRezToBuild.Enqueue(new mapToMultiRez(x, y)); + if (multiRezToBuild.Count == 1) + Util.FireAndForget(DoUpdateMultiResolutionFilesAsync); + } - if (!CreateTile(zoomLevel, x1, y1)) + return true; + } + + private void DoUpdateMultiResolutionFilesAsync(object o) + { + // This sleep causes the FireAndForget thread to be different than the invocation thread. + // It also allows other tiles to be uploaded so the multi-rez images are more likely + // to be correct. + Thread.Sleep(1 * 1000); + + while (multiRezToBuild.Count > 0) + { + mapToMultiRez toMultiRez = null; + lock (multiRezToBuild) + { + if (multiRezToBuild.Count > 0) + toMultiRez = multiRezToBuild.Dequeue(); + } + if (toMultiRez != null) + { + int x = toMultiRez.xx; + int y = toMultiRez.yy; + // m_log.DebugFormat("{0} DoUpdateMultiResolutionFilesAsync: doing build for <{1},{2}>", LogHeader, x, y); + + // Stitch seven more aggregate tiles together + for (uint zoomLevel = 2; zoomLevel <= ZOOM_LEVELS; zoomLevel++) { - m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to create tile for {0},{1} at zoom level {1}", x, y, zoomLevel); - reason = string.Format("Map tile at zoom level {0} failed", zoomLevel); - return false; + // Calculate the width (in full resolution tiles) and bottom-left + // corner of the current zoom level + int width = (int)Math.Pow(2, (double)(zoomLevel - 1)); + int x1 = x - (x % width); + int y1 = y - (y % width); + + lock (m_Sync) // must lock the reading and writing of the maptile files + { + if (!CreateTile(zoomLevel, x1, y1)) + { + m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to create tile for {0},{1} at zoom level {1}", x, y, zoomLevel); + return; + } + } } } } - return true; + return; } public byte[] GetMapTile(string fileName, out string format)