aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs273
1 files changed, 164 insertions, 109 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
index 849fec3..4c0fba5 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
@@ -312,6 +312,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
312 private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; 312 private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f;
313 313
314 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 314 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
315 private static string LogHeader = "[LLCLIENTVIEW]";
315 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients 316 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients
316 317
317 /// <summary> 318 /// <summary>
@@ -690,12 +691,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP
690 /// <returns>true if the handler was added. This is currently always the case.</returns> 691 /// <returns>true if the handler was added. This is currently always the case.</returns>
691 public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync) 692 public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync)
692 { 693 {
694 return AddLocalPacketHandler(packetType, handler, doAsync, false);
695 }
696
697 /// <summary>
698 /// Add a handler for the given packet type.
699 /// </summary>
700 /// <param name="packetType"></param>
701 /// <param name="handler"></param>
702 /// <param name="doAsync">
703 /// If true, when the packet is received handle it on a different thread. Whether this is given direct to
704 /// a threadpool thread or placed in a queue depends on the inEngine parameter.
705 /// </param>
706 /// <param name="inEngine">
707 /// If async is false then this parameter is ignored.
708 /// If async is true and inEngine is false, then the packet is sent directly to a
709 /// threadpool thread.
710 /// If async is true and inEngine is true, then the packet is sent to the IncomingPacketAsyncHandlingEngine.
711 /// This may result in slower handling but reduces the risk of overloading the simulator when there are many
712 /// simultaneous async requests.
713 /// </param>
714 /// <returns>true if the handler was added. This is currently always the case.</returns>
715 public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync, bool inEngine)
716 {
693 bool result = false; 717 bool result = false;
694 lock (m_packetHandlers) 718 lock (m_packetHandlers)
695 { 719 {
696 if (!m_packetHandlers.ContainsKey(packetType)) 720 if (!m_packetHandlers.ContainsKey(packetType))
697 { 721 {
698 m_packetHandlers.Add(packetType, new PacketProcessor() { method = handler, Async = doAsync }); 722 m_packetHandlers.Add(
723 packetType, new PacketProcessor() { method = handler, Async = doAsync });
699 result = true; 724 result = true;
700 } 725 }
701 } 726 }
@@ -1174,11 +1199,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1174 1199
1175 /// <summary> 1200 /// <summary>
1176 /// Send the region heightmap to the client 1201 /// Send the region heightmap to the client
1202 /// This method is only called when not doing intellegent terrain patch sending and
1203 /// is only called when the scene presence is initially created and sends all of the
1204 /// region's patches to the client.
1177 /// </summary> 1205 /// </summary>
1178 /// <param name="map">heightmap</param> 1206 /// <param name="map">heightmap</param>
1179 public virtual void SendLayerData(float[] map) 1207 public virtual void SendLayerData(float[] map)
1180 { 1208 {
1181 Util.FireAndForget(DoSendLayerData, map); 1209 Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData());
1182 1210
1183 // Send it sync, and async. It's not that much data 1211 // Send it sync, and async. It's not that much data
1184 // and it improves user experience just so much! 1212 // and it improves user experience just so much!
@@ -1191,15 +1219,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1191 /// <param name="o"></param> 1219 /// <param name="o"></param>
1192 private void DoSendLayerData(object o) 1220 private void DoSendLayerData(object o)
1193 { 1221 {
1194 float[] map = LLHeightFieldMoronize((float[])o); 1222 TerrainData map = (TerrainData)o;
1195 1223
1196 try 1224 try
1197 { 1225 {
1226 // Send LayerData in typerwriter pattern
1198 for (int y = 0; y < 16; y++) 1227 for (int y = 0; y < 16; y++)
1199 { 1228 {
1200 for (int x = 0; x < 16; x+=4) 1229 for (int x = 0; x < 16; x++)
1201 { 1230 {
1202 SendLayerPacket(x, y, map); 1231 SendLayerData(x, y, map);
1203 } 1232 }
1204 } 1233 }
1205 } 1234 }
@@ -1209,77 +1238,95 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1209 } 1238 }
1210 } 1239 }
1211 1240
1212 /// <summary> 1241 // Legacy form of invocation that passes around a bare data array.
1213 /// Sends a set of four patches (x, x+1, ..., x+3) to the client 1242 // Just ignore what was passed and use the real terrain info that is part of the scene.
1214 /// </summary> 1243 // As a HORRIBLE kludge in an attempt to not change the definition of IClientAPI,
1215 /// <param name="map">heightmap</param> 1244 // there is a special form for specifying multiple terrain patches to send.
1216 /// <param name="px">X coordinate for patches 0..12</param> 1245 // The form is to pass 'px' as negative the number of patches to send and to
1217 /// <param name="py">Y coordinate for patches 0..15</param> 1246 // pass the float array as pairs of patch X and Y coordinates. So, passing 'px'
1218 private void SendLayerPacket(int x, int y, float[] map) 1247 // as -2 and map= [3, 5, 8, 4] would mean to send two terrain heightmap patches
1248 // and the patches to send are <3,5> and <8,4>.
1249 public void SendLayerData(int px, int py, float[] map)
1219 { 1250 {
1220 int[] patches = new int[4]; 1251 if (px >= 0)
1221 patches[0] = x + 0 + y * 16; 1252 {
1222 patches[1] = x + 1 + y * 16; 1253 SendLayerData(px, py, m_scene.Heightmap.GetTerrainData());
1223 patches[2] = x + 2 + y * 16; 1254 }
1224 patches[3] = x + 3 + y * 16; 1255 else
1256 {
1257 int numPatches = -px;
1258 int[] xPatches = new int[numPatches];
1259 int[] yPatches = new int[numPatches];
1260 for (int pp = 0; pp < numPatches; pp++)
1261 {
1262 xPatches[pp] = (int)map[pp * 2];
1263 yPatches[pp] = (int)map[pp * 2 + 1];
1264 }
1225 1265
1226 float[] heightmap = (map.Length == 65536) ? 1266 // DebugSendingPatches("SendLayerData", xPatches, yPatches);
1227 map :
1228 LLHeightFieldMoronize(map);
1229 1267
1230 try 1268 SendLayerData(xPatches, yPatches, m_scene.Heightmap.GetTerrainData());
1231 {
1232 Packet layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches);
1233 OutPacket(layerpack, ThrottleOutPacketType.Land);
1234 } 1269 }
1235 catch 1270 }
1271
1272 private void DebugSendingPatches(string pWho, int[] pX, int[] pY)
1273 {
1274 if (m_log.IsDebugEnabled)
1236 { 1275 {
1237 for (int px = x ; px < x + 4 ; px++) 1276 int numPatches = pX.Length;
1238 SendLayerData(px, y, map); 1277 string Xs = "";
1278 string Ys = "";
1279 for (int pp = 0; pp < numPatches; pp++)
1280 {
1281 Xs += String.Format("{0}", (int)pX[pp]) + ",";
1282 Ys += String.Format("{0}", (int)pY[pp]) + ",";
1283 }
1284 m_log.DebugFormat("{0} {1}: numPatches={2}, X={3}, Y={4}", LogHeader, pWho, numPatches, Xs, Ys);
1239 } 1285 }
1240 } 1286 }
1241 1287
1242 /// <summary> 1288 /// <summary>
1243 /// Sends a specified patch to a client 1289 /// Sends a terrain packet for the point specified.
1290 /// This is a legacy call that has refarbed the terrain into a flat map of floats.
1291 /// We just use the terrain from the region we know about.
1244 /// </summary> 1292 /// </summary>
1245 /// <param name="px">Patch coordinate (x) 0..15</param> 1293 /// <param name="px">Patch coordinate (x) 0..15</param>
1246 /// <param name="py">Patch coordinate (y) 0..15</param> 1294 /// <param name="py">Patch coordinate (y) 0..15</param>
1247 /// <param name="map">heightmap</param> 1295 /// <param name="map">heightmap</param>
1248 public void SendLayerData(int px, int py, float[] map) 1296 public void SendLayerData(int px, int py, TerrainData terrData)
1297 {
1298 int[] xPatches = new[] { px };
1299 int[] yPatches = new[] { py };
1300 SendLayerData(xPatches, yPatches, terrData);
1301 }
1302
1303 private void SendLayerData(int[] px, int[] py, TerrainData terrData)
1249 { 1304 {
1250 try 1305 try
1251 { 1306 {
1252 int[] patches = new int[] { py * 16 + px }; 1307 /* test code using the terrain compressor in libOpenMetaverse
1253 float[] heightmap = (map.Length == 65536) ? 1308 int[] patchInd = new int[1];
1254 map : 1309 patchInd[0] = px + (py * Constants.TerrainPatchSize);
1255 LLHeightFieldMoronize(map); 1310 LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(terrData.GetFloatsSerialized(), patchInd);
1256 1311 */
1257 LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches); 1312 // Many, many patches could have been passed to us. Since the patches will be compressed
1258 1313 // into variable sized blocks, we cannot pre-compute how many will fit into one
1259 // When a user edits the terrain, so much data is sent, the data queues up fast and presents a sub optimal editing experience. 1314 // packet. While some fancy packing algorithm is possible, 4 seems to always fit.
1260 // To alleviate this issue, when the user edits the terrain, we start skipping the queues until they're done editing the terrain. 1315 int PatchesAssumedToFit = 4;
1261 // We also make them unreliable because it's extremely likely that multiple packets will be sent for a terrain patch area 1316 for (int pcnt = 0; pcnt < px.Length; pcnt += PatchesAssumedToFit)
1262 // invalidating previous packets for that area. 1317 {
1263 1318 int remaining = Math.Min(px.Length - pcnt, PatchesAssumedToFit);
1264 // It's possible for an editing user to flood themselves with edited packets but the majority of use cases are such that only a 1319 int[] xPatches = new int[remaining];
1265 // tiny percentage of users will be editing the terrain. Other, non-editing users will see the edits much slower. 1320 int[] yPatches = new int[remaining];
1266 1321 for (int ii = 0; ii < remaining; ii++)
1267 // One last note on this topic, by the time users are going to be editing the terrain, it's extremely likely that the sim will 1322 {
1268 // have rezzed already and therefore this is not likely going to cause any additional issues with lost packets, objects or terrain 1323 xPatches[ii] = px[pcnt + ii];
1269 // patches. 1324 yPatches[ii] = py[pcnt + ii];
1325 }
1326 LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLandPacket(terrData, xPatches, yPatches);
1327 // DebugSendingPatches("SendLayerDataInternal", xPatches, yPatches);
1270 1328
1271 // m_justEditedTerrain is volatile, so test once and duplicate two affected statements so we only have one cache miss. 1329 SendTheLayerPacket(layerpack);
1272 if (m_justEditedTerrain)
1273 {
1274 layerpack.Header.Reliable = false;
1275 OutPacket(layerpack,
1276 ThrottleOutPacketType.Unknown );
1277 }
1278 else
1279 {
1280 layerpack.Header.Reliable = true;
1281 OutPacket(layerpack,
1282 ThrottleOutPacketType.Task);
1283 } 1330 }
1284 } 1331 }
1285 catch (Exception e) 1332 catch (Exception e)
@@ -1288,36 +1335,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1288 } 1335 }
1289 } 1336 }
1290 1337
1291 /// <summary> 1338 // When a user edits the terrain, so much data is sent, the data queues up fast and presents a
1292 /// Munges heightfield into the LLUDP backed in restricted heightfield. 1339 // sub optimal editing experience. To alleviate this issue, when the user edits the terrain, we
1293 /// </summary> 1340 // start skipping the queues until they're done editing the terrain. We also make them
1294 /// <param name="map">float array in the base; Constants.RegionSize</param> 1341 // unreliable because it's extremely likely that multiple packets will be sent for a terrain patch
1295 /// <returns>float array in the base 256</returns> 1342 // area invalidating previous packets for that area.
1296 internal float[] LLHeightFieldMoronize(float[] map) 1343
1344 // It's possible for an editing user to flood themselves with edited packets but the majority
1345 // of use cases are such that only a tiny percentage of users will be editing the terrain.
1346 // Other, non-editing users will see the edits much slower.
1347
1348 // One last note on this topic, by the time users are going to be editing the terrain, it's
1349 // extremely likely that the sim will have rezzed already and therefore this is not likely going
1350 // to cause any additional issues with lost packets, objects or terrain patches.
1351
1352 // m_justEditedTerrain is volatile, so test once and duplicate two affected statements so we
1353 // only have one cache miss.
1354 private void SendTheLayerPacket(LayerDataPacket layerpack)
1297 { 1355 {
1298 if (map.Length == 65536) 1356 if (m_justEditedTerrain)
1299 return map; 1357 {
1358 layerpack.Header.Reliable = false;
1359 OutPacket(layerpack, ThrottleOutPacketType.Unknown );
1360 }
1300 else 1361 else
1301 { 1362 {
1302 float[] returnmap = new float[65536]; 1363 layerpack.Header.Reliable = true;
1303 1364 OutPacket(layerpack, ThrottleOutPacketType.Land);
1304 if (map.Length < 65535)
1305 {
1306 // rebase the vector stride to 256
1307 for (int i = 0; i < Constants.RegionSize; i++)
1308 Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, (int)Constants.RegionSize);
1309 }
1310 else
1311 {
1312 for (int i = 0; i < 256; i++)
1313 Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, 256);
1314 }
1315
1316 //Array.Copy(map,0,returnmap,0,(map.Length < 65536)? map.Length : 65536);
1317
1318 return returnmap;
1319 } 1365 }
1320
1321 } 1366 }
1322 1367
1323 /// <summary> 1368 /// <summary>
@@ -1346,21 +1391,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1346 { 1391 {
1347 Vector2[] windSpeeds = (Vector2[])o; 1392 Vector2[] windSpeeds = (Vector2[])o;
1348 TerrainPatch[] patches = new TerrainPatch[2]; 1393 TerrainPatch[] patches = new TerrainPatch[2];
1349 patches[0] = new TerrainPatch(); 1394 patches[0] = new TerrainPatch { Data = new float[16 * 16] };
1350 patches[0].Data = new float[16 * 16]; 1395 patches[1] = new TerrainPatch { Data = new float[16 * 16] };
1351 patches[1] = new TerrainPatch();
1352 patches[1].Data = new float[16 * 16];
1353 1396
1354 for (int y = 0; y < 16; y++) 1397 for (int x = 0; x < 16 * 16; x++)
1355 { 1398 {
1356 for (int x = 0; x < 16; x++) 1399 patches[0].Data[x] = windSpeeds[x].X;
1357 { 1400 patches[1].Data[x] = windSpeeds[x].Y;
1358 patches[0].Data[y * 16 + x] = windSpeeds[y * 16 + x].X;
1359 patches[1].Data[y * 16 + x] = windSpeeds[y * 16 + x].Y;
1360 }
1361 } 1401 }
1362 1402
1363 LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, TerrainPatch.LayerType.Wind); 1403 byte layerType = (byte)TerrainPatch.LayerType.Wind;
1404 if (m_scene.RegionInfo.RegionSizeX > Constants.RegionSize || m_scene.RegionInfo.RegionSizeY > Constants.RegionSize)
1405 layerType = (byte)TerrainPatch.LayerType.WindExtended;
1406
1407 // LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, (TerrainPatch.LayerType)layerType);
1408 LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLayerDataPacket(patches, layerType,
1409 (int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY);
1364 layerpack.Header.Zerocoded = true; 1410 layerpack.Header.Zerocoded = true;
1365 OutPacket(layerpack, ThrottleOutPacketType.Wind); 1411 OutPacket(layerpack, ThrottleOutPacketType.Wind);
1366 } 1412 }
@@ -1384,7 +1430,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1384 } 1430 }
1385 } 1431 }
1386 1432
1387 LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, TerrainPatch.LayerType.Cloud); 1433 byte layerType = (byte)TerrainPatch.LayerType.Cloud;
1434 if (m_scene.RegionInfo.RegionSizeX > Constants.RegionSize || m_scene.RegionInfo.RegionSizeY > Constants.RegionSize)
1435 layerType = (byte)TerrainPatch.LayerType.CloudExtended;
1436
1437 // LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, (TerrainPatch.LayerType)layerType);
1438 LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLayerDataPacket(patches, layerType,
1439 (int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY);
1388 layerpack.Header.Zerocoded = true; 1440 layerpack.Header.Zerocoded = true;
1389 OutPacket(layerpack, ThrottleOutPacketType.Cloud); 1441 OutPacket(layerpack, ThrottleOutPacketType.Cloud);
1390 } 1442 }
@@ -1489,10 +1541,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1489 mapReply.Data[i].Access = mapBlocks2[i].Access; 1541 mapReply.Data[i].Access = mapBlocks2[i].Access;
1490 mapReply.Data[i].Agents = mapBlocks2[i].Agents; 1542 mapReply.Data[i].Agents = mapBlocks2[i].Agents;
1491 1543
1492 // TODO: hookup varregion sim size here
1493 mapReply.Size[i] = new MapBlockReplyPacket.SizeBlock(); 1544 mapReply.Size[i] = new MapBlockReplyPacket.SizeBlock();
1494 mapReply.Size[i].SizeX = 256; 1545 mapReply.Size[i].SizeX = mapBlocks2[i].SizeX;
1495 mapReply.Size[i].SizeY = 256; 1546 mapReply.Size[i].SizeY = mapBlocks2[i].SizeY;
1496 } 1547 }
1497 OutPacket(mapReply, ThrottleOutPacketType.Land); 1548 OutPacket(mapReply, ThrottleOutPacketType.Land);
1498 } 1549 }
@@ -1657,15 +1708,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1657 1708
1658 public void SendKillObject(List<uint> localIDs) 1709 public void SendKillObject(List<uint> localIDs)
1659 { 1710 {
1660// foreach (uint id in localIDs)
1661// m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, id, regionHandle);
1662
1663 // remove pending entities
1664 lock (m_entityProps.SyncRoot)
1665 m_entityProps.Remove(localIDs);
1666 lock (m_entityUpdates.SyncRoot)
1667 m_entityUpdates.Remove(localIDs);
1668
1669 KillObjectPacket kill = (KillObjectPacket)PacketPool.Instance.GetPacket(PacketType.KillObject); 1711 KillObjectPacket kill = (KillObjectPacket)PacketPool.Instance.GetPacket(PacketType.KillObject);
1670 // TODO: don't create new blocks if recycling an old packet 1712 // TODO: don't create new blocks if recycling an old packet
1671 kill.ObjectData = new KillObjectPacket.ObjectDataBlock[localIDs.Count]; 1713 kill.ObjectData = new KillObjectPacket.ObjectDataBlock[localIDs.Count];
@@ -9087,6 +9129,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
9087 TeleportLocationRequest handlerTeleportLocationRequest = OnTeleportLocationRequest; 9129 TeleportLocationRequest handlerTeleportLocationRequest = OnTeleportLocationRequest;
9088 if (handlerTeleportLocationRequest != null) 9130 if (handlerTeleportLocationRequest != null)
9089 { 9131 {
9132 // Adjust teleport location to base of a larger region if requested to teleport to a sub-region
9133 uint locX, locY;
9134 Util.RegionHandleToWorldLoc(tpLocReq.Info.RegionHandle, out locX, out locY);
9135 if ((locX >= m_scene.RegionInfo.WorldLocX)
9136 && (locX < (m_scene.RegionInfo.WorldLocX + m_scene.RegionInfo.RegionSizeX))
9137 && (locY >= m_scene.RegionInfo.WorldLocY)
9138 && (locY < (m_scene.RegionInfo.WorldLocY + m_scene.RegionInfo.RegionSizeY)) )
9139 {
9140 tpLocReq.Info.RegionHandle = m_scene.RegionInfo.RegionHandle;
9141 tpLocReq.Info.Position.X += locX - m_scene.RegionInfo.WorldLocX;
9142 tpLocReq.Info.Position.Y += locY - m_scene.RegionInfo.WorldLocY;
9143 }
9144
9090 handlerTeleportLocationRequest(this, tpLocReq.Info.RegionHandle, tpLocReq.Info.Position, 9145 handlerTeleportLocationRequest(this, tpLocReq.Info.RegionHandle, tpLocReq.Info.Position,
9091 tpLocReq.Info.LookAt, 16); 9146 tpLocReq.Info.LookAt, 16);
9092 } 9147 }