diff options
author | Diva Canto | 2014-06-01 10:06:59 -0700 |
---|---|---|
committer | Diva Canto | 2014-06-01 10:06:59 -0700 |
commit | 0eaca7aafb0365fb21b6d1ce8e1590845a6b5125 (patch) | |
tree | 1120139228e2c934de544f3288b306699018bb50 /OpenSim/Region/CoreModules/World | |
parent | Fixed a few things pertaining to interfacing with the estate service. Specifi... (diff) | |
parent | Move the generation of the multi-resolution map tiles off the main (diff) | |
download | opensim-SC_OLD-0eaca7aafb0365fb21b6d1ce8e1590845a6b5125.zip opensim-SC_OLD-0eaca7aafb0365fb21b6d1ce8e1590845a6b5125.tar.gz opensim-SC_OLD-0eaca7aafb0365fb21b6d1ce8e1590845a6b5125.tar.bz2 opensim-SC_OLD-0eaca7aafb0365fb21b6d1ce8e1590845a6b5125.tar.xz |
Merge branch 'master' of ssh://opensimulator.org/var/git/opensim
Diffstat (limited to 'OpenSim/Region/CoreModules/World')
-rw-r--r-- | OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs | 295 |
1 files changed, 262 insertions, 33 deletions
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs index 6e1e8d6..c22c5b6 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 | |||
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 | |||
178 | // The flags of which terrain patches to send for each of the ScenePresence's | ||
179 | private Dictionary<UUID, PatchUpdates> m_perClientPatchUpdates = new Dictionary<UUID, PatchUpdates>(); | ||
180 | |||
102 | /// <summary> | 181 | /// <summary> |
103 | /// Human readable list of terrain file extensions that are supported. | 182 | /// Human readable list of terrain file extensions that are supported. |
104 | /// </summary> | 183 | /// </summary> |
@@ -127,7 +206,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
127 | { | 206 | { |
128 | IConfig terrainConfig = config.Configs["Terrain"]; | 207 | IConfig terrainConfig = config.Configs["Terrain"]; |
129 | if (terrainConfig != null) | 208 | if (terrainConfig != null) |
209 | { | ||
130 | m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain); | 210 | m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain); |
211 | m_sendTerrainUpdatesByViewDistance = terrainConfig.GetBoolean("SendTerrainUpdatesByViewDistance", m_sendTerrainUpdatesByViewDistance); | ||
212 | } | ||
131 | } | 213 | } |
132 | 214 | ||
133 | public void AddRegion(Scene scene) | 215 | public void AddRegion(Scene scene) |
@@ -422,9 +504,46 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
422 | } | 504 | } |
423 | } | 505 | } |
424 | 506 | ||
507 | // Someone diddled terrain outside the normal code paths. Set the taintedness for all clients. | ||
508 | // ITerrainModule.TaintTerrain() | ||
425 | public void TaintTerrain () | 509 | public void TaintTerrain () |
426 | { | 510 | { |
427 | m_channel.GetTerrainData().TaintAllTerrain(); | 511 | lock (m_perClientPatchUpdates) |
512 | { | ||
513 | // Set the flags for all clients so the tainted patches will be sent out | ||
514 | foreach (PatchUpdates pups in m_perClientPatchUpdates.Values) | ||
515 | { | ||
516 | pups.SetAll(m_scene.Heightmap.GetTerrainData()); | ||
517 | } | ||
518 | } | ||
519 | } | ||
520 | |||
521 | // ITerrainModule.PushTerrain() | ||
522 | public void PushTerrain(IClientAPI pClient) | ||
523 | { | ||
524 | if (m_sendTerrainUpdatesByViewDistance) | ||
525 | { | ||
526 | ScenePresence presence = m_scene.GetScenePresence(pClient.AgentId); | ||
527 | if (presence != null) | ||
528 | { | ||
529 | lock (m_perClientPatchUpdates) | ||
530 | { | ||
531 | PatchUpdates pups; | ||
532 | if (!m_perClientPatchUpdates.TryGetValue(pClient.AgentId, out pups)) | ||
533 | { | ||
534 | // There is a ScenePresence without a send patch map. Create one. | ||
535 | pups = new PatchUpdates(m_scene.Heightmap.GetTerrainData(), presence); | ||
536 | m_perClientPatchUpdates.Add(presence.UUID, pups); | ||
537 | } | ||
538 | pups.SetAll(true); | ||
539 | } | ||
540 | } | ||
541 | } | ||
542 | else | ||
543 | { | ||
544 | // The traditional way is to call into the protocol stack to send them all. | ||
545 | pClient.SendLayerData(new float[10]); | ||
546 | } | ||
428 | } | 547 | } |
429 | 548 | ||
430 | #region Plugin Loading Methods | 549 | #region Plugin Loading Methods |
@@ -676,6 +795,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
676 | } | 795 | } |
677 | } | 796 | } |
678 | } | 797 | } |
798 | |||
799 | // This event also causes changes to be sent to the clients | ||
800 | CheckSendingPatchesToClients(); | ||
801 | |||
802 | // If things changes, generate some events | ||
679 | if (shouldTaint) | 803 | if (shouldTaint) |
680 | { | 804 | { |
681 | m_scene.EventManager.TriggerTerrainTainted(); | 805 | m_scene.EventManager.TriggerTerrainTainted(); |
@@ -749,31 +873,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
749 | presence.ControllingClient.OnLandUndo -= client_OnLandUndo; | 873 | presence.ControllingClient.OnLandUndo -= client_OnLandUndo; |
750 | presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain; | 874 | presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain; |
751 | } | 875 | } |
876 | if (m_perClientPatchUpdates.ContainsKey(client)) | ||
877 | { | ||
878 | m_perClientPatchUpdates.Remove(client); | ||
879 | } | ||
752 | } | 880 | } |
753 | 881 | ||
754 | /// <summary> | 882 | /// <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 | 883 | /// Scan over changes in the terrain and limit height changes. This enforces the |
778 | /// non-estate owner limits on rate of terrain editting. | 884 | /// non-estate owner limits on rate of terrain editting. |
779 | /// Returns 'true' if any heights were limited. | 885 | /// Returns 'true' if any heights were limited. |
@@ -857,17 +963,140 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
857 | /// <param name="y">The patch corner to send</param> | 963 | /// <param name="y">The patch corner to send</param> |
858 | private void SendToClients(TerrainData terrData, int x, int y) | 964 | private void SendToClients(TerrainData terrData, int x, int y) |
859 | { | 965 | { |
860 | // We know the actual terrain data passed is ignored. This kludge saves changing IClientAPI. | 966 | if (m_sendTerrainUpdatesByViewDistance) |
861 | //float[] heightMap = terrData.GetFloatsSerialized(); | 967 | { |
862 | float[] heightMap = new float[10]; | 968 | // Add that this patch needs to be sent to the accounting for each client. |
863 | m_scene.ForEachClient( | 969 | lock (m_perClientPatchUpdates) |
864 | delegate(IClientAPI controller) | 970 | { |
971 | m_scene.ForEachScenePresence(presence => | ||
972 | { | ||
973 | PatchUpdates thisClientUpdates; | ||
974 | if (!m_perClientPatchUpdates.TryGetValue(presence.UUID, out thisClientUpdates)) | ||
975 | { | ||
976 | // There is a ScenePresence without a send patch map. Create one. | ||
977 | thisClientUpdates = new PatchUpdates(terrData, presence); | ||
978 | m_perClientPatchUpdates.Add(presence.UUID, thisClientUpdates); | ||
979 | } | ||
980 | thisClientUpdates.SetByXY(x, y, true); | ||
981 | } | ||
982 | ); | ||
983 | } | ||
984 | } | ||
985 | else | ||
986 | { | ||
987 | // Legacy update sending where the update is sent out as soon as noticed | ||
988 | // We know the actual terrain data passed is ignored. This kludge saves changing IClientAPI. | ||
989 | //float[] heightMap = terrData.GetFloatsSerialized(); | ||
990 | float[] heightMap = new float[10]; | ||
991 | m_scene.ForEachClient( | ||
992 | delegate(IClientAPI controller) | ||
865 | { | 993 | { |
866 | controller.SendLayerData( x / Constants.TerrainPatchSize, | 994 | controller.SendLayerData(x / Constants.TerrainPatchSize, |
867 | y / Constants.TerrainPatchSize, | 995 | y / Constants.TerrainPatchSize, |
868 | heightMap); | 996 | heightMap); |
869 | } | 997 | } |
870 | ); | 998 | ); |
999 | } | ||
1000 | } | ||
1001 | |||
1002 | private class PatchesToSend : IComparable<PatchesToSend> | ||
1003 | { | ||
1004 | public int PatchX; | ||
1005 | public int PatchY; | ||
1006 | public float Dist; | ||
1007 | public PatchesToSend(int pX, int pY, float pDist) | ||
1008 | { | ||
1009 | PatchX = pX; | ||
1010 | PatchY = pY; | ||
1011 | Dist = pDist; | ||
1012 | } | ||
1013 | public int CompareTo(PatchesToSend other) | ||
1014 | { | ||
1015 | return Dist.CompareTo(other.Dist); | ||
1016 | } | ||
1017 | } | ||
1018 | |||
1019 | // Called each frame time to see if there are any patches to send to any of the | ||
1020 | // ScenePresences. | ||
1021 | // Loop through all the per-client info and send any patches necessary. | ||
1022 | private void CheckSendingPatchesToClients() | ||
1023 | { | ||
1024 | lock (m_perClientPatchUpdates) | ||
1025 | { | ||
1026 | foreach (PatchUpdates pups in m_perClientPatchUpdates.Values) | ||
1027 | { | ||
1028 | if (pups.HasUpdates()) | ||
1029 | { | ||
1030 | // There is something that could be sent to this client. | ||
1031 | List<PatchesToSend> toSend = GetModifiedPatchesInViewDistance(pups); | ||
1032 | if (toSend.Count > 0) | ||
1033 | { | ||
1034 | // m_log.DebugFormat("{0} CheckSendingPatchesToClient: sending {1} patches to {2} in region {3}", | ||
1035 | // LogHeader, toSend.Count, pups.Presence.Name, m_scene.RegionInfo.RegionName); | ||
1036 | // Sort the patches to send by the distance from the presence | ||
1037 | toSend.Sort(); | ||
1038 | foreach (PatchesToSend pts in toSend) | ||
1039 | { | ||
1040 | // TODO: one can send multiple patches in a packet. Do that. | ||
1041 | pups.Presence.ControllingClient.SendLayerData(pts.PatchX, pts.PatchY, null); | ||
1042 | // presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), null, TerrainPatch.LayerType.Land); | ||
1043 | } | ||
1044 | } | ||
1045 | } | ||
1046 | } | ||
1047 | } | ||
1048 | } | ||
1049 | |||
1050 | private List<PatchesToSend> GetModifiedPatchesInViewDistance(PatchUpdates pups) | ||
1051 | { | ||
1052 | List<PatchesToSend> ret = new List<PatchesToSend>(); | ||
1053 | |||
1054 | ScenePresence presence = pups.Presence; | ||
1055 | if (presence == null) | ||
1056 | return ret; | ||
1057 | |||
1058 | // Compute the area of patches within our draw distance | ||
1059 | int startX = (((int) (presence.AbsolutePosition.X - presence.DrawDistance))/Constants.TerrainPatchSize) - 2; | ||
1060 | startX = Math.Max(startX, 0); | ||
1061 | startX = Math.Min(startX, (int)m_scene.RegionInfo.RegionSizeX/Constants.TerrainPatchSize); | ||
1062 | int startY = (((int) (presence.AbsolutePosition.Y - presence.DrawDistance))/Constants.TerrainPatchSize) - 2; | ||
1063 | startY = Math.Max(startY, 0); | ||
1064 | startY = Math.Min(startY, (int)m_scene.RegionInfo.RegionSizeY/Constants.TerrainPatchSize); | ||
1065 | int endX = (((int) (presence.AbsolutePosition.X + presence.DrawDistance))/Constants.TerrainPatchSize) + 2; | ||
1066 | endX = Math.Max(endX, 0); | ||
1067 | endX = Math.Min(endX, (int)m_scene.RegionInfo.RegionSizeX/Constants.TerrainPatchSize); | ||
1068 | int endY = (((int) (presence.AbsolutePosition.Y + presence.DrawDistance))/Constants.TerrainPatchSize) + 2; | ||
1069 | endY = Math.Max(endY, 0); | ||
1070 | endY = Math.Min(endY, (int)m_scene.RegionInfo.RegionSizeY/Constants.TerrainPatchSize); | ||
1071 | // m_log.DebugFormat("{0} GetModifiedPatchesInViewDistance. rName={1}, ddist={2}, apos={3}, start=<{4},{5}>, end=<{6},{7}>", | ||
1072 | // LogHeader, m_scene.RegionInfo.RegionName, | ||
1073 | // presence.DrawDistance, presence.AbsolutePosition, | ||
1074 | // startX, startY, endX, endY); | ||
1075 | for (int x = startX; x < endX; x++) | ||
1076 | { | ||
1077 | for (int y = startY; y < endY; y++) | ||
1078 | { | ||
1079 | //Need to make sure we don't send the same ones over and over | ||
1080 | Vector3 presencePos = presence.AbsolutePosition; | ||
1081 | Vector3 patchPos = new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, presencePos.Z); | ||
1082 | if (pups.GetByPatch(x, y)) | ||
1083 | { | ||
1084 | //Check which has less distance, camera or avatar position, both have to be done. | ||
1085 | //Its not a radius, its a diameter and we add 50 so that it doesn't look like it cuts off | ||
1086 | if (Util.DistanceLessThan(presencePos, patchPos, presence.DrawDistance + 50) | ||
1087 | || Util.DistanceLessThan(presence.CameraPosition, patchPos, presence.DrawDistance + 50)) | ||
1088 | { | ||
1089 | //They can see it, send it to them | ||
1090 | pups.SetByPatch(x, y, false); | ||
1091 | float dist = Vector3.DistanceSquared(presencePos, patchPos); | ||
1092 | ret.Add(new PatchesToSend(x, y, dist)); | ||
1093 | //Wait and send them all at once | ||
1094 | // pups.client.SendLayerData(x, y, null); | ||
1095 | } | ||
1096 | } | ||
1097 | } | ||
1098 | } | ||
1099 | return ret; | ||
871 | } | 1100 | } |
872 | 1101 | ||
873 | private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action, | 1102 | private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action, |