Change terrain update sending to be triggered by frame tick rather

than everytime terrain is changed.

The TerrainModule now hooks the frame event and, if terrain has changed,
sends terrain updates to the clients. This polling pattern replaces
the previous push on change pattern and will make it easier to do per
client throttling and per scene presence terrain update ordering.
This commit is contained in:
Robert Adams
2014-03-10 22:01:55 -07:00
parent 0237d9113d
commit 742f505440
3 changed files with 139 additions and 66 deletions

View File

@@ -149,8 +149,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain
m_scene.RegisterModuleInterface<ITerrainModule>(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();
@@ -189,8 +191,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<ITerrainModule>(this);
@@ -266,7 +270,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;
}
@@ -347,7 +350,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
}
}
CheckForTerrainUpdates();
m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
return;
}
@@ -418,7 +420,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
public void TaintTerrain ()
{
CheckForTerrainUpdates();
m_channel.GetTerrainData().TaintAllTerrain();
}
#region Plugin Loading Methods
@@ -646,8 +648,40 @@ namespace OpenSim.Region.CoreModules.World.Terrain
m_scene.RegionInfo.RegionName, filename, m_supportFileExtensionsForTileSave);
}
/// <summary>
/// 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.
/// </summary>
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;
}
}
}
if (shouldTaint)
{
m_scene.EventManager.TriggerTerrainTainted();
m_tainted = true;
}
}
/// <summary>
/// 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.
/// </summary>
private void EventManager_OnTerrainTick()
{
@@ -697,6 +731,22 @@ namespace OpenSim.Region.CoreModules.World.Terrain
client.OnUnackedTerrain += client_OnUnackedTerrain;
}
/// <summary>
/// Installs terrain brush hook to IClientAPI
/// </summary>
/// <param name="client"></param>
private void EventManager_OnClientClosed(UUID client, Scene scene)
{
ScenePresence presence = scene.GetScenePresence(client);
if (presence != null)
{
presence.ControllingClient.OnModifyTerrain -= client_OnModifyTerrain;
presence.ControllingClient.OnBakeTerrain -= client_OnBakeTerrain;
presence.ControllingClient.OnLandUndo -= client_OnLandUndo;
presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain;
}
}
/// <summary>
/// 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
@@ -717,36 +767,32 @@ namespace OpenSim.Region.CoreModules.World.Terrain
/// </summary>
private void CheckForTerrainUpdates(bool respectEstateSettings)
{
bool shouldTaint = false;
float[] terrHeights = m_channel.GetFloatsSerialised();
int x;
for (x = 0; x < m_channel.Width; x += Constants.TerrainPatchSize)
}
/// <summary>
/// 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.
/// </summary>
private bool EnforceEstateLimits()
{
TerrainData terrData = m_channel.GetTerrainData();
bool wasLimited = false;
for (int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize)
{
int y;
for (y = 0; y < m_channel.Height; y += Constants.TerrainPatchSize)
for (int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize)
{
if (m_channel.Tainted(x, y))
{
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.
if (respectEstateSettings && LimitChannelChanges(x, y))
{
// Terrain heights were modified. Refetch the terrain info.
terrHeights = m_channel.GetFloatsSerialised();
}
// m_log.DebugFormat("{0} Patch modified. Sending (x,y) = ({1},{2})", LogHeader, x, y);
SendToClients(terrHeights, x, y);
shouldTaint = true;
wasLimited |= LimitChannelChanges(terrData, x, y);
}
}
}
if (shouldTaint)
{
m_scene.EventManager.TriggerTerrainTainted();
m_tainted = true;
}
return wasLimited;
}
/// <summary>
@@ -754,11 +800,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
/// are all within the current estate limits
/// <returns>true if changes were limited, false otherwise</returns>
/// </summary>
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
@@ -766,19 +812,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;
}
}
@@ -806,11 +851,16 @@ namespace OpenSim.Region.CoreModules.World.Terrain
/// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param>
/// <param name="x">The patch corner to send</param>
/// <param name="y">The patch corner to send</param>
private void SendToClients(float[] heightMap, int x, int y)
private void SendToClients(TerrainData terrData, int x, int y)
{
float[] heightMap = terrData.GetFloatsSerialized();
m_scene.ForEachClient(
delegate(IClientAPI controller)
{ controller.SendLayerData( x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, heightMap); }
{
controller.SendLayerData( x / Constants.TerrainPatchSize,
y / Constants.TerrainPatchSize,
heightMap);
}
);
}
@@ -856,7 +906,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
@@ -897,7 +949,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain
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
@@ -948,7 +1002,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
private void InterfaceLoadFile(Object[] args)
{
LoadFromFile((string) args[0]);
CheckForTerrainUpdates();
}
private void InterfaceLoadTileFile(Object[] args)
@@ -958,7 +1011,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
(int) args[2],
(int) args[3],
(int) args[4]);
CheckForTerrainUpdates();
}
private void InterfaceSaveFile(Object[] args)
@@ -987,7 +1039,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)
@@ -1028,7 +1079,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
}
CheckForTerrainUpdates();
}
private void InterfaceRescaleTerrain(Object[] args)
@@ -1086,7 +1136,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
}
}
CheckForTerrainUpdates();
}
}
@@ -1097,7 +1146,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)
@@ -1106,7 +1154,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)
@@ -1115,7 +1162,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 InterfaceFillTerrain(Object[] args)
@@ -1125,7 +1171,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 InterfaceMinTerrain(Object[] args)
@@ -1138,7 +1183,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)
@@ -1151,7 +1195,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)
@@ -1214,7 +1257,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
if (m_plugineffects.ContainsKey(firstArg))
{
m_plugineffects[firstArg].RunEffect(m_channel);
CheckForTerrainUpdates();
}
else
{