aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorRobert Adams2014-03-16 21:31:38 -0700
committerRobert Adams2014-05-31 12:19:49 -0700
commitdb5a42ffac7e46d88ef434b939230290d78530a7 (patch)
tree423eced17c42e19d5468e0d79fc54ebad815624c
parentFix a bug where estate not found would result in a dummy estate record with e... (diff)
downloadopensim-SC-db5a42ffac7e46d88ef434b939230290d78530a7.zip
opensim-SC-db5a42ffac7e46d88ef434b939230290d78530a7.tar.gz
opensim-SC-db5a42ffac7e46d88ef434b939230290d78530a7.tar.bz2
opensim-SC-db5a42ffac7e46d88ef434b939230290d78530a7.tar.xz
varregion: send terrain patches from where the avatar outward if the parameter
[Terrain]SendTerrainUpdatesByViewDistance=true. This tracks which patches have been sent to each client and outputs the patches as the avatar moves.
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs292
-rw-r--r--OpenSim/Region/Framework/Interfaces/ITerrainModule.cs9
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneBase.cs7
3 files changed, 273 insertions, 35 deletions
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
index 6e1e8d6..0d7321d 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
@@ -96,9 +96,87 @@ namespace OpenSim.Region.CoreModules.World.Terrain
96 private Scene m_scene; 96 private Scene m_scene;
97 private volatile bool m_tainted; 97 private volatile bool m_tainted;
98 private readonly Stack<LandUndoState> m_undo = new Stack<LandUndoState>(5); 98 private readonly Stack<LandUndoState> m_undo = new Stack<LandUndoState>(5);
99 99
100 private String m_InitialTerrain = "pinhead-island"; 100 private String m_InitialTerrain = "pinhead-island";
101 101
102 // If true, send terrain patch updates to clients based on their view distance
103 private bool m_sendTerrainUpdatesByViewDistance = false;
104
105 // Class to keep the per client collection of terrain patches that must be sent.
106 // A patch is set to 'true' meaning it should be sent to the client. Once the
107 // patch packet is queued to the client, the bit for that patch is set to 'false'.
108 private class PatchUpdates
109 {
110 private bool[,] updated; // for each patch, whether it needs to be sent to this client
111 private int updateCount; // number of patches that need to be sent
112 public ScenePresence Presence; // a reference to the client to send to
113 public PatchUpdates(TerrainData terrData, ScenePresence pPresence)
114 {
115 updated = new bool[terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize];
116 updateCount = 0;
117 Presence = pPresence;
118 // Initially, send all patches to the client
119 SetAll(true);
120 }
121 // Returns 'true' if there are any patches marked for sending
122 public bool HasUpdates()
123 {
124 return (updateCount > 0);
125 }
126 public void SetByXY(int x, int y, bool state)
127 {
128 this.SetByPatch(x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, state);
129 }
130 public bool GetByPatch(int patchX, int patchY)
131 {
132 return updated[patchX, patchY];
133 }
134 public void SetByPatch(int patchX, int patchY, bool state)
135 {
136 bool prevState = updated[patchX, patchY];
137 if (!prevState && state)
138 updateCount++;
139 if (prevState && !state)
140 updateCount--;
141 updated[patchX, patchY] = state;
142 }
143 public void SetAll(bool state)
144 {
145 updateCount = 0;
146 for (int xx = 0; xx < updated.GetLength(0); xx++)
147 for (int yy = 0; yy < updated.GetLength(1); yy++)
148 updated[xx, yy] = state;
149 if (state)
150 updateCount = updated.GetLength(0) * updated.GetLength(1);
151 }
152 // Logically OR's the terrain data's patch taint map into this client's update map.
153 public void SetAll(TerrainData terrData)
154 {
155 if (updated.GetLength(0) != (terrData.SizeX / Constants.TerrainPatchSize)
156 || updated.GetLength(1) != (terrData.SizeY / Constants.TerrainPatchSize))
157 {
158 throw new Exception(
159 String.Format("{0} PatchUpdates.SetAll: patch array not same size as terrain. arr=<{1},{2}>, terr=<{3},{4}>",
160 LogHeader, updated.GetLength(0), updated.GetLength(1),
161 terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize)
162 );
163 }
164 for (int xx = 0; xx < terrData.SizeX; xx += Constants.TerrainPatchSize)
165 {
166 for (int yy = 0; yy < terrData.SizeY; yy += Constants.TerrainPatchSize)
167 {
168 // Only set tainted. The patch bit may be set if the patch was to be sent later.
169 if (terrData.IsTaintedAt(xx, yy, false))
170 {
171 this.SetByXY(xx, yy, true);
172 }
173 }
174 }
175 }
176 }
177 // The flags of which terrain patches to send for each of the ScenePresence's
178 private Dictionary<UUID, PatchUpdates> m_perClientPatchUpdates = new Dictionary<UUID, PatchUpdates>();
179
102 /// <summary> 180 /// <summary>
103 /// Human readable list of terrain file extensions that are supported. 181 /// Human readable list of terrain file extensions that are supported.
104 /// </summary> 182 /// </summary>
@@ -127,7 +205,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain
127 { 205 {
128 IConfig terrainConfig = config.Configs["Terrain"]; 206 IConfig terrainConfig = config.Configs["Terrain"];
129 if (terrainConfig != null) 207 if (terrainConfig != null)
208 {
130 m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain); 209 m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain);
210 m_sendTerrainUpdatesByViewDistance = terrainConfig.GetBoolean("SendTerrainUpdatesByViewDistance", m_sendTerrainUpdatesByViewDistance);
211 }
131 } 212 }
132 213
133 public void AddRegion(Scene scene) 214 public void AddRegion(Scene scene)
@@ -422,9 +503,46 @@ namespace OpenSim.Region.CoreModules.World.Terrain
422 } 503 }
423 } 504 }
424 505
506 // Someone diddled terrain outside the normal code paths. Set the taintedness for all clients.
507 // ITerrainModule.TaintTerrain()
425 public void TaintTerrain () 508 public void TaintTerrain ()
426 { 509 {
427 m_channel.GetTerrainData().TaintAllTerrain(); 510 lock (m_perClientPatchUpdates)
511 {
512 // Set the flags for all clients so the tainted patches will be sent out
513 foreach (PatchUpdates pups in m_perClientPatchUpdates.Values)
514 {
515 pups.SetAll(m_scene.Heightmap.GetTerrainData());
516 }
517 }
518 }
519
520 // ITerrainModule.PushTerrain()
521 public void PushTerrain(IClientAPI pClient)
522 {
523 if (m_sendTerrainUpdatesByViewDistance)
524 {
525 ScenePresence presence = m_scene.GetScenePresence(pClient.AgentId);
526 if (presence != null)
527 {
528 lock (m_perClientPatchUpdates)
529 {
530 PatchUpdates pups;
531 if (!m_perClientPatchUpdates.TryGetValue(pClient.AgentId, out pups))
532 {
533 // There is a ScenePresence without a send patch map. Create one.
534 pups = new PatchUpdates(m_scene.Heightmap.GetTerrainData(), presence);
535 m_perClientPatchUpdates.Add(presence.UUID, pups);
536 }
537 pups.SetAll(true);
538 }
539 }
540 }
541 else
542 {
543 // The traditional way is to call into the protocol stack to send them all.
544 pClient.SendLayerData(new float[10]);
545 }
428 } 546 }
429 547
430 #region Plugin Loading Methods 548 #region Plugin Loading Methods
@@ -676,6 +794,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
676 } 794 }
677 } 795 }
678 } 796 }
797
798 // This event also causes changes to be sent to the clients
799 CheckSendingPatchesToClients();
800
801 // If things changes, generate some events
679 if (shouldTaint) 802 if (shouldTaint)
680 { 803 {
681 m_scene.EventManager.TriggerTerrainTainted(); 804 m_scene.EventManager.TriggerTerrainTainted();
@@ -749,31 +872,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain
749 presence.ControllingClient.OnLandUndo -= client_OnLandUndo; 872 presence.ControllingClient.OnLandUndo -= client_OnLandUndo;
750 presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain; 873 presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain;
751 } 874 }
875 if (m_perClientPatchUpdates.ContainsKey(client))
876 {
877 m_perClientPatchUpdates.Remove(client);
878 }
752 } 879 }
753 880
754 /// <summary> 881 /// <summary>
755 /// Checks to see if the terrain has been modified since last check
756 /// but won't attempt to limit those changes to the limits specified in the estate settings
757 /// currently invoked by the command line operations in the region server only
758 /// </summary>
759 private void CheckForTerrainUpdates()
760 {
761 CheckForTerrainUpdates(false);
762 }
763
764 /// <summary>
765 /// Checks to see if the terrain has been modified since last check.
766 /// If it has been modified, every all the terrain patches are sent to the client.
767 /// If the call is asked to respect the estate settings for terrain_raise_limit and
768 /// terrain_lower_limit, it will clamp terrain updates between these values
769 /// currently invoked by client_OnModifyTerrain only and not the Commander interfaces
770 /// <param name="respectEstateSettings">should height map deltas be limited to the estate settings limits</param>
771 /// </summary>
772 private void CheckForTerrainUpdates(bool respectEstateSettings)
773 {
774 }
775
776 /// <summary>
777 /// Scan over changes in the terrain and limit height changes. This enforces the 882 /// Scan over changes in the terrain and limit height changes. This enforces the
778 /// non-estate owner limits on rate of terrain editting. 883 /// non-estate owner limits on rate of terrain editting.
779 /// Returns 'true' if any heights were limited. 884 /// Returns 'true' if any heights were limited.
@@ -857,17 +962,138 @@ namespace OpenSim.Region.CoreModules.World.Terrain
857 /// <param name="y">The patch corner to send</param> 962 /// <param name="y">The patch corner to send</param>
858 private void SendToClients(TerrainData terrData, int x, int y) 963 private void SendToClients(TerrainData terrData, int x, int y)
859 { 964 {
860 // We know the actual terrain data passed is ignored. This kludge saves changing IClientAPI. 965 if (m_sendTerrainUpdatesByViewDistance)
861 //float[] heightMap = terrData.GetFloatsSerialized(); 966 {
862 float[] heightMap = new float[10]; 967 // Add that this patch needs to be sent to the accounting for each client.
863 m_scene.ForEachClient( 968 lock (m_perClientPatchUpdates)
864 delegate(IClientAPI controller) 969 {
970 m_scene.ForEachScenePresence(presence =>
971 {
972 PatchUpdates thisClientUpdates;
973 if (!m_perClientPatchUpdates.TryGetValue(presence.UUID, out thisClientUpdates))
974 {
975 // There is a ScenePresence without a send patch map. Create one.
976 thisClientUpdates = new PatchUpdates(terrData, presence);
977 m_perClientPatchUpdates.Add(presence.UUID, thisClientUpdates);
978 }
979 thisClientUpdates.SetByXY(x, y, true);
980 }
981 );
982 }
983 }
984 else
985 {
986 // Legacy update sending where the update is sent out as soon as noticed
987 // We know the actual terrain data passed is ignored. This kludge saves changing IClientAPI.
988 //float[] heightMap = terrData.GetFloatsSerialized();
989 float[] heightMap = new float[10];
990 m_scene.ForEachClient(
991 delegate(IClientAPI controller)
992 {
993 controller.SendLayerData(x / Constants.TerrainPatchSize,
994 y / Constants.TerrainPatchSize,
995 heightMap);
996 }
997 );
998 }
999 }
1000
1001 private class PatchesToSend : IComparable<PatchesToSend>
1002 {
1003 public int PatchX;
1004 public int PatchY;
1005 public float Dist;
1006 public PatchesToSend(int pX, int pY, float pDist)
1007 {
1008 PatchX = pX;
1009 PatchY = pY;
1010 Dist = pDist;
1011 }
1012 public int CompareTo(PatchesToSend other)
1013 {
1014 return Dist.CompareTo(other.Dist);
1015 }
1016 }
1017
1018 // Called each frame time to see if there are any patches to send to any of the
1019 // ScenePresences.
1020 // Loop through all the per-client info and send any patches necessary.
1021 private void CheckSendingPatchesToClients()
1022 {
1023 lock (m_perClientPatchUpdates)
1024 {
1025 foreach (PatchUpdates pups in m_perClientPatchUpdates.Values)
1026 {
1027 if (pups.HasUpdates())
1028 {
1029 // There is something that could be sent to this client.
1030 List<PatchesToSend> toSend = GetModifiedPatchesInViewDistance(pups);
1031 if (toSend.Count > 0)
1032 {
1033 m_log.DebugFormat("{0} CheckSendingPatchesToClient: sending {1} patches to {2}",
1034 LogHeader, toSend.Count, pups.Presence.Name);
1035 // Sort the patches to send by the distance from the presence
1036 toSend.Sort();
1037 foreach (PatchesToSend pts in toSend)
1038 {
1039 // TODO: one can send multiple patches in a packet. Do that.
1040 pups.Presence.ControllingClient.SendLayerData(pts.PatchX, pts.PatchY, null);
1041 // presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), null, TerrainPatch.LayerType.Land);
1042 }
1043 }
1044 }
1045 }
1046 }
1047 }
1048
1049 private List<PatchesToSend> GetModifiedPatchesInViewDistance(PatchUpdates pups)
1050 {
1051 List<PatchesToSend> ret = new List<PatchesToSend>();
1052
1053 ScenePresence presence = pups.Presence;
1054 if (presence == null)
1055 return ret;
1056
1057 // See if there are patches within our view distance to send.
1058 int startX = (((int) (presence.AbsolutePosition.X - presence.DrawDistance))/Constants.TerrainPatchSize) - 2;
1059 startX = Math.Max(startX, 0);
1060 startX = Math.Min(startX, (int)m_scene.RegionInfo.RegionSizeX/Constants.TerrainPatchSize);
1061 int startY = (((int) (presence.AbsolutePosition.Y - presence.DrawDistance))/Constants.TerrainPatchSize) - 2;
1062 startY = Math.Max(startY, 0);
1063 startY = Math.Min(startY, (int)m_scene.RegionInfo.RegionSizeY/Constants.TerrainPatchSize);
1064 int endX = (((int) (presence.AbsolutePosition.X + presence.DrawDistance))/Constants.TerrainPatchSize) + 2;
1065 endX = Math.Max(endX, 0);
1066 endX = Math.Min(endX, (int)m_scene.RegionInfo.RegionSizeX/Constants.TerrainPatchSize);
1067 int endY = (((int) (presence.AbsolutePosition.Y + presence.DrawDistance))/Constants.TerrainPatchSize) + 2;
1068 endY = Math.Max(endY, 0);
1069 endY = Math.Min(endY, (int)m_scene.RegionInfo.RegionSizeY/Constants.TerrainPatchSize);
1070 // m_log.DebugFormat("{0} GetModifiedPatchesInViewDistance. start=<{1},{2}>, end=<{3},{4}>",
1071 // LogHeader, startX, startY, endX, endY);
1072 for (int x = startX; x < endX; x++)
1073 {
1074 for (int y = startY; y < endY; y++)
1075 {
1076 //Need to make sure we don't send the same ones over and over
1077 Vector3 presencePos = presence.AbsolutePosition;
1078 Vector3 patchPos = new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, presencePos.Z);
1079 if (pups.GetByPatch(x, y))
865 { 1080 {
866 controller.SendLayerData( x / Constants.TerrainPatchSize, 1081 //Check which has less distance, camera or avatar position, both have to be done.
867 y / Constants.TerrainPatchSize, 1082 //Its not a radius, its a diameter and we add 50 so that it doesn't look like it cuts off
868 heightMap); 1083 if (Util.DistanceLessThan(presencePos, patchPos, presence.DrawDistance + 50)
1084 || Util.DistanceLessThan(presence.CameraPosition, patchPos, presence.DrawDistance + 50))
1085 {
1086 //They can see it, send it to them
1087 pups.SetByPatch(x, y, false);
1088 float dist = Vector3.DistanceSquared(presencePos, patchPos);
1089 ret.Add(new PatchesToSend(x, y, dist));
1090 //Wait and send them all at once
1091 // pups.client.SendLayerData(x, y, null);
1092 }
869 } 1093 }
870 ); 1094 }
1095 }
1096 return ret;
871 } 1097 }
872 1098
873 private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action, 1099 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 a6f5d98..28f797a 100644
--- a/OpenSim/Region/Framework/Interfaces/ITerrainModule.cs
+++ b/OpenSim/Region/Framework/Interfaces/ITerrainModule.cs
@@ -24,9 +24,10 @@
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27using System.IO;
27 28
29using OpenSim.Framework;
28 30
29using System.IO;
30using OpenMetaverse; 31using OpenMetaverse;
31 32
32namespace OpenSim.Region.Framework.Interfaces 33namespace OpenSim.Region.Framework.Interfaces
@@ -44,6 +45,12 @@ namespace OpenSim.Region.Framework.Interfaces
44 void TaintTerrain(); 45 void TaintTerrain();
45 46
46 /// <summary> 47 /// <summary>
48 /// When a client initially connects, all the terrain must be pushed to the viewer.
49 /// This call causes all the terrain patches to be sent to the client.
50 /// </summary>
51 void PushTerrain(IClientAPI pClient);
52
53 /// <summary>
47 /// Load a terrain from a stream. 54 /// Load a terrain from a stream.
48 /// </summary> 55 /// </summary>
49 /// <param name="filename"> 56 /// <param name="filename">
diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs
index 4208669..0445268 100644
--- a/OpenSim/Region/Framework/Scenes/SceneBase.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs
@@ -213,7 +213,12 @@ namespace OpenSim.Region.Framework.Scenes
213 /// <param name="RemoteClient">Client to send to</param> 213 /// <param name="RemoteClient">Client to send to</param>
214 public virtual void SendLayerData(IClientAPI RemoteClient) 214 public virtual void SendLayerData(IClientAPI RemoteClient)
215 { 215 {
216 RemoteClient.SendLayerData(Heightmap.GetFloatsSerialised()); 216 // RemoteClient.SendLayerData(Heightmap.GetFloatsSerialised());
217 ITerrainModule terrModule = RequestModuleInterface<ITerrainModule>();
218 if (terrModule != null)
219 {
220 terrModule.PushTerrain(RemoteClient);
221 }
217 } 222 }
218 223
219 #endregion 224 #endregion