diff options
Diffstat (limited to 'OpenSim/Region')
-rw-r--r-- | OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs | 292 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Interfaces/ITerrainModule.cs | 9 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/SceneBase.cs | 7 |
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 | */ |
27 | using System.IO; | ||
27 | 28 | ||
29 | using OpenSim.Framework; | ||
28 | 30 | ||
29 | using System.IO; | ||
30 | using OpenMetaverse; | 31 | using OpenMetaverse; |
31 | 32 | ||
32 | namespace OpenSim.Region.Framework.Interfaces | 33 | namespace 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 |