diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/UDP')
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | 1158 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs | 90 | ||||
-rwxr-xr-x[-rw-r--r--] | OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | 350 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs | 37 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs | 279 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs | 182 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs | 9 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs | 14 |
8 files changed, 1567 insertions, 552 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index b7d5a80..ac041f5 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | |||
@@ -1257,6 +1257,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1257 | SendLayerTopRight(x1 + 1, y1, x2, y2 - 1); | 1257 | SendLayerTopRight(x1 + 1, y1, x2, y2 - 1); |
1258 | } | 1258 | } |
1259 | 1259 | ||
1260 | static private readonly byte[] TerrainPacketHeader = new byte[] { | ||
1261 | Helpers.MSG_RELIABLE, // zero code is not as spec | ||
1262 | 0, 0, 0, 0, // sequence number | ||
1263 | 0, // extra | ||
1264 | 11, // ID (high frequency) | ||
1265 | }; | ||
1266 | |||
1267 | private const int END_OF_PATCHES = 97; | ||
1268 | private const int STRIDE = 264; | ||
1269 | |||
1260 | public void SendLayerData(int[] map) | 1270 | public void SendLayerData(int[] map) |
1261 | { | 1271 | { |
1262 | if(map == null) | 1272 | if(map == null) |
@@ -1264,9 +1274,75 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1264 | 1274 | ||
1265 | try | 1275 | try |
1266 | { | 1276 | { |
1267 | List<LayerDataPacket> packets = OpenSimTerrainCompressor.CreateLayerDataPackets(m_scene.Heightmap.GetTerrainData(), map); | 1277 | TerrainData terrData = m_scene.Heightmap.GetTerrainData(); |
1268 | foreach (LayerDataPacket pkt in packets) | 1278 | byte landPacketType = (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize) ? |
1269 | OutPacket(pkt, ThrottleOutPacketType.Land); | 1279 | (byte)TerrainPatch.LayerType.LandExtended : (byte)TerrainPatch.LayerType.Land; |
1280 | |||
1281 | int numberPatchs = map.Length / 2; | ||
1282 | |||
1283 | UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); | ||
1284 | byte[] data = buf.Data; | ||
1285 | |||
1286 | Buffer.BlockCopy(TerrainPacketHeader, 0, data, 0, 7); | ||
1287 | |||
1288 | data[7] = landPacketType; | ||
1289 | //data[8] and data[9] == datablock size to fill later | ||
1290 | |||
1291 | data[10] = 0; // BitPack needs this on reused packets | ||
1292 | |||
1293 | // start data | ||
1294 | BitPack bitpack = new BitPack(data, 10); | ||
1295 | bitpack.PackBits(STRIDE, 16); | ||
1296 | bitpack.PackBitsFromByte(16); | ||
1297 | bitpack.PackBitsFromByte(landPacketType); | ||
1298 | |||
1299 | int s; | ||
1300 | int datasize = 0; | ||
1301 | for (int i = 0; i < numberPatchs; i++) | ||
1302 | { | ||
1303 | s = 2 * i; | ||
1304 | OpenSimTerrainCompressor.CreatePatchFromTerrainData(bitpack, terrData, map[s], map[s + 1]); | ||
1305 | if (bitpack.BytePos > 950 && i != numberPatchs - 1) | ||
1306 | { | ||
1307 | //finish this packet | ||
1308 | bitpack.PackBitsFromByte(END_OF_PATCHES); | ||
1309 | |||
1310 | // fix the datablock lenght | ||
1311 | datasize = bitpack.BytePos - 9; | ||
1312 | data[8] = (byte)datasize; | ||
1313 | data[9] = (byte)(datasize >> 8); | ||
1314 | |||
1315 | buf.DataLength = bitpack.BytePos + 1; | ||
1316 | m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Land, null, false, false); | ||
1317 | |||
1318 | // start another | ||
1319 | buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); | ||
1320 | data = buf.Data; | ||
1321 | |||
1322 | Buffer.BlockCopy(TerrainPacketHeader, 0, data, 0, 7); | ||
1323 | |||
1324 | data[7] = landPacketType; | ||
1325 | //data[8] and data[9] == datablock size to fill later | ||
1326 | |||
1327 | data[10] = 0; // BitPack needs this | ||
1328 | // start data | ||
1329 | bitpack = new BitPack(data, 10); | ||
1330 | |||
1331 | bitpack.PackBits(STRIDE, 16); | ||
1332 | bitpack.PackBitsFromByte(16); | ||
1333 | bitpack.PackBitsFromByte(landPacketType); | ||
1334 | } | ||
1335 | } | ||
1336 | |||
1337 | bitpack.PackBitsFromByte(END_OF_PATCHES); | ||
1338 | |||
1339 | datasize = bitpack.BytePos - 9; | ||
1340 | data[8] = (byte)datasize; | ||
1341 | data[9] = (byte)(datasize >> 8); | ||
1342 | |||
1343 | buf.DataLength = bitpack.BytePos + 1; | ||
1344 | m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Land, null, false, false); | ||
1345 | |||
1270 | } | 1346 | } |
1271 | catch (Exception e) | 1347 | catch (Exception e) |
1272 | { | 1348 | { |
@@ -1677,8 +1753,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1677 | 1753 | ||
1678 | public void SendKillObject(List<uint> localIDs) | 1754 | public void SendKillObject(List<uint> localIDs) |
1679 | { | 1755 | { |
1680 | // foreach (uint id in localIDs) | 1756 | // foreach (uint id in localIDs) |
1681 | // m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, id, regionHandle); | 1757 | // m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, id, regionHandle); |
1682 | 1758 | ||
1683 | // remove pending entities to reduce looping chances. | 1759 | // remove pending entities to reduce looping chances. |
1684 | lock (m_entityProps.SyncRoot) | 1760 | lock (m_entityProps.SyncRoot) |
@@ -1702,10 +1778,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1702 | 1778 | ||
1703 | if(++nsent >= 200) | 1779 | if(++nsent >= 200) |
1704 | { | 1780 | { |
1705 | kill.Header.Reliable = true; | ||
1706 | kill.Header.Zerocoded = true; | ||
1707 | OutPacket(kill, ThrottleOutPacketType.Task); | 1781 | OutPacket(kill, ThrottleOutPacketType.Task); |
1708 | |||
1709 | perpacket = localIDs.Count - i - 1; | 1782 | perpacket = localIDs.Count - i - 1; |
1710 | if(perpacket == 0) | 1783 | if(perpacket == 0) |
1711 | break; | 1784 | break; |
@@ -1720,8 +1793,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1720 | 1793 | ||
1721 | if(nsent != 0) | 1794 | if(nsent != 0) |
1722 | { | 1795 | { |
1723 | kill.Header.Reliable = true; | ||
1724 | kill.Header.Zerocoded = true; | ||
1725 | OutPacket(kill, ThrottleOutPacketType.Task); | 1796 | OutPacket(kill, ThrottleOutPacketType.Task); |
1726 | } | 1797 | } |
1727 | } | 1798 | } |
@@ -3832,32 +3903,136 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3832 | OutPacket(avp, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority); | 3903 | OutPacket(avp, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority); |
3833 | } | 3904 | } |
3834 | 3905 | ||
3906 | /* | ||
3835 | public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) | 3907 | public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) |
3836 | { | 3908 | { |
3837 | // m_log.DebugFormat("[LLCLIENTVIEW]: Sending animations for {0} to {1}", sourceAgentId, Name); | 3909 | // m_log.DebugFormat("[LLCLIENTVIEW]: Sending animations for {0} to {1}", sourceAgentId, Name); |
3838 | 3910 | ||
3839 | AvatarAnimationPacket ani = (AvatarAnimationPacket)PacketPool.Instance.GetPacket(PacketType.AvatarAnimation); | 3911 | AvatarAnimationPacket ani = (AvatarAnimationPacket)PacketPool.Instance.GetPacket(PacketType.AvatarAnimation); |
3840 | // TODO: don't create new blocks if recycling an old packet | 3912 | // TODO: don't create new blocks if recycling an old packet |
3841 | ani.AnimationSourceList = new AvatarAnimationPacket.AnimationSourceListBlock[animations.Length]; | ||
3842 | ani.Sender = new AvatarAnimationPacket.SenderBlock(); | 3913 | ani.Sender = new AvatarAnimationPacket.SenderBlock(); |
3843 | ani.Sender.ID = sourceAgentId; | 3914 | ani.Sender.ID = sourceAgentId; |
3844 | ani.AnimationList = new AvatarAnimationPacket.AnimationListBlock[animations.Length]; | 3915 | ani.AnimationList = new AvatarAnimationPacket.AnimationListBlock[animations.Length]; |
3845 | ani.PhysicalAvatarEventList = new AvatarAnimationPacket.PhysicalAvatarEventListBlock[0]; | 3916 | ani.PhysicalAvatarEventList = new AvatarAnimationPacket.PhysicalAvatarEventListBlock[0]; |
3846 | 3917 | ||
3847 | for (int i = 0; i < animations.Length; ++i) | 3918 | //self animations |
3919 | if (sourceAgentId == AgentId) | ||
3848 | { | 3920 | { |
3849 | ani.AnimationList[i] = new AvatarAnimationPacket.AnimationListBlock(); | 3921 | List<int> withobjects = new List<int>(animations.Length); |
3850 | ani.AnimationList[i].AnimID = animations[i]; | 3922 | List<int> noobjects = new List<int>(animations.Length); |
3851 | ani.AnimationList[i].AnimSequenceID = seqs[i]; | 3923 | for(int i = 0; i < animations.Length; ++i) |
3924 | { | ||
3925 | if(objectIDs[i] == sourceAgentId || objectIDs[i] == UUID.Zero) | ||
3926 | noobjects.Add(i); | ||
3927 | else | ||
3928 | withobjects.Add(i); | ||
3929 | } | ||
3852 | 3930 | ||
3853 | ani.AnimationSourceList[i] = new AvatarAnimationPacket.AnimationSourceListBlock(); | 3931 | ani.AnimationSourceList = new AvatarAnimationPacket.AnimationSourceListBlock[withobjects.Count]; |
3854 | if (objectIDs[i].Equals(sourceAgentId)) | 3932 | int k = 0; |
3855 | ani.AnimationSourceList[i].ObjectID = UUID.Zero; | 3933 | foreach (int i in withobjects) |
3856 | else | 3934 | { |
3857 | ani.AnimationSourceList[i].ObjectID = objectIDs[i]; | 3935 | ani.AnimationList[k] = new AvatarAnimationPacket.AnimationListBlock(); |
3936 | ani.AnimationList[k].AnimID = animations[i]; | ||
3937 | ani.AnimationList[k].AnimSequenceID = seqs[i]; | ||
3938 | ani.AnimationSourceList[k] = new AvatarAnimationPacket.AnimationSourceListBlock(); | ||
3939 | ani.AnimationSourceList[k].ObjectID = objectIDs[i]; | ||
3940 | k++; | ||
3941 | } | ||
3942 | foreach (int i in noobjects) | ||
3943 | { | ||
3944 | ani.AnimationList[k] = new AvatarAnimationPacket.AnimationListBlock(); | ||
3945 | ani.AnimationList[k].AnimID = animations[i]; | ||
3946 | ani.AnimationList[k].AnimSequenceID = seqs[i]; | ||
3947 | k++; | ||
3948 | } | ||
3949 | } | ||
3950 | else | ||
3951 | { | ||
3952 | ani.AnimationSourceList = new AvatarAnimationPacket.AnimationSourceListBlock[0]; | ||
3953 | for (int i = 0; i < animations.Length; ++i) | ||
3954 | { | ||
3955 | ani.AnimationList[i] = new AvatarAnimationPacket.AnimationListBlock(); | ||
3956 | ani.AnimationList[i].AnimID = animations[i]; | ||
3957 | ani.AnimationList[i].AnimSequenceID = seqs[i]; | ||
3958 | } | ||
3858 | } | 3959 | } |
3960 | |||
3859 | OutPacket(ani, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority); | 3961 | OutPacket(ani, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority); |
3860 | } | 3962 | } |
3963 | */ | ||
3964 | |||
3965 | static private readonly byte[] AvatarAnimationHeader = new byte[] { | ||
3966 | Helpers.MSG_RELIABLE, | ||
3967 | 0, 0, 0, 0, // sequence number | ||
3968 | 0, // extra | ||
3969 | 20 // ID (high frequency) | ||
3970 | }; | ||
3971 | |||
3972 | public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) | ||
3973 | { | ||
3974 | // m_log.DebugFormat("[LLCLIENTVIEW]: Sending animations for {0} to {1}", sourceAgentId, Name); | ||
3975 | |||
3976 | UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); | ||
3977 | byte[] data = buf.Data; | ||
3978 | //setup header | ||
3979 | Buffer.BlockCopy(AvatarAnimationHeader, 0, data, 0, 7); | ||
3980 | //agent block | ||
3981 | sourceAgentId.ToBytes(data, 7); | ||
3982 | |||
3983 | // animations count | ||
3984 | data[23] = (byte)animations.Length; | ||
3985 | |||
3986 | int pos = 24; | ||
3987 | |||
3988 | //self animations | ||
3989 | if (sourceAgentId == AgentId) | ||
3990 | { | ||
3991 | List<int> withobjects = new List<int>(animations.Length); | ||
3992 | List<int> noobjects = new List<int>(animations.Length); | ||
3993 | for (int i = 0; i < animations.Length; ++i) | ||
3994 | { | ||
3995 | if (objectIDs[i] == sourceAgentId || objectIDs[i] == UUID.Zero) | ||
3996 | noobjects.Add(i); | ||
3997 | else | ||
3998 | withobjects.Add(i); | ||
3999 | } | ||
4000 | |||
4001 | // first the ones with corresponding objects | ||
4002 | foreach (int i in withobjects) | ||
4003 | { | ||
4004 | animations[i].ToBytes(data, pos); pos += 16; | ||
4005 | Utils.IntToBytesSafepos(seqs[i], data, pos); pos += 4; | ||
4006 | } | ||
4007 | // then the rest | ||
4008 | foreach (int i in noobjects) | ||
4009 | { | ||
4010 | animations[i].ToBytes(data, pos); pos += 16; | ||
4011 | Utils.IntToBytesSafepos(seqs[i], data, pos); pos += 4; | ||
4012 | } | ||
4013 | // object ids block | ||
4014 | data[pos++] = (byte)withobjects.Count; | ||
4015 | foreach (int i in withobjects) | ||
4016 | { | ||
4017 | objectIDs[i].ToBytes(data, pos); pos += 16; | ||
4018 | } | ||
4019 | } | ||
4020 | else | ||
4021 | { | ||
4022 | for(int i = 0; i < animations.Length; ++i) | ||
4023 | { | ||
4024 | animations[i].ToBytes(data, pos); pos += 16; | ||
4025 | Utils.IntToBytesSafepos(seqs[i], data, pos); pos += 4; | ||
4026 | } | ||
4027 | data[pos++] = 0; // no object ids | ||
4028 | } | ||
4029 | |||
4030 | data[pos++] = 0; // no physical avatar events | ||
4031 | |||
4032 | buf.DataLength = pos; | ||
4033 | m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority, | ||
4034 | null, false, false); | ||
4035 | } | ||
3861 | 4036 | ||
3862 | public void SendObjectAnimations(UUID[] animations, int[] seqs, UUID senderId) | 4037 | public void SendObjectAnimations(UUID[] animations, int[] seqs, UUID senderId) |
3863 | { | 4038 | { |
@@ -3889,36 +4064,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3889 | /// </summary> | 4064 | /// </summary> |
3890 | public void SendEntityFullUpdateImmediate(ISceneEntity ent) | 4065 | public void SendEntityFullUpdateImmediate(ISceneEntity ent) |
3891 | { | 4066 | { |
3892 | // m_log.DebugFormat( | 4067 | if (ent == null || (!(ent is ScenePresence) && !(ent is SceneObjectPart))) |
3893 | // "[LLCLIENTVIEW]: Sending immediate object update for avatar {0} {1} to {2} {3}", | ||
3894 | // avatar.Name, avatar.UUID, Name, AgentId); | ||
3895 | |||
3896 | if (ent == null) | ||
3897 | return; | 4068 | return; |
3898 | 4069 | ||
3899 | ObjectUpdatePacket objupdate = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); | 4070 | UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); |
3900 | objupdate.Header.Zerocoded = true; | 4071 | Buffer.BlockCopy(objectUpdateHeader, 0, buf.Data, 0, 7); |
3901 | 4072 | ||
3902 | objupdate.RegionData.TimeDilation = Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f); | 4073 | LLUDPZeroEncoder zc = new LLUDPZeroEncoder(buf.Data); |
3903 | objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; | 4074 | zc.Position = 7; |
3904 | 4075 | ||
3905 | if(ent is ScenePresence) | 4076 | zc.AddUInt64(m_scene.RegionInfo.RegionHandle); |
3906 | { | 4077 | zc.AddUInt16(Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f)); |
3907 | ScenePresence presence = ent as ScenePresence; | 4078 | |
3908 | objupdate.RegionData.RegionHandle = presence.RegionHandle; | 4079 | zc.AddByte(1); // block count |
3909 | objupdate.ObjectData[0] = CreateAvatarUpdateBlock(presence); | 4080 | |
3910 | } | 4081 | ThrottleOutPacketType ptype = ThrottleOutPacketType.Task; |
3911 | else if(ent is SceneObjectPart) | 4082 | if (ent is ScenePresence) |
3912 | { | 4083 | { |
3913 | SceneObjectPart part = ent as SceneObjectPart; | 4084 | CreateAvatarUpdateBlock(ent as ScenePresence, zc); |
3914 | objupdate.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; | 4085 | ptype |= ThrottleOutPacketType.HighPriority; |
3915 | objupdate.ObjectData[0] = CreatePrimUpdateBlock(part, (ScenePresence)SceneAgent); | ||
3916 | } | 4086 | } |
4087 | else | ||
4088 | CreatePrimUpdateBlock(ent as SceneObjectPart, (ScenePresence)SceneAgent, zc); | ||
3917 | 4089 | ||
3918 | OutPacket(objupdate, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority); | 4090 | buf.DataLength = zc.Finish(); |
3919 | 4091 | m_udpServer.SendUDPPacket(m_udpClient, buf, ptype , null, false, false); | |
3920 | // We need to record the avatar local id since the root prim of an attachment points to this. | ||
3921 | // m_attachmentsSent.Add(avatar.LocalId); | ||
3922 | } | 4092 | } |
3923 | 4093 | ||
3924 | public void SendEntityTerseUpdateImmediate(ISceneEntity ent) | 4094 | public void SendEntityTerseUpdateImmediate(ISceneEntity ent) |
@@ -4089,6 +4259,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4089 | ResendPrimUpdate(update); | 4259 | ResendPrimUpdate(update); |
4090 | } | 4260 | } |
4091 | 4261 | ||
4262 | static private readonly byte[] objectUpdateHeader = new byte[] { | ||
4263 | Helpers.MSG_RELIABLE | Helpers.MSG_ZEROCODED, | ||
4264 | 0, 0, 0, 0, // sequence number | ||
4265 | 0, // extra | ||
4266 | 12 // ID (high frequency) | ||
4267 | }; | ||
4268 | |||
4269 | static private readonly byte[] terseUpdateHeader = new byte[] { | ||
4270 | Helpers.MSG_RELIABLE | Helpers.MSG_ZEROCODED, // zero code is not as spec | ||
4271 | 0, 0, 0, 0, // sequence number | ||
4272 | 0, // extra | ||
4273 | 15 // ID (high frequency) | ||
4274 | }; | ||
4275 | |||
4092 | private void ProcessEntityUpdates(int maxUpdatesBytes) | 4276 | private void ProcessEntityUpdates(int maxUpdatesBytes) |
4093 | { | 4277 | { |
4094 | if (!IsActive) | 4278 | if (!IsActive) |
@@ -4098,14 +4282,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4098 | if (mysp == null) | 4282 | if (mysp == null) |
4099 | return; | 4283 | return; |
4100 | 4284 | ||
4101 | List<ObjectUpdatePacket.ObjectDataBlock> objectUpdateBlocks = null; | 4285 | // List<ObjectUpdateCompressedPacket.ObjectDataBlock> compressedUpdateBlocks = null; |
4102 | List<ObjectUpdateCompressedPacket.ObjectDataBlock> compressedUpdateBlocks = null; | ||
4103 | List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> terseUpdateBlocks = null; | ||
4104 | List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> terseAgentUpdateBlocks = null; | ||
4105 | List<EntityUpdate> objectUpdates = null; | 4286 | List<EntityUpdate> objectUpdates = null; |
4106 | List<EntityUpdate> compressedUpdates = null; | 4287 | // List<EntityUpdate> compressedUpdates = null; |
4107 | List<EntityUpdate> terseUpdates = null; | 4288 | List<EntityUpdate> terseUpdates = null; |
4108 | List<EntityUpdate> terseAgentUpdates = null; | ||
4109 | List<SceneObjectPart> ObjectAnimationUpdates = null; | 4289 | List<SceneObjectPart> ObjectAnimationUpdates = null; |
4110 | 4290 | ||
4111 | // Check to see if this is a flush | 4291 | // Check to see if this is a flush |
@@ -4120,10 +4300,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4120 | float cullingrange = 64.0f; | 4300 | float cullingrange = 64.0f; |
4121 | Vector3 mypos = Vector3.Zero; | 4301 | Vector3 mypos = Vector3.Zero; |
4122 | 4302 | ||
4123 | bool orderedDequeue = m_scene.UpdatePrioritizationScheme == UpdatePrioritizationSchemes.SimpleAngularDistance; | 4303 | //bool orderedDequeue = m_scene.UpdatePrioritizationScheme == UpdatePrioritizationSchemes.SimpleAngularDistance; |
4304 | bool orderedDequeue = false; // temporary off | ||
4124 | 4305 | ||
4125 | HashSet<SceneObjectGroup> GroupsNeedFullUpdate = new HashSet<SceneObjectGroup>(); | 4306 | HashSet<SceneObjectGroup> GroupsNeedFullUpdate = new HashSet<SceneObjectGroup>(); |
4126 | 4307 | ||
4308 | |||
4127 | if (doCulling) | 4309 | if (doCulling) |
4128 | { | 4310 | { |
4129 | cullingrange = mysp.DrawDistance + m_scene.ReprioritizationDistance + 16f; | 4311 | cullingrange = mysp.DrawDistance + m_scene.ReprioritizationDistance + 16f; |
@@ -4179,7 +4361,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4179 | } | 4361 | } |
4180 | 4362 | ||
4181 | if (grp.IsAttachment) | 4363 | if (grp.IsAttachment) |
4182 | { // Someone else's HUD, why are we getting these? | 4364 | { |
4365 | // animated attachments are nasty if not supported by viewer | ||
4366 | if(!m_SupportObjectAnimations && grp.RootPart.Shape.MeshFlagEntry) | ||
4367 | continue; | ||
4368 | |||
4369 | // Someone else's HUD, why are we getting these? | ||
4183 | if (grp.OwnerID != AgentId && grp.HasPrivateAttachmentPoint) | 4370 | if (grp.OwnerID != AgentId && grp.HasPrivateAttachmentPoint) |
4184 | continue; | 4371 | continue; |
4185 | 4372 | ||
@@ -4273,7 +4460,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4273 | if(ObjectAnimationUpdates == null) | 4460 | if(ObjectAnimationUpdates == null) |
4274 | ObjectAnimationUpdates = new List<SceneObjectPart>(); | 4461 | ObjectAnimationUpdates = new List<SceneObjectPart>(); |
4275 | ObjectAnimationUpdates.Add(sop); | 4462 | ObjectAnimationUpdates.Add(sop); |
4276 | maxUpdatesBytes -= 32 * sop.Animations.Count + 16; | 4463 | maxUpdatesBytes -= 20 * sop.Animations.Count + 24; |
4277 | } | 4464 | } |
4278 | } | 4465 | } |
4279 | } | 4466 | } |
@@ -4304,7 +4491,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4304 | PrimUpdateFlags.Velocity | | 4491 | PrimUpdateFlags.Velocity | |
4305 | PrimUpdateFlags.Acceleration | | 4492 | PrimUpdateFlags.Acceleration | |
4306 | PrimUpdateFlags.AngularVelocity | | 4493 | PrimUpdateFlags.AngularVelocity | |
4307 | PrimUpdateFlags.CollisionPlane | 4494 | PrimUpdateFlags.CollisionPlane | |
4495 | PrimUpdateFlags.Textures | ||
4308 | ); | 4496 | ); |
4309 | 4497 | ||
4310 | #endregion UpdateFlags to packet type conversion | 4498 | #endregion UpdateFlags to packet type conversion |
@@ -4326,48 +4514,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4326 | 4514 | ||
4327 | if ((updateFlags & canNotUseImprovedMask) == 0) | 4515 | if ((updateFlags & canNotUseImprovedMask) == 0) |
4328 | { | 4516 | { |
4329 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock ablock = | 4517 | if (terseUpdates == null) |
4330 | CreateImprovedTerseBlock(update.Entity); | ||
4331 | |||
4332 | if (update.Entity is ScenePresence) | ||
4333 | { | 4518 | { |
4334 | // ALL presence updates go into a special list | 4519 | terseUpdates = new List<EntityUpdate>(); |
4335 | if (terseAgentUpdateBlocks == null) | 4520 | maxUpdatesBytes -= 18; |
4336 | { | ||
4337 | terseAgentUpdateBlocks = new List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>(); | ||
4338 | terseAgentUpdates = new List<EntityUpdate>(); | ||
4339 | } | ||
4340 | terseAgentUpdateBlocks.Add(ablock); | ||
4341 | terseAgentUpdates.Add(update); | ||
4342 | } | 4521 | } |
4522 | terseUpdates.Add(update); | ||
4523 | |||
4524 | if (update.Entity is ScenePresence) | ||
4525 | maxUpdatesBytes -= 63; // no texture entry | ||
4343 | else | 4526 | else |
4344 | { | 4527 | { |
4345 | // Everything else goes here | 4528 | if ((updateFlags & PrimUpdateFlags.Textures) == 0) |
4346 | if (terseUpdateBlocks == null) | 4529 | maxUpdatesBytes -= 47; |
4347 | { | 4530 | else |
4348 | terseUpdateBlocks = new List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>(); | 4531 | maxUpdatesBytes -= 150; // aprox |
4349 | terseUpdates = new List<EntityUpdate>(); | ||
4350 | } | ||
4351 | terseUpdateBlocks.Add(ablock); | ||
4352 | terseUpdates.Add(update); | ||
4353 | } | 4532 | } |
4354 | maxUpdatesBytes -= ablock.Length; | ||
4355 | } | 4533 | } |
4356 | else | 4534 | else |
4357 | { | 4535 | { |
4358 | ObjectUpdatePacket.ObjectDataBlock ablock; | ||
4359 | if (update.Entity is ScenePresence) | 4536 | if (update.Entity is ScenePresence) |
4360 | ablock = CreateAvatarUpdateBlock((ScenePresence)update.Entity); | 4537 | maxUpdatesBytes -= 150; // crude estimation |
4361 | else | 4538 | else |
4362 | ablock = CreatePrimUpdateBlock((SceneObjectPart)update.Entity, mysp); | 4539 | maxUpdatesBytes -= 300; |
4363 | if(objectUpdateBlocks == null) | 4540 | |
4541 | if(objectUpdates == null) | ||
4364 | { | 4542 | { |
4365 | objectUpdateBlocks = new List<ObjectUpdatePacket.ObjectDataBlock>(); | ||
4366 | objectUpdates = new List<EntityUpdate>(); | 4543 | objectUpdates = new List<EntityUpdate>(); |
4544 | maxUpdatesBytes -= 18; | ||
4367 | } | 4545 | } |
4368 | objectUpdateBlocks.Add(ablock); | ||
4369 | objectUpdates.Add(update); | 4546 | objectUpdates.Add(update); |
4370 | maxUpdatesBytes -= ablock.Length; | ||
4371 | } | 4547 | } |
4372 | 4548 | ||
4373 | #endregion Block Construction | 4549 | #endregion Block Construction |
@@ -4382,29 +4558,87 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4382 | 4558 | ||
4383 | timeDilation = Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f); | 4559 | timeDilation = Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f); |
4384 | 4560 | ||
4385 | if (terseAgentUpdateBlocks != null) | 4561 | if(objectUpdates != null) |
4386 | { | 4562 | { |
4387 | ImprovedTerseObjectUpdatePacket packet | 4563 | int blocks = objectUpdates.Count; |
4388 | = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); | 4564 | List<EntityUpdate> tau = new List<EntityUpdate>(30); |
4389 | packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; | ||
4390 | packet.RegionData.TimeDilation = timeDilation; | ||
4391 | packet.ObjectData = terseAgentUpdateBlocks.ToArray(); | ||
4392 | terseAgentUpdateBlocks.Clear(); | ||
4393 | 4565 | ||
4394 | OutPacket(packet, ThrottleOutPacketType.Unknown, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseAgentUpdates, oPacket); }); | 4566 | UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); |
4395 | } | 4567 | Buffer.BlockCopy(objectUpdateHeader, 0, buf.Data, 0, 7); |
4396 | 4568 | ||
4397 | if (objectUpdateBlocks != null) | 4569 | LLUDPZeroEncoder zc = new LLUDPZeroEncoder(buf.Data); |
4398 | { | 4570 | zc.Position = 7; |
4399 | ObjectUpdatePacket packet = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); | 4571 | |
4400 | packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; | 4572 | zc.AddUInt64(m_scene.RegionInfo.RegionHandle); |
4401 | packet.RegionData.TimeDilation = timeDilation; | 4573 | zc.AddUInt16(Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f)); |
4402 | packet.ObjectData = objectUpdateBlocks.ToArray(); | 4574 | |
4403 | objectUpdateBlocks.Clear(); | 4575 | zc.AddByte(1); // tmp block count |
4576 | |||
4577 | int countposition = zc.Position - 1; | ||
4578 | |||
4579 | int lastpos = 0; | ||
4580 | int lastzc = 0; | ||
4581 | |||
4582 | int count = 0; | ||
4583 | foreach (EntityUpdate eu in objectUpdates) | ||
4584 | { | ||
4585 | lastpos = zc.Position; | ||
4586 | lastzc = zc.ZeroCount; | ||
4587 | if (eu.Entity is ScenePresence) | ||
4588 | CreateAvatarUpdateBlock((ScenePresence)eu.Entity, zc); | ||
4589 | else | ||
4590 | CreatePrimUpdateBlock((SceneObjectPart)eu.Entity, mysp, zc); | ||
4591 | if (zc.Position < LLUDPServer.MAXPAYLOAD) | ||
4592 | { | ||
4593 | tau.Add(eu); | ||
4594 | ++count; | ||
4595 | --blocks; | ||
4596 | } | ||
4597 | else if (blocks > 0) | ||
4598 | { | ||
4599 | // we need more packets | ||
4600 | UDPPacketBuffer newbuf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); | ||
4601 | Buffer.BlockCopy(buf.Data, 0, newbuf.Data, 0, countposition); // start is the same | ||
4404 | 4602 | ||
4405 | OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(objectUpdates, oPacket); }); | 4603 | buf.Data[countposition] = (byte)count; |
4604 | // get pending zeros at cut point | ||
4605 | if(lastzc > 0) | ||
4606 | { | ||
4607 | buf.Data[lastpos++] = 0; | ||
4608 | buf.Data[lastpos++] = (byte)lastzc; | ||
4609 | } | ||
4610 | buf.DataLength = lastpos; | ||
4611 | |||
4612 | m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, | ||
4613 | delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, false); | ||
4614 | |||
4615 | buf = newbuf; | ||
4616 | zc.Data = buf.Data; | ||
4617 | zc.ZeroCount = 0; | ||
4618 | zc.Position = countposition + 1; | ||
4619 | // im lazy now, just do last again | ||
4620 | if (eu.Entity is ScenePresence) | ||
4621 | CreateAvatarUpdateBlock((ScenePresence)eu.Entity, zc); | ||
4622 | else | ||
4623 | CreatePrimUpdateBlock((SceneObjectPart)eu.Entity, mysp, zc); | ||
4624 | |||
4625 | tau = new List<EntityUpdate>(30); | ||
4626 | tau.Add(eu); | ||
4627 | count = 1; | ||
4628 | --blocks; | ||
4629 | } | ||
4630 | } | ||
4631 | |||
4632 | if (count > 0) | ||
4633 | { | ||
4634 | buf.Data[countposition] = (byte)count; | ||
4635 | buf.DataLength = zc.Finish(); | ||
4636 | m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, | ||
4637 | delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, false); | ||
4638 | } | ||
4406 | } | 4639 | } |
4407 | 4640 | ||
4641 | /* | ||
4408 | if (compressedUpdateBlocks != null) | 4642 | if (compressedUpdateBlocks != null) |
4409 | { | 4643 | { |
4410 | ObjectUpdateCompressedPacket packet = (ObjectUpdateCompressedPacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdateCompressed); | 4644 | ObjectUpdateCompressedPacket packet = (ObjectUpdateCompressedPacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdateCompressed); |
@@ -4415,20 +4649,68 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4415 | 4649 | ||
4416 | OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(compressedUpdates, oPacket); }); | 4650 | OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(compressedUpdates, oPacket); }); |
4417 | } | 4651 | } |
4418 | 4652 | */ | |
4419 | if (terseUpdateBlocks != null) | 4653 | if (terseUpdates != null) |
4420 | { | 4654 | { |
4421 | ImprovedTerseObjectUpdatePacket packet = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket( | 4655 | int blocks = terseUpdates.Count; |
4422 | PacketType.ImprovedTerseObjectUpdate); | 4656 | List<EntityUpdate> tau = new List<EntityUpdate>(30); |
4423 | packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; | 4657 | |
4424 | packet.RegionData.TimeDilation = timeDilation; | 4658 | UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); |
4425 | packet.ObjectData = terseUpdateBlocks.ToArray(); | 4659 | |
4426 | terseUpdateBlocks.Clear(); | 4660 | //setup header and regioninfo block |
4661 | Buffer.BlockCopy(terseUpdateHeader, 0, buf.Data, 0, 7); | ||
4662 | Utils.UInt64ToBytesSafepos(m_scene.RegionInfo.RegionHandle, buf.Data, 7); | ||
4663 | Utils.UInt16ToBytes(timeDilation, buf.Data, 15); | ||
4664 | int pos = 18; | ||
4665 | int lastpos = 0; | ||
4427 | 4666 | ||
4428 | OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates, oPacket); }); | 4667 | int count = 0; |
4668 | foreach (EntityUpdate eu in terseUpdates) | ||
4669 | { | ||
4670 | lastpos = pos; | ||
4671 | CreateImprovedTerseBlock(eu.Entity, buf.Data, ref pos, (eu.Flags & PrimUpdateFlags.Textures) != 0); | ||
4672 | if (pos < LLUDPServer.MAXPAYLOAD) | ||
4673 | { | ||
4674 | tau.Add(eu); | ||
4675 | ++count; | ||
4676 | --blocks; | ||
4677 | } | ||
4678 | else if (blocks > 0) | ||
4679 | { | ||
4680 | // we need more packets | ||
4681 | UDPPacketBuffer newbuf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); | ||
4682 | Buffer.BlockCopy(buf.Data, 0, newbuf.Data, 0, 17); // start is the same | ||
4683 | // copy what we done in excess | ||
4684 | int extralen = pos - lastpos; | ||
4685 | if(extralen > 0) | ||
4686 | Buffer.BlockCopy(buf.Data, lastpos, newbuf.Data, 18, extralen); | ||
4687 | |||
4688 | pos = 18 + extralen; | ||
4689 | |||
4690 | buf.Data[17] = (byte)count; | ||
4691 | buf.DataLength = lastpos; | ||
4692 | // zero encode is not as spec | ||
4693 | m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, | ||
4694 | delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, true); | ||
4695 | |||
4696 | tau = new List<EntityUpdate>(30); | ||
4697 | tau.Add(eu); | ||
4698 | count = 1; | ||
4699 | --blocks; | ||
4700 | buf = newbuf; | ||
4701 | } | ||
4702 | } | ||
4703 | |||
4704 | if (count > 0) | ||
4705 | { | ||
4706 | buf.Data[17] = (byte)count; | ||
4707 | buf.DataLength = pos; | ||
4708 | m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, | ||
4709 | delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, true); | ||
4710 | } | ||
4429 | } | 4711 | } |
4430 | 4712 | ||
4431 | if(ObjectAnimationUpdates != null) | 4713 | if (ObjectAnimationUpdates != null) |
4432 | { | 4714 | { |
4433 | foreach (SceneObjectPart sop in ObjectAnimationUpdates) | 4715 | foreach (SceneObjectPart sop in ObjectAnimationUpdates) |
4434 | { | 4716 | { |
@@ -4445,13 +4727,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4445 | UUID[] ids = null; | 4727 | UUID[] ids = null; |
4446 | int[] seqs = null; | 4728 | int[] seqs = null; |
4447 | int count = sop.GetAnimations(out ids, out seqs); | 4729 | int count = sop.GetAnimations(out ids, out seqs); |
4448 | if(count < 0) | ||
4449 | continue; | ||
4450 | 4730 | ||
4451 | ObjectAnimationPacket ani = (ObjectAnimationPacket)PacketPool.Instance.GetPacket(PacketType.ObjectAnimation); | 4731 | ObjectAnimationPacket ani = (ObjectAnimationPacket)PacketPool.Instance.GetPacket(PacketType.ObjectAnimation); |
4452 | ani.Sender = new ObjectAnimationPacket.SenderBlock(); | 4732 | ani.Sender = new ObjectAnimationPacket.SenderBlock(); |
4453 | ani.Sender.ID = sop.UUID; | 4733 | ani.Sender.ID = sop.UUID; |
4454 | ani.AnimationList = new ObjectAnimationPacket.AnimationListBlock[sop.Animations.Count]; | 4734 | ani.AnimationList = new ObjectAnimationPacket.AnimationListBlock[count]; |
4455 | 4735 | ||
4456 | for(int i = 0; i< count; i++) | 4736 | for(int i = 0; i< count; i++) |
4457 | { | 4737 | { |
@@ -4642,9 +4922,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4642 | // These are used to implement an adaptive backoff in the number | 4922 | // These are used to implement an adaptive backoff in the number |
4643 | // of updates converted to packets. Since we don't want packets | 4923 | // of updates converted to packets. Since we don't want packets |
4644 | // to sit in the queue with old data, only convert enough updates | 4924 | // to sit in the queue with old data, only convert enough updates |
4645 | // to packets that can be sent in 200ms. | 4925 | // to packets that can be sent in 30ms. |
4646 | // private Int32 m_LastQueueFill = 0; | ||
4647 | // private Int32 m_maxUpdates = 0; | ||
4648 | 4926 | ||
4649 | void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) | 4927 | void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) |
4650 | { | 4928 | { |
@@ -5645,9 +5923,144 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5645 | return block; | 5923 | return block; |
5646 | } | 5924 | } |
5647 | 5925 | ||
5926 | protected void CreateImprovedTerseBlock(ISceneEntity entity, byte[] data, ref int pos, bool includeTexture) | ||
5927 | { | ||
5928 | #region ScenePresence/SOP Handling | ||
5929 | |||
5930 | bool avatar = (entity is ScenePresence); | ||
5931 | uint localID = entity.LocalId; | ||
5932 | uint attachPoint; | ||
5933 | Vector4 collisionPlane; | ||
5934 | Vector3 position, velocity, acceleration, angularVelocity; | ||
5935 | Quaternion rotation; | ||
5936 | byte datasize; | ||
5937 | byte[] te = null; | ||
5938 | |||
5939 | if (avatar) | ||
5940 | { | ||
5941 | ScenePresence presence = (ScenePresence)entity; | ||
5942 | |||
5943 | position = presence.OffsetPosition; | ||
5944 | velocity = presence.Velocity; | ||
5945 | acceleration = Vector3.Zero; | ||
5946 | rotation = presence.Rotation; | ||
5947 | // tpvs can only see rotations around Z in some cases | ||
5948 | if (!presence.Flying && !presence.IsSatOnObject) | ||
5949 | { | ||
5950 | rotation.X = 0f; | ||
5951 | rotation.Y = 0f; | ||
5952 | } | ||
5953 | rotation.Normalize(); | ||
5954 | angularVelocity = presence.AngularVelocity; | ||
5955 | |||
5956 | // m_log.DebugFormat( | ||
5957 | // "[LLCLIENTVIEW]: Sending terse update to {0} with position {1} in {2}", Name, presence.OffsetPosition, m_scene.Name); | ||
5958 | |||
5959 | attachPoint = presence.State; | ||
5960 | collisionPlane = presence.CollisionPlane; | ||
5961 | |||
5962 | datasize = 60; | ||
5963 | } | ||
5964 | else | ||
5965 | { | ||
5966 | SceneObjectPart part = (SceneObjectPart)entity; | ||
5967 | |||
5968 | attachPoint = part.ParentGroup.AttachmentPoint; | ||
5969 | attachPoint = ((attachPoint % 16) * 16 + (attachPoint / 16)); | ||
5970 | // m_log.DebugFormat( | ||
5971 | // "[LLCLIENTVIEW]: Sending attachPoint {0} for {1} {2} to {3}", | ||
5972 | // attachPoint, part.Name, part.LocalId, Name); | ||
5973 | |||
5974 | collisionPlane = Vector4.Zero; | ||
5975 | position = part.RelativePosition; | ||
5976 | velocity = part.Velocity; | ||
5977 | acceleration = part.Acceleration; | ||
5978 | angularVelocity = part.AngularVelocity; | ||
5979 | rotation = part.RotationOffset; | ||
5980 | |||
5981 | datasize = 44; | ||
5982 | if(includeTexture) | ||
5983 | te = part.Shape.TextureEntry; | ||
5984 | } | ||
5985 | |||
5986 | #endregion ScenePresence/SOP Handling | ||
5987 | //object block size | ||
5988 | data[pos++] = datasize; | ||
5989 | |||
5990 | // LocalID | ||
5991 | Utils.UIntToBytes(localID, data, pos); | ||
5992 | pos += 4; | ||
5993 | |||
5994 | data[pos++] = (byte)attachPoint; | ||
5995 | |||
5996 | // Avatar/CollisionPlane | ||
5997 | if (avatar) | ||
5998 | { | ||
5999 | data[pos++] = 1; | ||
6000 | |||
6001 | if (collisionPlane == Vector4.Zero) | ||
6002 | collisionPlane = Vector4.UnitW; | ||
6003 | //m_log.DebugFormat("CollisionPlane: {0}",collisionPlane); | ||
6004 | collisionPlane.ToBytes(data, pos); | ||
6005 | pos += 16; | ||
6006 | } | ||
6007 | else | ||
6008 | { | ||
6009 | data[pos++] = 0; | ||
6010 | } | ||
6011 | |||
6012 | // Position | ||
6013 | position.ToBytes(data, pos); | ||
6014 | pos += 12; | ||
6015 | |||
6016 | // Velocity | ||
6017 | ClampVectorForUint(ref velocity, 128f); | ||
6018 | Utils.FloatToUInt16Bytes(velocity.X, 128.0f, data, pos); pos += 2; | ||
6019 | Utils.FloatToUInt16Bytes(velocity.Y, 128.0f, data, pos); pos += 2; | ||
6020 | Utils.FloatToUInt16Bytes(velocity.Z, 128.0f, data, pos); pos += 2; | ||
6021 | |||
6022 | // Acceleration | ||
6023 | ClampVectorForUint(ref acceleration, 64f); | ||
6024 | Utils.FloatToUInt16Bytes(acceleration.X, 64.0f, data, pos); pos += 2; | ||
6025 | Utils.FloatToUInt16Bytes(acceleration.Y, 64.0f, data, pos); pos += 2; | ||
6026 | Utils.FloatToUInt16Bytes(acceleration.Z, 64.0f, data, pos); pos += 2; | ||
6027 | |||
6028 | // Rotation | ||
6029 | Utils.FloatToUInt16Bytes(rotation.X, 1.0f, data, pos); pos += 2; | ||
6030 | Utils.FloatToUInt16Bytes(rotation.Y, 1.0f, data, pos); pos += 2; | ||
6031 | Utils.FloatToUInt16Bytes(rotation.Z, 1.0f, data, pos); pos += 2; | ||
6032 | Utils.FloatToUInt16Bytes(rotation.W, 1.0f, data, pos); pos += 2; | ||
6033 | |||
6034 | // Angular Velocity | ||
6035 | ClampVectorForUint(ref angularVelocity, 64f); | ||
6036 | Utils.FloatToUInt16Bytes(angularVelocity.X, 64.0f, data, pos); pos += 2; | ||
6037 | Utils.FloatToUInt16Bytes(angularVelocity.Y, 64.0f, data, pos); pos += 2; | ||
6038 | Utils.FloatToUInt16Bytes(angularVelocity.Z, 64.0f, data, pos); pos += 2; | ||
6039 | |||
6040 | // texture entry block size | ||
6041 | if(te == null) | ||
6042 | { | ||
6043 | data[pos++] = 0; | ||
6044 | data[pos++] = 0; | ||
6045 | } | ||
6046 | else | ||
6047 | { | ||
6048 | int len = te.Length & 0x7fff; | ||
6049 | int totlen = len + 4; | ||
6050 | data[pos++] = (byte)totlen; | ||
6051 | data[pos++] = (byte)(totlen >> 8); | ||
6052 | data[pos++] = (byte)len; // wtf ??? | ||
6053 | data[pos++] = (byte)(len >> 8); | ||
6054 | data[pos++] = 0; | ||
6055 | data[pos++] = 0; | ||
6056 | Buffer.BlockCopy(te, 0, data, pos, len); | ||
6057 | pos += len; | ||
6058 | } | ||
6059 | // total size 63 or 47 + (texture size + 4) | ||
6060 | } | ||
6061 | |||
5648 | protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(ScenePresence data) | 6062 | protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(ScenePresence data) |
5649 | { | 6063 | { |
5650 | Vector3 offsetPosition = data.OffsetPosition; | ||
5651 | Quaternion rotation = data.Rotation; | 6064 | Quaternion rotation = data.Rotation; |
5652 | // tpvs can only see rotations around Z in some cases | 6065 | // tpvs can only see rotations around Z in some cases |
5653 | if(!data.Flying && !data.IsSatOnObject) | 6066 | if(!data.Flying && !data.IsSatOnObject) |
@@ -5657,27 +6070,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5657 | } | 6070 | } |
5658 | rotation.Normalize(); | 6071 | rotation.Normalize(); |
5659 | 6072 | ||
5660 | uint parentID = data.ParentID; | ||
5661 | |||
5662 | // m_log.DebugFormat( | 6073 | // m_log.DebugFormat( |
5663 | // "[LLCLIENTVIEW]: Sending full update to {0} with pos {1}, vel {2} in {3}", Name, data.OffsetPosition, data.Velocity, m_scene.Name); | 6074 | // "[LLCLIENTVIEW]: Sending full update to {0} with pos {1}, vel {2} in {3}", Name, data.OffsetPosition, data.Velocity, m_scene.Name); |
5664 | 6075 | ||
5665 | byte[] objectData = new byte[76]; | 6076 | byte[] objectData = new byte[76]; |
5666 | 6077 | ||
5667 | Vector3 velocity = new Vector3(0, 0, 0); | 6078 | //Vector3 velocity = Vector3.Zero; |
5668 | Vector3 acceleration = new Vector3(0, 0, 0); | 6079 | Vector3 acceleration = Vector3.Zero; |
6080 | Vector3 angularvelocity = Vector3.Zero; | ||
5669 | 6081 | ||
5670 | data.CollisionPlane.ToBytes(objectData, 0); | 6082 | data.CollisionPlane.ToBytes(objectData, 0); |
5671 | offsetPosition.ToBytes(objectData, 16); | 6083 | data.OffsetPosition.ToBytes(objectData, 16); |
5672 | velocity.ToBytes(objectData, 28); | 6084 | data.Velocity.ToBytes(objectData, 28); |
5673 | acceleration.ToBytes(objectData, 40); | 6085 | acceleration.ToBytes(objectData, 40); |
5674 | rotation.ToBytes(objectData, 52); | 6086 | rotation.ToBytes(objectData, 52); |
5675 | data.AngularVelocity.ToBytes(objectData, 64); | 6087 | angularvelocity.ToBytes(objectData, 64); |
5676 | 6088 | ||
5677 | ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); | 6089 | ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); |
5678 | 6090 | ||
5679 | update.Data = Utils.EmptyBytes; | 6091 | update.Data = Utils.EmptyBytes; |
5680 | update.ExtraParams = new byte[1]; | 6092 | update.ExtraParams = Utils.EmptyBytes; |
5681 | update.FullID = data.UUID; | 6093 | update.FullID = data.UUID; |
5682 | update.ID = data.LocalId; | 6094 | update.ID = data.LocalId; |
5683 | update.Material = (byte)Material.Flesh; | 6095 | update.Material = (byte)Material.Flesh; |
@@ -5714,7 +6126,158 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5714 | return update; | 6126 | return update; |
5715 | } | 6127 | } |
5716 | 6128 | ||
5717 | // protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SceneObjectPart data, UUID recipientID) | 6129 | protected void CreateAvatarUpdateBlock(ScenePresence data, byte[] dest, ref int pos) |
6130 | { | ||
6131 | Quaternion rotation = data.Rotation; | ||
6132 | // tpvs can only see rotations around Z in some cases | ||
6133 | if (!data.Flying && !data.IsSatOnObject) | ||
6134 | { | ||
6135 | rotation.X = 0f; | ||
6136 | rotation.Y = 0f; | ||
6137 | } | ||
6138 | rotation.Normalize(); | ||
6139 | |||
6140 | //Vector3 velocity = Vector3.Zero; | ||
6141 | //Vector3 acceleration = Vector3.Zero; | ||
6142 | //Vector3 angularvelocity = Vector3.Zero; | ||
6143 | |||
6144 | Utils.UIntToBytesSafepos(data.LocalId, dest, pos); pos += 4; | ||
6145 | dest[pos++] = 0; // state | ||
6146 | data.UUID.ToBytes(dest, pos); pos += 16; | ||
6147 | Utils.UIntToBytesSafepos(0 , dest, pos); pos += 4; // crc | ||
6148 | dest[pos++] = (byte)PCode.Avatar; | ||
6149 | dest[pos++] = (byte)Material.Flesh; | ||
6150 | dest[pos++] = 0; // clickaction | ||
6151 | data.Appearance.AvatarSize.ToBytes(dest, pos); pos += 12; | ||
6152 | |||
6153 | // objectdata block | ||
6154 | dest[pos++] = 76; | ||
6155 | data.CollisionPlane.ToBytes(dest, pos); pos += 16; | ||
6156 | data.OffsetPosition.ToBytes(dest, pos); pos += 12; | ||
6157 | data.Velocity.ToBytes(dest, pos); pos += 12; | ||
6158 | |||
6159 | //acceleration.ToBytes(dest, pos); pos += 12; | ||
6160 | Array.Clear(dest, pos, 12); pos += 12; | ||
6161 | |||
6162 | rotation.ToBytes(dest, pos); pos += 12; | ||
6163 | |||
6164 | //angularvelocity.ToBytes(dest, pos); pos += 12; | ||
6165 | Array.Clear(dest, pos, 12); pos += 12; | ||
6166 | |||
6167 | SceneObjectPart parentPart = data.ParentPart; | ||
6168 | if (parentPart != null) | ||
6169 | { | ||
6170 | Utils.UIntToBytesSafepos(parentPart.ParentGroup.LocalId, dest, pos); | ||
6171 | pos += 4; | ||
6172 | } | ||
6173 | else | ||
6174 | { | ||
6175 | // Utils.UIntToBytesSafepos(0, dest, pos); | ||
6176 | // pos += 4; | ||
6177 | dest[pos++] = 0; | ||
6178 | dest[pos++] = 0; | ||
6179 | dest[pos++] = 0; | ||
6180 | dest[pos++] = 0; | ||
6181 | } | ||
6182 | |||
6183 | //Utils.UIntToBytesSafepos(0, dest, pos); pos += 4; //update flags | ||
6184 | dest[pos++] = 0; | ||
6185 | dest[pos++] = 0; | ||
6186 | dest[pos++] = 0; | ||
6187 | dest[pos++] = 0; | ||
6188 | |||
6189 | //pbs | ||
6190 | dest[pos++] = 16; | ||
6191 | dest[pos++] = 1; | ||
6192 | //Utils.UInt16ToBytes(0, dest, pos); pos += 2; | ||
6193 | //Utils.UInt16ToBytes(0, dest, pos); pos += 2; | ||
6194 | dest[pos++] = 0; | ||
6195 | dest[pos++] = 0; | ||
6196 | dest[pos++] = 0; | ||
6197 | dest[pos++] = 0; | ||
6198 | |||
6199 | dest[pos++] = 100; | ||
6200 | dest[pos++] = 100; | ||
6201 | |||
6202 | // rest of pbs is 0 (15), texture entry (2) and texture anim (1) | ||
6203 | const int pbszeros = 15 + 2 + 1; | ||
6204 | Array.Clear(dest, pos, pbszeros); pos += pbszeros; | ||
6205 | |||
6206 | //NameValue | ||
6207 | byte[] nv = Utils.StringToBytes("FirstName STRING RW SV " + data.Firstname + "\nLastName STRING RW SV " + | ||
6208 | data.Lastname + "\nTitle STRING RW SV " + data.Grouptitle); | ||
6209 | int len = nv.Length; | ||
6210 | dest[pos++] = (byte)len; | ||
6211 | dest[pos++] = (byte)(len >> 8); | ||
6212 | Buffer.BlockCopy(nv, 0, dest, pos, len); pos += len; | ||
6213 | |||
6214 | // data(2), text(1), text color(4), media url(1), PBblock(1), ExtramParams(1), | ||
6215 | // sound id(16), sound owner(16) gain (4), flags (1), radius (4) | ||
6216 | // jointtype(1) joint pivot(12) joint offset(12) | ||
6217 | const int lastzeros = 2 + 1 + 4 + 1 + 1 + 1 + 16 + 16 + 4 + 1 + 4 + 1 + 12 + 12; | ||
6218 | Array.Clear(dest, pos, lastzeros); pos += lastzeros; | ||
6219 | } | ||
6220 | |||
6221 | protected void CreateAvatarUpdateBlock(ScenePresence data, LLUDPZeroEncoder zc) | ||
6222 | { | ||
6223 | Quaternion rotation = data.Rotation; | ||
6224 | // tpvs can only see rotations around Z in some cases | ||
6225 | if (!data.Flying && !data.IsSatOnObject) | ||
6226 | { | ||
6227 | rotation.X = 0f; | ||
6228 | rotation.Y = 0f; | ||
6229 | } | ||
6230 | rotation.Normalize(); | ||
6231 | |||
6232 | zc.AddUInt(data.LocalId); | ||
6233 | zc.AddByte(0); | ||
6234 | zc.AddUUID(data.UUID); | ||
6235 | zc.AddZeros(4); // crc unused | ||
6236 | zc.AddByte((byte)PCode.Avatar); | ||
6237 | zc.AddByte((byte)Material.Flesh); | ||
6238 | zc.AddByte(0); // clickaction | ||
6239 | zc.AddVector3(data.Appearance.AvatarSize); | ||
6240 | |||
6241 | // objectdata block | ||
6242 | zc.AddByte(76); // fixed avatar block size | ||
6243 | zc.AddVector4(data.CollisionPlane); | ||
6244 | zc.AddVector3(data.OffsetPosition); | ||
6245 | zc.AddVector3(data.Velocity); | ||
6246 | //zc.AddVector3(acceleration); | ||
6247 | zc.AddZeros(12); | ||
6248 | zc.AddNormQuat(rotation); | ||
6249 | //zc.AddVector3(angularvelocity); | ||
6250 | zc.AddZeros(12); | ||
6251 | |||
6252 | SceneObjectPart parentPart = data.ParentPart; | ||
6253 | if (parentPart != null) | ||
6254 | zc.AddUInt(parentPart.ParentGroup.LocalId); | ||
6255 | else | ||
6256 | zc.AddZeros(4); | ||
6257 | |||
6258 | zc.AddZeros(4); //update flags | ||
6259 | |||
6260 | //pbs volume data 23 | ||
6261 | //texture entry 2 | ||
6262 | //texture anim (1) | ||
6263 | const int pbszeros = 23 + 2 + 1; | ||
6264 | zc.AddZeros(pbszeros); | ||
6265 | |||
6266 | //NameValue | ||
6267 | byte[] nv = Utils.StringToBytes("FirstName STRING RW SV " + data.Firstname + "\nLastName STRING RW SV " + | ||
6268 | data.Lastname + "\nTitle STRING RW SV " + data.Grouptitle); | ||
6269 | int len = nv.Length; | ||
6270 | zc.AddByte((byte)len); | ||
6271 | zc.AddByte((byte)(len >> 8)); | ||
6272 | zc.AddBytes(nv, len); | ||
6273 | |||
6274 | // data(2), text(1), text color(4), media url(1), PBblock(1), ExtramParams(1), | ||
6275 | // sound id(16), sound owner(16) gain (4), flags (1), radius (4) | ||
6276 | // jointtype(1) joint pivot(12) joint offset(12) | ||
6277 | const int lastzeros = 2 + 1 + 4 + 1 + 1 + 1 + 16 + 16 + 4 + 1 + 4 + 1 + 12 + 12; | ||
6278 | zc.AddZeros(lastzeros); | ||
6279 | } | ||
6280 | |||
5718 | protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SceneObjectPart part, ScenePresence sp) | 6281 | protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SceneObjectPart part, ScenePresence sp) |
5719 | { | 6282 | { |
5720 | byte[] objectData = new byte[60]; | 6283 | byte[] objectData = new byte[60]; |
@@ -5737,32 +6300,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5737 | //update.JointPivot = Vector3.Zero; | 6300 | //update.JointPivot = Vector3.Zero; |
5738 | //update.JointType = 0; | 6301 | //update.JointType = 0; |
5739 | update.Material = part.Material; | 6302 | update.Material = part.Material; |
5740 | /* | ||
5741 | if (data.ParentGroup.IsAttachment) | ||
5742 | { | ||
5743 | update.NameValue | ||
5744 | = Util.StringToBytes256( | ||
5745 | string.Format("AttachItemID STRING RW SV {0}", data.ParentGroup.FromItemID)); | ||
5746 | |||
5747 | update.State = (byte)((data.ParentGroup.AttachmentPoint % 16) * 16 + (data.ParentGroup.AttachmentPoint / 16)); | ||
5748 | |||
5749 | // m_log.DebugFormat( | ||
5750 | // "[LLCLIENTVIEW]: Sending NameValue {0} for {1} {2} to {3}", | ||
5751 | // Util.UTF8.GetString(update.NameValue), data.Name, data.LocalId, Name); | ||
5752 | // | ||
5753 | // m_log.DebugFormat( | ||
5754 | // "[LLCLIENTVIEW]: Sending state {0} for {1} {2} to {3}", | ||
5755 | // update.State, data.Name, data.LocalId, Name); | ||
5756 | } | ||
5757 | else | ||
5758 | { | ||
5759 | update.NameValue = Utils.EmptyBytes; | ||
5760 | |||
5761 | // The root part state is the canonical state for all parts of the object. The other part states in the | ||
5762 | // case for attachments may contain conflicting values that can end up crashing the viewer. | ||
5763 | update.State = data.ParentGroup.RootPart.Shape.State; | ||
5764 | } | ||
5765 | */ | ||
5766 | 6303 | ||
5767 | if (part.ParentGroup.IsAttachment) | 6304 | if (part.ParentGroup.IsAttachment) |
5768 | { | 6305 | { |
@@ -5858,15 +6395,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5858 | 6395 | ||
5859 | #endregion PrimFlags | 6396 | #endregion PrimFlags |
5860 | 6397 | ||
5861 | if (part.Sound != UUID.Zero || part.SoundFlags != 0) | 6398 | bool hassound = part.Sound != UUID.Zero || part.SoundFlags != 0; |
6399 | if (hassound) | ||
5862 | { | 6400 | { |
5863 | update.Sound = part.Sound; | 6401 | update.Sound = part.Sound; |
5864 | update.OwnerID = part.OwnerID; | ||
5865 | update.Gain = (float)part.SoundGain; | 6402 | update.Gain = (float)part.SoundGain; |
5866 | update.Radius = (float)part.SoundRadius; | 6403 | update.Radius = (float)part.SoundRadius; |
5867 | update.Flags = part.SoundFlags; | 6404 | update.Flags = part.SoundFlags; |
5868 | } | 6405 | } |
5869 | 6406 | ||
6407 | if(hassound || update.PSBlock.Length > 1) | ||
6408 | update.OwnerID = part.OwnerID; | ||
6409 | |||
5870 | switch ((PCode)part.Shape.PCode) | 6410 | switch ((PCode)part.Shape.PCode) |
5871 | { | 6411 | { |
5872 | case PCode.Grass: | 6412 | case PCode.Grass: |
@@ -5882,6 +6422,308 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5882 | return update; | 6422 | return update; |
5883 | } | 6423 | } |
5884 | 6424 | ||
6425 | protected void CreatePrimUpdateBlock(SceneObjectPart part, ScenePresence sp, LLUDPZeroEncoder zc) | ||
6426 | { | ||
6427 | // prepare data | ||
6428 | |||
6429 | #region PrimFlags | ||
6430 | // prim/update flags | ||
6431 | PrimFlags primflags = (PrimFlags)m_scene.Permissions.GenerateClientFlags(part, sp); | ||
6432 | // Don't send the CreateSelected flag to everyone | ||
6433 | primflags &= ~PrimFlags.CreateSelected; | ||
6434 | if (sp.UUID == part.OwnerID) | ||
6435 | { | ||
6436 | if (part.CreateSelected) | ||
6437 | { | ||
6438 | // Only send this flag once, then unset it | ||
6439 | primflags |= PrimFlags.CreateSelected; | ||
6440 | part.CreateSelected = false; | ||
6441 | } | ||
6442 | } | ||
6443 | #endregion PrimFlags | ||
6444 | |||
6445 | // data block | ||
6446 | byte[] data = null; | ||
6447 | byte state = part.Shape.State; | ||
6448 | PCode pcode = (PCode)part.Shape.PCode; | ||
6449 | |||
6450 | //vegetation is special so just do it inline | ||
6451 | if(pcode == PCode.Grass || pcode == PCode.Tree || pcode == PCode.NewTree) | ||
6452 | { | ||
6453 | zc.AddUInt(part.LocalId); | ||
6454 | zc.AddByte(state); // state | ||
6455 | zc.AddUUID(part.UUID); | ||
6456 | zc.AddZeros(4); // crc unused | ||
6457 | zc.AddByte((byte)pcode); | ||
6458 | // material 1 | ||
6459 | // clickaction 1 | ||
6460 | zc.AddZeros(2); | ||
6461 | zc.AddVector3(part.Shape.Scale); | ||
6462 | |||
6463 | // objectdata block | ||
6464 | zc.AddByte(60); // fixed object block size | ||
6465 | zc.AddVector3(part.RelativePosition); | ||
6466 | if (pcode == PCode.Grass) | ||
6467 | zc.AddZeros(48); | ||
6468 | else | ||
6469 | { | ||
6470 | zc.AddZeros(24); | ||
6471 | Quaternion rot = part.RotationOffset; | ||
6472 | rot.Normalize(); | ||
6473 | zc.AddNormQuat(rot); | ||
6474 | zc.AddZeros(12); | ||
6475 | } | ||
6476 | |||
6477 | zc.AddUInt(part.ParentID); | ||
6478 | zc.AddUInt((uint)primflags); //update flags | ||
6479 | |||
6480 | /* | ||
6481 | if (pcode == PCode.Grass) | ||
6482 | { | ||
6483 | //pbs volume data 23 | ||
6484 | //texture entry 2 | ||
6485 | //texture anim 1 | ||
6486 | //name value 2 | ||
6487 | // data 1 | ||
6488 | // text 5 | ||
6489 | // media url 1 | ||
6490 | // particle system 1 | ||
6491 | // Extraparams 1 | ||
6492 | // sound id 16 | ||
6493 | // ownwer 16 | ||
6494 | // sound gain 4 | ||
6495 | // sound flags 1 | ||
6496 | // sound radius 4 | ||
6497 | // jointtype 1 | ||
6498 | // joint pivot 12 | ||
6499 | // joint offset 12 | ||
6500 | zc.AddZeros(23 + 2 + 1 + 2 + 1 + 5 + 1 + 1 + 1 + 16 + 16 + 4 + 1 + 4 + 1 + 12 + 12); | ||
6501 | return; | ||
6502 | } | ||
6503 | */ | ||
6504 | |||
6505 | //pbs volume data 23 | ||
6506 | //texture entry 2 | ||
6507 | //texture anim 1 | ||
6508 | //name value 2 | ||
6509 | zc.AddZeros(23 + 2 + 1 + 2); | ||
6510 | |||
6511 | //data: the tree type | ||
6512 | zc.AddByte(1); | ||
6513 | zc.AddZeros(1); | ||
6514 | zc.AddByte(state); | ||
6515 | |||
6516 | // text 5 | ||
6517 | // media url 1 | ||
6518 | // particle system 1 | ||
6519 | // Extraparams 1 | ||
6520 | // sound id 16 | ||
6521 | // ownwer 16 | ||
6522 | // sound gain 4 | ||
6523 | // sound flags 1 | ||
6524 | // sound radius 4 | ||
6525 | // jointtype 1 | ||
6526 | // joint pivot 12 | ||
6527 | // joint offset 12 | ||
6528 | zc.AddZeros(5 + 1 + 1 + 1 + 16 + 16 + 4 + 1 + 4 + 1 + 12 + 12); | ||
6529 | |||
6530 | return; | ||
6531 | } | ||
6532 | |||
6533 | //NameValue and state | ||
6534 | byte[] nv = null; | ||
6535 | |||
6536 | if (part.ParentGroup.IsAttachment) | ||
6537 | { | ||
6538 | if (part.IsRoot) | ||
6539 | nv = Util.StringToBytes256("AttachItemID STRING RW SV " + part.ParentGroup.FromItemID); | ||
6540 | |||
6541 | int st = (int)part.ParentGroup.AttachmentPoint; | ||
6542 | state = (byte)(((st & 0xf0) >> 4) + ((st & 0x0f) << 4)); ; | ||
6543 | } | ||
6544 | |||
6545 | // filter out mesh faces hack | ||
6546 | ushort profileBegin = part.Shape.ProfileBegin; | ||
6547 | ushort profileHollow = part.Shape.ProfileHollow; | ||
6548 | byte profileCurve = part.Shape.ProfileCurve; | ||
6549 | byte pathScaleY = part.Shape.PathScaleY; | ||
6550 | |||
6551 | if (part.Shape.SculptType == (byte)SculptType.Mesh) // filter out hack | ||
6552 | { | ||
6553 | profileCurve = (byte)(part.Shape.ProfileCurve & 0x0f); | ||
6554 | // fix old values that confused viewers | ||
6555 | if (profileBegin == 1) | ||
6556 | profileBegin = 9375; | ||
6557 | if (profileHollow == 1) | ||
6558 | profileHollow = 27500; | ||
6559 | // fix torus hole size Y that also confuse some viewers | ||
6560 | if (profileCurve == (byte)ProfileShape.Circle && pathScaleY < 150) | ||
6561 | pathScaleY = 150; | ||
6562 | } | ||
6563 | |||
6564 | // do encode the things | ||
6565 | zc.AddUInt(part.LocalId); | ||
6566 | zc.AddByte(state); // state | ||
6567 | zc.AddUUID(part.UUID); | ||
6568 | zc.AddZeros(4); // crc unused | ||
6569 | zc.AddByte((byte)pcode); | ||
6570 | zc.AddByte(part.Material); | ||
6571 | zc.AddByte(part.ClickAction); // clickaction | ||
6572 | zc.AddVector3(part.Shape.Scale); | ||
6573 | |||
6574 | // objectdata block | ||
6575 | zc.AddByte(60); // fixed object block size | ||
6576 | zc.AddVector3(part.RelativePosition); | ||
6577 | zc.AddVector3(part.Velocity); | ||
6578 | zc.AddVector3(part.Acceleration); | ||
6579 | Quaternion rotation = part.RotationOffset; | ||
6580 | rotation.Normalize(); | ||
6581 | zc.AddNormQuat(rotation); | ||
6582 | zc.AddVector3(part.AngularVelocity); | ||
6583 | |||
6584 | zc.AddUInt(part.ParentID); | ||
6585 | zc.AddUInt((uint)primflags); //update flags | ||
6586 | |||
6587 | //pbs | ||
6588 | zc.AddByte(part.Shape.PathCurve); | ||
6589 | zc.AddByte(profileCurve); | ||
6590 | zc.AddUInt16(part.Shape.PathBegin); | ||
6591 | zc.AddUInt16(part.Shape.PathEnd); | ||
6592 | zc.AddByte(part.Shape.PathScaleX); | ||
6593 | zc.AddByte(pathScaleY); | ||
6594 | zc.AddByte(part.Shape.PathShearX); | ||
6595 | zc.AddByte(part.Shape.PathShearY); | ||
6596 | zc.AddByte((byte)part.Shape.PathTwist); | ||
6597 | zc.AddByte((byte)part.Shape.PathTwistBegin); | ||
6598 | zc.AddByte((byte)part.Shape.PathRadiusOffset); | ||
6599 | zc.AddByte((byte)part.Shape.PathTaperX); | ||
6600 | zc.AddByte((byte)part.Shape.PathTaperY); | ||
6601 | zc.AddByte(part.Shape.PathRevolutions); | ||
6602 | zc.AddByte((byte)part.Shape.PathSkew); | ||
6603 | zc.AddUInt16(profileBegin); | ||
6604 | zc.AddUInt16(part.Shape.ProfileEnd); | ||
6605 | zc.AddUInt16(profileHollow); | ||
6606 | |||
6607 | // texture | ||
6608 | byte[] tentry = part.Shape.TextureEntry; | ||
6609 | if (tentry == null) | ||
6610 | zc.AddZeros(2); | ||
6611 | else | ||
6612 | { | ||
6613 | int len = tentry.Length; | ||
6614 | zc.AddByte((byte)len); | ||
6615 | zc.AddByte((byte)(len >> 8)); | ||
6616 | zc.AddBytes(tentry, len); | ||
6617 | } | ||
6618 | |||
6619 | // texture animation | ||
6620 | byte[] tanim = part.TextureAnimation; | ||
6621 | if (tanim == null) | ||
6622 | zc.AddZeros(1); | ||
6623 | else | ||
6624 | { | ||
6625 | int len = tanim.Length; | ||
6626 | zc.AddByte((byte)len); | ||
6627 | zc.AddBytes(tanim, len); | ||
6628 | } | ||
6629 | |||
6630 | //NameValue | ||
6631 | if(nv == null) | ||
6632 | zc.AddZeros(2); | ||
6633 | else | ||
6634 | { | ||
6635 | int len = nv.Length; | ||
6636 | zc.AddByte((byte)len); | ||
6637 | zc.AddByte((byte)(len >> 8)); | ||
6638 | zc.AddBytes(nv, len); | ||
6639 | } | ||
6640 | |||
6641 | // data | ||
6642 | if (data == null) | ||
6643 | zc.AddZeros(2); | ||
6644 | else | ||
6645 | { | ||
6646 | int len = data.Length; | ||
6647 | zc.AddByte((byte)len); | ||
6648 | zc.AddByte((byte)(len >> 8)); | ||
6649 | zc.AddBytes(data, len); | ||
6650 | } | ||
6651 | |||
6652 | //text | ||
6653 | if (part.Text == null || part.Text.Length == 0) | ||
6654 | zc.AddZeros(5); | ||
6655 | else | ||
6656 | { | ||
6657 | byte[] tbuf = Util.StringToBytes(part.Text, 254); | ||
6658 | int len = tbuf.Length; | ||
6659 | zc.AddByte((byte)len); | ||
6660 | zc.AddBytes(tbuf, len); | ||
6661 | |||
6662 | //textcolor | ||
6663 | byte[] tc = part.GetTextColor().GetBytes(false); | ||
6664 | zc.AddBytes(tc, 4); | ||
6665 | } | ||
6666 | |||
6667 | //media url | ||
6668 | if (part.MediaUrl == null || part.MediaUrl.Length == 0) | ||
6669 | zc.AddZeros(1); | ||
6670 | else | ||
6671 | { | ||
6672 | byte[] tbuf = Util.StringToBytes(part.MediaUrl, 255); | ||
6673 | int len = tbuf.Length; | ||
6674 | zc.AddByte((byte)len); | ||
6675 | zc.AddBytes(tbuf, len); | ||
6676 | } | ||
6677 | |||
6678 | bool hasps = false; | ||
6679 | //particle system | ||
6680 | byte[] ps = part.ParticleSystem; | ||
6681 | if (ps == null || ps.Length < 1) | ||
6682 | zc.AddZeros(1); | ||
6683 | else | ||
6684 | { | ||
6685 | int len = ps.Length; | ||
6686 | zc.AddByte((byte)len); | ||
6687 | zc.AddBytes(ps, len); | ||
6688 | hasps = true; | ||
6689 | } | ||
6690 | |||
6691 | //Extraparams | ||
6692 | byte[] ep = part.Shape.ExtraParams; | ||
6693 | if (ep == null || ep.Length < 2) | ||
6694 | zc.AddZeros(1); | ||
6695 | else | ||
6696 | { | ||
6697 | int len = ep.Length; | ||
6698 | zc.AddByte((byte)len); | ||
6699 | zc.AddBytes(ep, len); | ||
6700 | } | ||
6701 | |||
6702 | bool hassound = part.Sound != UUID.Zero || part.SoundFlags != 0; | ||
6703 | if (hassound) | ||
6704 | zc.AddUUID(part.Sound); | ||
6705 | else | ||
6706 | zc.AddZeros(16); | ||
6707 | |||
6708 | if (hassound || hasps) | ||
6709 | zc.AddUUID(part.OwnerID); | ||
6710 | else | ||
6711 | zc.AddZeros(16); | ||
6712 | |||
6713 | if (hassound) | ||
6714 | { | ||
6715 | zc.AddFloat((float)part.SoundGain); | ||
6716 | zc.AddByte(part.SoundFlags); | ||
6717 | zc.AddFloat((float)part.SoundRadius); | ||
6718 | } | ||
6719 | else | ||
6720 | zc.AddZeros(9); | ||
6721 | |||
6722 | // jointtype(1) joint pivot(12) joint offset(12) | ||
6723 | const int lastzeros = 1 + 12 + 12; | ||
6724 | zc.AddZeros(lastzeros); | ||
6725 | } | ||
6726 | |||
5885 | protected ObjectUpdateCompressedPacket.ObjectDataBlock CreateCompressedUpdateBlock(SceneObjectPart part, PrimUpdateFlags updateFlags) | 6727 | protected ObjectUpdateCompressedPacket.ObjectDataBlock CreateCompressedUpdateBlock(SceneObjectPart part, PrimUpdateFlags updateFlags) |
5886 | { | 6728 | { |
5887 | // TODO: Implement this | 6729 | // TODO: Implement this |
@@ -10047,7 +10889,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
10047 | 10889 | ||
10048 | UUID.TryParse(Utils.BytesToString(messagePacket.ParamList[1].Parameter), out Prey); | 10890 | UUID.TryParse(Utils.BytesToString(messagePacket.ParamList[1].Parameter), out Prey); |
10049 | 10891 | ||
10050 | OnEstateTeleportOneUserHomeRequest(this, invoice, SenderID, Prey); | 10892 | OnEstateTeleportOneUserHomeRequest(this, invoice, SenderID, Prey, false); |
10051 | } | 10893 | } |
10052 | return true; | 10894 | return true; |
10053 | case "teleporthomeallusers": | 10895 | case "teleporthomeallusers": |
@@ -10195,7 +11037,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
10195 | 11037 | ||
10196 | UUID.TryParse(Utils.BytesToString(messagePacket.ParamList[0].Parameter), out Prey); | 11038 | UUID.TryParse(Utils.BytesToString(messagePacket.ParamList[0].Parameter), out Prey); |
10197 | 11039 | ||
10198 | OnEstateTeleportOneUserHomeRequest(this, invoice, SenderID, Prey); | 11040 | OnEstateTeleportOneUserHomeRequest(this, invoice, SenderID, Prey, true); |
10199 | } | 11041 | } |
10200 | return true; | 11042 | return true; |
10201 | 11043 | ||
@@ -12560,14 +13402,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12560 | /// provide your own method.</param> | 13402 | /// provide your own method.</param> |
12561 | protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method) | 13403 | protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method) |
12562 | { | 13404 | { |
12563 | |||
12564 | /* this is causing packet loss for some reason | ||
12565 | if(!m_udpClient.IsConnected) | ||
12566 | { | ||
12567 | PacketPool.Instance.ReturnPacket(packet); | ||
12568 | return; | ||
12569 | } | ||
12570 | */ | ||
12571 | if (m_outPacketsToDrop != null) | 13405 | if (m_outPacketsToDrop != null) |
12572 | { | 13406 | { |
12573 | if (m_outPacketsToDrop.Contains(packet.Type.ToString())) | 13407 | if (m_outPacketsToDrop.Contains(packet.Type.ToString())) |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index 439621a..2981337 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs | |||
@@ -120,7 +120,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
120 | /// <summary>Circuit code that this client is connected on</summary> | 120 | /// <summary>Circuit code that this client is connected on</summary> |
121 | public readonly uint CircuitCode; | 121 | public readonly uint CircuitCode; |
122 | /// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary> | 122 | /// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary> |
123 | public IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200); | 123 | public IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(256); |
124 | 124 | ||
125 | /// <summary>Packets we have sent that need to be ACKed by the client</summary> | 125 | /// <summary>Packets we have sent that need to be ACKed by the client</summary> |
126 | public UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); | 126 | public UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); |
@@ -210,12 +210,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
210 | } | 210 | } |
211 | } | 211 | } |
212 | 212 | ||
213 | /// <summary> | ||
214 | /// This is the percentage of the udp texture queue to add to the task queue since | ||
215 | /// textures are now generally handled through http. | ||
216 | /// </summary> | ||
217 | private double m_cannibalrate = 0.0; | ||
218 | |||
219 | private ClientInfo m_info = new ClientInfo(); | 213 | private ClientInfo m_info = new ClientInfo(); |
220 | 214 | ||
221 | /// <summary> | 215 | /// <summary> |
@@ -257,8 +251,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
257 | // Create an array of token buckets for this clients different throttle categories | 251 | // Create an array of token buckets for this clients different throttle categories |
258 | m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; | 252 | m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; |
259 | 253 | ||
260 | m_cannibalrate = rates.CannibalizeTextureRate; | ||
261 | |||
262 | m_burst = rates.Total * rates.BrustTime; | 254 | m_burst = rates.Total * rates.BrustTime; |
263 | 255 | ||
264 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) | 256 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) |
@@ -449,12 +441,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
449 | asset = Math.Max(asset, LLUDPServer.MTU); | 441 | asset = Math.Max(asset, LLUDPServer.MTU); |
450 | */ | 442 | */ |
451 | 443 | ||
452 | // Since most textures are now delivered through http, make it possible | ||
453 | // to cannibalize some of the bw from the texture throttle to use for | ||
454 | // the task queue (e.g. object updates) | ||
455 | task = task + (int)(m_cannibalrate * texture); | ||
456 | texture = (int)((1 - m_cannibalrate) * texture); | ||
457 | |||
458 | int total = resend + land + wind + cloud + task + texture + asset; | 444 | int total = resend + land + wind + cloud + task + texture + asset; |
459 | 445 | ||
460 | float m_burst = total * m_burstTime; | 446 | float m_burst = total * m_burstTime; |
@@ -575,22 +561,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
575 | { | 561 | { |
576 | DoubleLocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category]; | 562 | DoubleLocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category]; |
577 | 563 | ||
578 | if (m_deliverPackets == false) | 564 | if (forceQueue || m_deliverPackets == false) |
579 | { | 565 | { |
580 | queue.Enqueue(packet, highPriority); | 566 | queue.Enqueue(packet, highPriority); |
581 | return true; | 567 | return true; |
582 | } | 568 | } |
583 | 569 | ||
584 | TokenBucket bucket = m_throttleCategories[category]; | 570 | // need to enqueue if queue is not empty |
585 | |||
586 | // Don't send this packet if queue is not empty | ||
587 | if (queue.Count > 0 || m_nextPackets[category] != null) | 571 | if (queue.Count > 0 || m_nextPackets[category] != null) |
588 | { | 572 | { |
589 | queue.Enqueue(packet, highPriority); | 573 | queue.Enqueue(packet, highPriority); |
590 | return true; | 574 | return true; |
591 | } | 575 | } |
592 | 576 | ||
593 | if (!forceQueue && bucket.CheckTokens(packet.Buffer.DataLength)) | 577 | // check bandwidth |
578 | TokenBucket bucket = m_throttleCategories[category]; | ||
579 | if (bucket.CheckTokens(packet.Buffer.DataLength)) | ||
594 | { | 580 | { |
595 | // enough tokens so it can be sent imediatly by caller | 581 | // enough tokens so it can be sent imediatly by caller |
596 | bucket.RemoveTokens(packet.Buffer.DataLength); | 582 | bucket.RemoveTokens(packet.Buffer.DataLength); |
@@ -608,7 +594,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
608 | // We don't have a token bucket for this category, so it will not be queued | 594 | // We don't have a token bucket for this category, so it will not be queued |
609 | return false; | 595 | return false; |
610 | } | 596 | } |
611 | |||
612 | } | 597 | } |
613 | 598 | ||
614 | /// <summary> | 599 | /// <summary> |
@@ -650,6 +635,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
650 | // leaving a dequeued packet still waiting to be sent out. Try to | 635 | // leaving a dequeued packet still waiting to be sent out. Try to |
651 | // send it again | 636 | // send it again |
652 | OutgoingPacket nextPacket = m_nextPackets[i]; | 637 | OutgoingPacket nextPacket = m_nextPackets[i]; |
638 | if(nextPacket.Buffer == null) | ||
639 | { | ||
640 | if (m_packetOutboxes[i].Count < 5) | ||
641 | emptyCategories |= CategoryToFlag(i); | ||
642 | continue; | ||
643 | } | ||
653 | if (bucket.RemoveTokens(nextPacket.Buffer.DataLength)) | 644 | if (bucket.RemoveTokens(nextPacket.Buffer.DataLength)) |
654 | { | 645 | { |
655 | // Send the packet | 646 | // Send the packet |
@@ -681,21 +672,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
681 | { | 672 | { |
682 | // A packet was pulled off the queue. See if we have | 673 | // A packet was pulled off the queue. See if we have |
683 | // enough tokens in the bucket to send it out | 674 | // enough tokens in the bucket to send it out |
684 | if (bucket.RemoveTokens(packet.Buffer.DataLength)) | 675 | if(packet.Buffer == null) |
685 | { | 676 | { |
686 | // Send the packet | 677 | // packet canceled elsewhere (by a ack for example) |
687 | m_udpServer.SendPacketFinal(packet); | ||
688 | packetSent = true; | ||
689 | |||
690 | if (queue.Count < 5) | 678 | if (queue.Count < 5) |
691 | emptyCategories |= CategoryToFlag(i); | 679 | emptyCategories |= CategoryToFlag(i); |
692 | } | 680 | } |
693 | else | 681 | else |
694 | { | 682 | { |
695 | // Save the dequeued packet for the next iteration | 683 | if (bucket.RemoveTokens(packet.Buffer.DataLength)) |
696 | m_nextPackets[i] = packet; | 684 | { |
685 | // Send the packet | ||
686 | m_udpServer.SendPacketFinal(packet); | ||
687 | packetSent = true; | ||
688 | |||
689 | if (queue.Count < 5) | ||
690 | emptyCategories |= CategoryToFlag(i); | ||
691 | } | ||
692 | else | ||
693 | { | ||
694 | // Save the dequeued packet for the next iteration | ||
695 | m_nextPackets[i] = packet; | ||
696 | } | ||
697 | } | 697 | } |
698 | |||
699 | } | 698 | } |
700 | else | 699 | else |
701 | { | 700 | { |
@@ -803,8 +802,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
803 | } | 802 | } |
804 | } | 803 | } |
805 | 804 | ||
806 | |||
807 | |||
808 | /// <summary> | 805 | /// <summary> |
809 | /// Fires the OnQueueEmpty callback and sets the minimum time that it | 806 | /// Fires the OnQueueEmpty callback and sets the minimum time that it |
810 | /// can be called again | 807 | /// can be called again |
@@ -843,6 +840,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
843 | return 0; | 840 | return 0; |
844 | } | 841 | } |
845 | 842 | ||
843 | public void FreeUDPBuffer(UDPPacketBuffer buf) | ||
844 | { | ||
845 | m_udpServer.FreeUDPBuffer(buf); | ||
846 | } | ||
847 | |||
846 | /// <summary> | 848 | /// <summary> |
847 | /// Converts a <seealso cref="ThrottleOutPacketType"/> integer to a | 849 | /// Converts a <seealso cref="ThrottleOutPacketType"/> integer to a |
848 | /// flag value | 850 | /// flag value |
@@ -853,34 +855,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
853 | { | 855 | { |
854 | ThrottleOutPacketType category = (ThrottleOutPacketType)i; | 856 | ThrottleOutPacketType category = (ThrottleOutPacketType)i; |
855 | 857 | ||
856 | /* | ||
857 | * Land = 1, | ||
858 | /// <summary>Wind data</summary> | ||
859 | Wind = 2, | ||
860 | /// <summary>Cloud data</summary> | ||
861 | Cloud = 3, | ||
862 | /// <summary>Any packets that do not fit into the other throttles</summary> | ||
863 | Task = 4, | ||
864 | /// <summary>Texture assets</summary> | ||
865 | Texture = 5, | ||
866 | /// <summary>Non-texture assets</summary> | ||
867 | Asset = 6, | ||
868 | */ | ||
869 | |||
870 | switch (category) | 858 | switch (category) |
871 | { | 859 | { |
872 | case ThrottleOutPacketType.Land: | 860 | case ThrottleOutPacketType.Land: |
873 | return ThrottleOutPacketTypeFlags.Land; | 861 | return ThrottleOutPacketTypeFlags.Land; // Terrain data |
874 | case ThrottleOutPacketType.Wind: | 862 | case ThrottleOutPacketType.Wind: |
875 | return ThrottleOutPacketTypeFlags.Wind; | 863 | return ThrottleOutPacketTypeFlags.Wind; // Wind data |
876 | case ThrottleOutPacketType.Cloud: | 864 | case ThrottleOutPacketType.Cloud: |
877 | return ThrottleOutPacketTypeFlags.Cloud; | 865 | return ThrottleOutPacketTypeFlags.Cloud; // Cloud data |
878 | case ThrottleOutPacketType.Task: | 866 | case ThrottleOutPacketType.Task: |
879 | return ThrottleOutPacketTypeFlags.Task; | 867 | return ThrottleOutPacketTypeFlags.Task; // Object updates and everything not on the other categories |
880 | case ThrottleOutPacketType.Texture: | 868 | case ThrottleOutPacketType.Texture: |
881 | return ThrottleOutPacketTypeFlags.Texture; | 869 | return ThrottleOutPacketTypeFlags.Texture; // Textures data (also impacts http texture and mesh by default) |
882 | case ThrottleOutPacketType.Asset: | 870 | case ThrottleOutPacketType.Asset: |
883 | return ThrottleOutPacketTypeFlags.Asset; | 871 | return ThrottleOutPacketTypeFlags.Asset; // Non-texture Assets data |
884 | default: | 872 | default: |
885 | return 0; | 873 | return 0; |
886 | } | 874 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 35d29a5..2300800 100644..100755 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | |||
@@ -256,6 +256,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
256 | 256 | ||
257 | /// <summary>Maximum transmission unit, or UDP packet size, for the LLUDP protocol</summary> | 257 | /// <summary>Maximum transmission unit, or UDP packet size, for the LLUDP protocol</summary> |
258 | public const int MTU = 1400; | 258 | public const int MTU = 1400; |
259 | public const int MAXPAYLOAD = 1250; | ||
259 | 260 | ||
260 | /// <summary>Number of forced client logouts due to no receipt of packets before timeout.</summary> | 261 | /// <summary>Number of forced client logouts due to no receipt of packets before timeout.</summary> |
261 | public int ClientLogoutsDueToNoReceives { get; protected set; } | 262 | public int ClientLogoutsDueToNoReceives { get; protected set; } |
@@ -274,10 +275,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
274 | /// <summary>The measured resolution of Environment.TickCount</summary> | 275 | /// <summary>The measured resolution of Environment.TickCount</summary> |
275 | public readonly float TickCountResolution; | 276 | public readonly float TickCountResolution; |
276 | 277 | ||
277 | /// <summary>Number of prim updates to put on the queue each time the | ||
278 | /// OnQueueEmpty event is triggered for updates</summary> | ||
279 | public readonly int PrimUpdatesPerCallback; | ||
280 | |||
281 | /// <summary>Number of texture packets to put on the queue each time the | 278 | /// <summary>Number of texture packets to put on the queue each time the |
282 | /// OnQueueEmpty event is triggered for textures</summary> | 279 | /// OnQueueEmpty event is triggered for textures</summary> |
283 | public readonly int TextureSendLimit; | 280 | public readonly int TextureSendLimit; |
@@ -344,18 +341,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
344 | 341 | ||
345 | protected ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>> m_pendingCache = new ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>>(); | 342 | protected ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>> m_pendingCache = new ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>>(); |
346 | 343 | ||
347 | protected Pool<IncomingPacket> m_incomingPacketPool; | ||
348 | |||
349 | /// <summary> | ||
350 | /// Stat for number of packets in the main pool awaiting use. | ||
351 | /// </summary> | ||
352 | protected Stat m_poolCountStat; | ||
353 | |||
354 | /// <summary> | ||
355 | /// Stat for number of packets in the inbound packet pool awaiting use. | ||
356 | /// </summary> | ||
357 | protected Stat m_incomingPacketPoolStat; | ||
358 | |||
359 | protected int m_defaultRTO = 0; | 344 | protected int m_defaultRTO = 0; |
360 | protected int m_maxRTO = 0; | 345 | protected int m_maxRTO = 0; |
361 | protected int m_ackTimeout = 0; | 346 | protected int m_ackTimeout = 0; |
@@ -452,7 +437,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
452 | m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0); | 437 | m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0); |
453 | sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0); | 438 | sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0); |
454 | 439 | ||
455 | PrimUpdatesPerCallback = config.GetInt("PrimUpdatesPerCallback", 100); | ||
456 | TextureSendLimit = config.GetInt("TextureSendLimit", 20); | 440 | TextureSendLimit = config.GetInt("TextureSendLimit", 20); |
457 | 441 | ||
458 | m_defaultRTO = config.GetInt("DefaultRTO", 0); | 442 | m_defaultRTO = config.GetInt("DefaultRTO", 0); |
@@ -463,7 +447,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
463 | } | 447 | } |
464 | else | 448 | else |
465 | { | 449 | { |
466 | PrimUpdatesPerCallback = 100; | ||
467 | TextureSendLimit = 20; | 450 | TextureSendLimit = 20; |
468 | m_ackTimeout = 1000 * 60; // 1 minute | 451 | m_ackTimeout = 1000 * 60; // 1 minute |
469 | m_pausedAckTimeout = 1000 * 300; // 5 minutes | 452 | m_pausedAckTimeout = 1000 * 300; // 5 minutes |
@@ -498,7 +481,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
498 | 481 | ||
499 | // if (usePools) | 482 | // if (usePools) |
500 | // EnablePools(); | 483 | // EnablePools(); |
501 | base.DisablePools(); | ||
502 | } | 484 | } |
503 | 485 | ||
504 | public void Start() | 486 | public void Start() |
@@ -554,83 +536,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
554 | OqrEngine.Stop(); | 536 | OqrEngine.Stop(); |
555 | } | 537 | } |
556 | 538 | ||
557 | public override bool EnablePools() | ||
558 | { | ||
559 | if (!UsePools) | ||
560 | { | ||
561 | base.EnablePools(); | ||
562 | |||
563 | m_incomingPacketPool = new Pool<IncomingPacket>(() => new IncomingPacket(), 500); | ||
564 | |||
565 | return true; | ||
566 | } | ||
567 | |||
568 | return false; | ||
569 | } | ||
570 | |||
571 | public override bool DisablePools() | ||
572 | { | ||
573 | if (UsePools) | ||
574 | { | ||
575 | base.DisablePools(); | ||
576 | |||
577 | StatsManager.DeregisterStat(m_incomingPacketPoolStat); | ||
578 | |||
579 | // We won't null out the pool to avoid a race condition with code that may be in the middle of using it. | ||
580 | |||
581 | return true; | ||
582 | } | ||
583 | |||
584 | return false; | ||
585 | } | ||
586 | |||
587 | /// <summary> | ||
588 | /// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene | ||
589 | /// stats. | ||
590 | /// </summary> | ||
591 | protected internal void EnablePoolStats() | ||
592 | { | ||
593 | m_poolCountStat | ||
594 | = new Stat( | ||
595 | "UDPPacketBufferPoolCount", | ||
596 | "Objects within the UDPPacketBuffer pool", | ||
597 | "The number of objects currently stored within the UDPPacketBuffer pool", | ||
598 | "", | ||
599 | "clientstack", | ||
600 | Scene.Name, | ||
601 | StatType.Pull, | ||
602 | stat => stat.Value = Pool.Count, | ||
603 | StatVerbosity.Debug); | ||
604 | |||
605 | StatsManager.RegisterStat(m_poolCountStat); | ||
606 | |||
607 | m_incomingPacketPoolStat | ||
608 | = new Stat( | ||
609 | "IncomingPacketPoolCount", | ||
610 | "Objects within incoming packet pool", | ||
611 | "The number of objects currently stored within the incoming packet pool", | ||
612 | "", | ||
613 | "clientstack", | ||
614 | Scene.Name, | ||
615 | StatType.Pull, | ||
616 | stat => stat.Value = m_incomingPacketPool.Count, | ||
617 | StatVerbosity.Debug); | ||
618 | |||
619 | StatsManager.RegisterStat(m_incomingPacketPoolStat); | ||
620 | } | ||
621 | |||
622 | /// <summary> | ||
623 | /// Disables pool stats. | ||
624 | /// </summary> | ||
625 | protected internal void DisablePoolStats() | ||
626 | { | ||
627 | StatsManager.DeregisterStat(m_poolCountStat); | ||
628 | m_poolCountStat = null; | ||
629 | |||
630 | StatsManager.DeregisterStat(m_incomingPacketPoolStat); | ||
631 | m_incomingPacketPoolStat = null; | ||
632 | } | ||
633 | |||
634 | /// <summary> | 539 | /// <summary> |
635 | /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging. | 540 | /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging. |
636 | /// </summary> | 541 | /// </summary> |
@@ -658,8 +563,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
658 | string.Format("Incoming Packet Async Handling Engine ({0})", Scene.Name), | 563 | string.Format("Incoming Packet Async Handling Engine ({0})", Scene.Name), |
659 | "INCOMING PACKET ASYNC HANDLING ENGINE"); | 564 | "INCOMING PACKET ASYNC HANDLING ENGINE"); |
660 | */ | 565 | */ |
661 | OqrEngine | 566 | OqrEngine = new JobEngine( |
662 | = new JobEngine( | ||
663 | string.Format("Outgoing Queue Refill Engine ({0})", Scene.Name), | 567 | string.Format("Outgoing Queue Refill Engine ({0})", Scene.Name), |
664 | "OUTGOING QUEUE REFILL ENGINE"); | 568 | "OUTGOING QUEUE REFILL ENGINE"); |
665 | 569 | ||
@@ -769,15 +673,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
769 | stat => stat.Value = OqrEngine.JobsWaiting, | 673 | stat => stat.Value = OqrEngine.JobsWaiting, |
770 | StatVerbosity.Debug)); | 674 | StatVerbosity.Debug)); |
771 | 675 | ||
772 | // We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by | 676 | StatsManager.RegisterStat( |
773 | // scene name | 677 | new Stat( |
774 | if (UsePools) | 678 | "UDPBuffersPoolCount", |
775 | EnablePoolStats(); | 679 | "Buffers in the UDP buffers pool", |
776 | 680 | "The number of buffers currently stored within the UDP buffers pool", | |
681 | "", | ||
682 | "clientstack", | ||
683 | Scene.Name, | ||
684 | StatType.Pull, | ||
685 | stat => stat.Value = m_udpBuffersPoolPtr + 1, | ||
686 | StatVerbosity.Debug)); | ||
777 | 687 | ||
778 | LLUDPServerCommands commands = new LLUDPServerCommands(MainConsole.Instance, this); | 688 | LLUDPServerCommands commands = new LLUDPServerCommands(MainConsole.Instance, this); |
779 | commands.Register(); | 689 | commands.Register(); |
780 | |||
781 | } | 690 | } |
782 | 691 | ||
783 | public bool HandlesRegion(Location x) | 692 | public bool HandlesRegion(Location x) |
@@ -939,9 +848,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
939 | // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting | 848 | // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting |
940 | // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here | 849 | // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here |
941 | // to accomodate for both common scenarios and provide ample room for ACK appending in both | 850 | // to accomodate for both common scenarios and provide ample room for ACK appending in both |
942 | int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; | 851 | //int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; |
943 | 852 | ||
944 | UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); | 853 | //UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); |
854 | UDPPacketBuffer buffer = GetNewUDPBuffer(udpClient.RemoteEndPoint); | ||
945 | 855 | ||
946 | // Zerocode if needed | 856 | // Zerocode if needed |
947 | if (doZerocode) | 857 | if (doZerocode) |
@@ -971,7 +881,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
971 | // If the packet data wasn't already copied during zerocoding, copy it now | 881 | // If the packet data wasn't already copied during zerocoding, copy it now |
972 | if (doCopy) | 882 | if (doCopy) |
973 | { | 883 | { |
974 | if (dataLength <= buffer.Data.Length) | 884 | //if (dataLength <= buffer.Data.Length) |
885 | if (dataLength <= LLUDPServer.MTU) | ||
975 | { | 886 | { |
976 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | 887 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); |
977 | } | 888 | } |
@@ -979,7 +890,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
979 | { | 890 | { |
980 | m_log.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + | 891 | m_log.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + |
981 | type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length); | 892 | type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length); |
982 | buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, dataLength); | 893 | // buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, dataLength); |
894 | buffer = GetNewUDPBuffer(udpClient.RemoteEndPoint); | ||
983 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | 895 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); |
984 | } | 896 | } |
985 | } | 897 | } |
@@ -1017,6 +929,89 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1017 | #endregion Queue or Send | 929 | #endregion Queue or Send |
1018 | } | 930 | } |
1019 | 931 | ||
932 | public unsafe UDPPacketBuffer ZeroEncode(UDPPacketBuffer input) | ||
933 | { | ||
934 | UDPPacketBuffer zb = GetNewUDPBuffer(null); | ||
935 | int srclen = input.DataLength; | ||
936 | byte[] src = input.Data; | ||
937 | byte[] dest = zb.Data; | ||
938 | |||
939 | int zerolen = 6; | ||
940 | byte zerocount = 0; | ||
941 | |||
942 | for (int i = zerolen; i < srclen; i++) | ||
943 | { | ||
944 | if (src[i] == 0x00) | ||
945 | { | ||
946 | zerocount++; | ||
947 | if (zerocount == 0) | ||
948 | { | ||
949 | dest[zerolen++] = 0x00; | ||
950 | dest[zerolen++] = 0xff; | ||
951 | zerocount++; | ||
952 | } | ||
953 | } | ||
954 | else | ||
955 | { | ||
956 | if (zerocount != 0) | ||
957 | { | ||
958 | dest[zerolen++] = 0x00; | ||
959 | dest[zerolen++] = zerocount; | ||
960 | zerocount = 0; | ||
961 | } | ||
962 | |||
963 | dest[zerolen++] = src[i]; | ||
964 | } | ||
965 | } | ||
966 | |||
967 | if (zerocount != 0) | ||
968 | { | ||
969 | dest[zerolen++] = 0x00; | ||
970 | dest[zerolen++] = zerocount; | ||
971 | } | ||
972 | |||
973 | if(zerolen >= srclen) | ||
974 | { | ||
975 | FreeUDPBuffer(zb); | ||
976 | |||
977 | src[0] &= unchecked((byte)~Helpers.MSG_ZEROCODED); | ||
978 | return input; | ||
979 | } | ||
980 | |||
981 | Buffer.BlockCopy(src, 0, dest, 0, 6); | ||
982 | |||
983 | zb.RemoteEndPoint = input.RemoteEndPoint; | ||
984 | zb.DataLength = zerolen; | ||
985 | |||
986 | FreeUDPBuffer(input); | ||
987 | |||
988 | return zb; | ||
989 | } | ||
990 | |||
991 | public void SendUDPPacket( | ||
992 | LLUDPClient udpClient, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod method, bool forcequeue, bool zerocode) | ||
993 | { | ||
994 | bool highPriority = false; | ||
995 | |||
996 | if(zerocode) | ||
997 | buffer = ZeroEncode(buffer); | ||
998 | |||
999 | if (category != ThrottleOutPacketType.Unknown && (category & ThrottleOutPacketType.HighPriority) != 0) | ||
1000 | { | ||
1001 | category = (ThrottleOutPacketType)((int)category & 127); | ||
1002 | highPriority = true; | ||
1003 | } | ||
1004 | |||
1005 | OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null); | ||
1006 | |||
1007 | // If we were not provided a method for handling unacked, use the UDPServer default method | ||
1008 | if ((outgoingPacket.Buffer.Data[0] & Helpers.MSG_RELIABLE) != 0) | ||
1009 | outgoingPacket.UnackedMethod = ((method == null) ? delegate (OutgoingPacket oPacket) { ResendUnacked(oPacket); } : method); | ||
1010 | |||
1011 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, forcequeue, highPriority)) | ||
1012 | SendPacketFinal(outgoingPacket); | ||
1013 | } | ||
1014 | |||
1020 | public void SendAcks(LLUDPClient udpClient) | 1015 | public void SendAcks(LLUDPClient udpClient) |
1021 | { | 1016 | { |
1022 | uint ack; | 1017 | uint ack; |
@@ -1066,7 +1061,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1066 | { | 1061 | { |
1067 | LLUDPClient udpClient = client.UDPClient; | 1062 | LLUDPClient udpClient = client.UDPClient; |
1068 | 1063 | ||
1069 | if (!udpClient.IsConnected) | 1064 | if (!client.IsActive || !udpClient.IsConnected) |
1070 | return; | 1065 | return; |
1071 | 1066 | ||
1072 | // Disconnect an agent if no packets are received for some time | 1067 | // Disconnect an agent if no packets are received for some time |
@@ -1136,14 +1131,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1136 | internal void SendPacketFinal(OutgoingPacket outgoingPacket) | 1131 | internal void SendPacketFinal(OutgoingPacket outgoingPacket) |
1137 | { | 1132 | { |
1138 | UDPPacketBuffer buffer = outgoingPacket.Buffer; | 1133 | UDPPacketBuffer buffer = outgoingPacket.Buffer; |
1134 | if(buffer == null) // canceled packet | ||
1135 | return; | ||
1136 | LLUDPClient udpClient = outgoingPacket.Client; | ||
1137 | if (!udpClient.IsConnected) | ||
1138 | return; | ||
1139 | |||
1139 | byte flags = buffer.Data[0]; | 1140 | byte flags = buffer.Data[0]; |
1140 | bool isResend = (flags & Helpers.MSG_RESENT) != 0; | 1141 | bool isResend = (flags & Helpers.MSG_RESENT) != 0; |
1141 | bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; | 1142 | bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; |
1142 | bool isZerocoded = (flags & Helpers.MSG_ZEROCODED) != 0; | 1143 | bool isZerocoded = (flags & Helpers.MSG_ZEROCODED) != 0; |
1143 | LLUDPClient udpClient = outgoingPacket.Client; | ||
1144 | |||
1145 | if (!udpClient.IsConnected) | ||
1146 | return; | ||
1147 | 1144 | ||
1148 | int dataLength = buffer.DataLength; | 1145 | int dataLength = buffer.DataLength; |
1149 | 1146 | ||
@@ -1154,7 +1151,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1154 | // no more ACKs to append | 1151 | // no more ACKs to append |
1155 | int ackCount = 0; | 1152 | int ackCount = 0; |
1156 | uint ack; | 1153 | uint ack; |
1157 | while (dataLength + 5 < buffer.Data.Length && ackCount < 256 && udpClient.PendingAcks.Dequeue(out ack)) | 1154 | while (dataLength + 5 < MTU && ackCount < 256 && udpClient.PendingAcks.Dequeue(out ack)) |
1158 | { | 1155 | { |
1159 | Utils.UIntToBytesBig(ack, buffer.Data, dataLength); | 1156 | Utils.UIntToBytesBig(ack, buffer.Data, dataLength); |
1160 | dataLength += 4; | 1157 | dataLength += 4; |
@@ -1168,22 +1165,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1168 | // Set the appended ACKs flag on this packet | 1165 | // Set the appended ACKs flag on this packet |
1169 | buffer.Data[0] = (byte)(buffer.Data[0] | Helpers.MSG_APPENDED_ACKS); | 1166 | buffer.Data[0] = (byte)(buffer.Data[0] | Helpers.MSG_APPENDED_ACKS); |
1170 | } | 1167 | } |
1168 | buffer.DataLength = dataLength; | ||
1171 | } | 1169 | } |
1172 | 1170 | ||
1173 | buffer.DataLength = dataLength; | ||
1174 | |||
1175 | if (!isResend) | 1171 | if (!isResend) |
1176 | { | 1172 | { |
1177 | // Not a resend, assign a new sequence number | 1173 | // Not a resend, assign a new sequence number |
1178 | uint sequenceNumber = (uint)Interlocked.Increment(ref udpClient.CurrentSequence); | 1174 | uint sequenceNumber = (uint)Interlocked.Increment(ref udpClient.CurrentSequence); |
1179 | Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); | 1175 | Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); |
1180 | outgoingPacket.SequenceNumber = sequenceNumber; | 1176 | outgoingPacket.SequenceNumber = sequenceNumber; |
1181 | |||
1182 | if (isReliable) | ||
1183 | { | ||
1184 | // Add this packet to the list of ACK responses we are waiting on from the server | ||
1185 | udpClient.NeedAcks.Add(outgoingPacket); | ||
1186 | } | ||
1187 | } | 1177 | } |
1188 | else | 1178 | else |
1189 | { | 1179 | { |
@@ -1196,9 +1186,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1196 | PacketsSentCount++; | 1186 | PacketsSentCount++; |
1197 | 1187 | ||
1198 | SyncSend(buffer); | 1188 | SyncSend(buffer); |
1189 | |||
1199 | // Keep track of when this packet was sent out (right now) | 1190 | // Keep track of when this packet was sent out (right now) |
1200 | outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; | 1191 | outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; |
1201 | 1192 | ||
1193 | if (outgoingPacket.UnackedMethod == null) | ||
1194 | FreeUDPBuffer(buffer); | ||
1195 | else if(!isResend) | ||
1196 | { | ||
1197 | // Add this packet to the list of ACK responses we are waiting on from the server | ||
1198 | udpClient.NeedAcks.Add(outgoingPacket); | ||
1199 | } | ||
1200 | |||
1202 | if (udpClient.DebugDataOutLevel > 0) | 1201 | if (udpClient.DebugDataOutLevel > 0) |
1203 | m_log.DebugFormat( | 1202 | m_log.DebugFormat( |
1204 | "[LLUDPSERVER]: Sending packet #{0} (rel: {1}, res: {2}) to {3} from {4}", | 1203 | "[LLUDPSERVER]: Sending packet #{0} (rel: {1}, res: {2}) to {3} from {4}", |
@@ -1240,7 +1239,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1240 | // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); | 1239 | // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); |
1241 | 1240 | ||
1242 | RecordMalformedInboundPacket(endPoint); | 1241 | RecordMalformedInboundPacket(endPoint); |
1243 | 1242 | FreeUDPBuffer(buffer); | |
1244 | return; // Drop undersized packet | 1243 | return; // Drop undersized packet |
1245 | } | 1244 | } |
1246 | 1245 | ||
@@ -1260,21 +1259,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1260 | // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); | 1259 | // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); |
1261 | 1260 | ||
1262 | RecordMalformedInboundPacket(endPoint); | 1261 | RecordMalformedInboundPacket(endPoint); |
1263 | 1262 | FreeUDPBuffer(buffer); | |
1264 | return; // Malformed header | 1263 | return; // Malformed header |
1265 | } | 1264 | } |
1266 | 1265 | ||
1267 | try | 1266 | try |
1268 | { | 1267 | { |
1269 | // packet = Packet.BuildPacket(buffer.Data, ref packetEnd, | 1268 | // get a buffer for zero decode using the udp buffers pool |
1270 | // // Only allocate a buffer for zerodecoding if the packet is zerocoded | 1269 | UDPPacketBuffer zerodecodebufferholder = null; |
1271 | // ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); | 1270 | byte[] zerodecodebuffer = null; |
1271 | // only if needed | ||
1272 | if (((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0)) | ||
1273 | { | ||
1274 | zerodecodebufferholder = GetNewUDPBuffer(null); | ||
1275 | zerodecodebuffer = zerodecodebufferholder.Data; | ||
1276 | } | ||
1277 | |||
1278 | packet = Packet.BuildPacket(buffer.Data, ref packetEnd, zerodecodebuffer); | ||
1272 | // If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we | 1279 | // If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we |
1273 | // assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all | 1280 | // assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all |
1274 | // bytes are copied out). | 1281 | // bytes are copied out). |
1275 | packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd, | 1282 | // packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd, zerodecodebuffer); |
1276 | // Only allocate a buffer for zerodecoding if the packet is zerocoded | 1283 | if(zerodecodebufferholder != null) |
1277 | ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); | 1284 | FreeUDPBuffer(zerodecodebufferholder); |
1278 | } | 1285 | } |
1279 | catch (Exception e) | 1286 | catch (Exception e) |
1280 | { | 1287 | { |
@@ -1292,7 +1299,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1292 | } | 1299 | } |
1293 | 1300 | ||
1294 | RecordMalformedInboundPacket(endPoint); | 1301 | RecordMalformedInboundPacket(endPoint); |
1295 | 1302 | FreeUDPBuffer(buffer); | |
1296 | return; | 1303 | return; |
1297 | } | 1304 | } |
1298 | 1305 | ||
@@ -1311,17 +1318,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1311 | lock (m_pendingCache) | 1318 | lock (m_pendingCache) |
1312 | { | 1319 | { |
1313 | if (m_pendingCache.Contains(endPoint)) | 1320 | if (m_pendingCache.Contains(endPoint)) |
1321 | { | ||
1322 | FreeUDPBuffer(buffer); | ||
1314 | return; | 1323 | return; |
1324 | } | ||
1315 | 1325 | ||
1316 | m_pendingCache.AddOrUpdate(endPoint, new Queue<UDPPacketBuffer>(), 60); | 1326 | m_pendingCache.AddOrUpdate(endPoint, new Queue<UDPPacketBuffer>(), 60); |
1317 | } | 1327 | } |
1318 | 1328 | ||
1319 | // We need to copy the endpoint so that it doesn't get changed when another thread reuses the | 1329 | Util.FireAndForget(HandleUseCircuitCode, new object[] { endPoint, packet }); |
1320 | // buffer. | 1330 | FreeUDPBuffer(buffer); |
1321 | object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; | ||
1322 | |||
1323 | Util.FireAndForget(HandleUseCircuitCode, array); | ||
1324 | |||
1325 | return; | 1331 | return; |
1326 | } | 1332 | } |
1327 | } | 1333 | } |
@@ -1336,24 +1342,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1336 | queue.Enqueue(buffer); | 1342 | queue.Enqueue(buffer); |
1337 | return; | 1343 | return; |
1338 | } | 1344 | } |
1339 | |||
1340 | /* | ||
1341 | else if (packet.Type == PacketType.CompleteAgentMovement) | ||
1342 | { | ||
1343 | // Send ack straight away to let the viewer know that we got it. | ||
1344 | SendAckImmediate(endPoint, packet.Header.Sequence); | ||
1345 | |||
1346 | // We need to copy the endpoint so that it doesn't get changed when another thread reuses the | ||
1347 | // buffer. | ||
1348 | object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; | ||
1349 | |||
1350 | Util.FireAndForget(HandleCompleteMovementIntoRegion, array); | ||
1351 | |||
1352 | return; | ||
1353 | } | ||
1354 | */ | ||
1355 | } | 1345 | } |
1356 | 1346 | ||
1347 | FreeUDPBuffer(buffer); | ||
1348 | |||
1357 | // Determine which agent this packet came from | 1349 | // Determine which agent this packet came from |
1358 | if (client == null || !(client is LLClientView)) | 1350 | if (client == null || !(client is LLClientView)) |
1359 | { | 1351 | { |
@@ -1471,10 +1463,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1471 | LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); | 1463 | LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); |
1472 | #endregion BinaryStats | 1464 | #endregion BinaryStats |
1473 | 1465 | ||
1474 | |||
1475 | //AgentUpdate removed from here | ||
1476 | |||
1477 | |||
1478 | #region Ping Check Handling | 1466 | #region Ping Check Handling |
1479 | 1467 | ||
1480 | if (packet.Type == PacketType.StartPingCheck) | 1468 | if (packet.Type == PacketType.StartPingCheck) |
@@ -1506,17 +1494,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1506 | 1494 | ||
1507 | IncomingPacket incomingPacket; | 1495 | IncomingPacket incomingPacket; |
1508 | 1496 | ||
1509 | // Inbox insertion | 1497 | incomingPacket = new IncomingPacket((LLClientView)client, packet); |
1510 | if (UsePools) | ||
1511 | { | ||
1512 | incomingPacket = m_incomingPacketPool.GetObject(); | ||
1513 | incomingPacket.Client = (LLClientView)client; | ||
1514 | incomingPacket.Packet = packet; | ||
1515 | } | ||
1516 | else | ||
1517 | { | ||
1518 | incomingPacket = new IncomingPacket((LLClientView)client, packet); | ||
1519 | } | ||
1520 | 1498 | ||
1521 | // if (incomingPacket.Packet.Type == PacketType.AgentUpdate || | 1499 | // if (incomingPacket.Packet.Type == PacketType.AgentUpdate || |
1522 | // incomingPacket.Packet.Type == PacketType.ChatFromViewer) | 1500 | // incomingPacket.Packet.Type == PacketType.ChatFromViewer) |
@@ -1525,7 +1503,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1525 | // else | 1503 | // else |
1526 | // packetInbox.Enqueue(incomingPacket); | 1504 | // packetInbox.Enqueue(incomingPacket); |
1527 | packetInbox.Add(incomingPacket); | 1505 | packetInbox.Add(incomingPacket); |
1528 | |||
1529 | } | 1506 | } |
1530 | 1507 | ||
1531 | #region BinaryStats | 1508 | #region BinaryStats |
@@ -1685,7 +1662,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1685 | { | 1662 | { |
1686 | m_log.DebugFormat("[LLUDPSERVER]: Client created but no pending queue present"); | 1663 | m_log.DebugFormat("[LLUDPSERVER]: Client created but no pending queue present"); |
1687 | return; | 1664 | return; |
1688 | |||
1689 | } | 1665 | } |
1690 | m_pendingCache.Remove(endPoint); | 1666 | m_pendingCache.Remove(endPoint); |
1691 | } | 1667 | } |
@@ -1881,13 +1857,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1881 | byte[] packetData = ack.ToBytes(); | 1857 | byte[] packetData = ack.ToBytes(); |
1882 | int length = packetData.Length; | 1858 | int length = packetData.Length; |
1883 | 1859 | ||
1884 | UDPPacketBuffer buffer = new UDPPacketBuffer(remoteEndpoint, length); | 1860 | UDPPacketBuffer buffer = GetNewUDPBuffer(remoteEndpoint); |
1885 | buffer.DataLength = length; | 1861 | buffer.DataLength = length; |
1886 | 1862 | ||
1887 | Buffer.BlockCopy(packetData, 0, buffer.Data, 0, length); | 1863 | Buffer.BlockCopy(packetData, 0, buffer.Data, 0, length); |
1888 | 1864 | ||
1889 | // AsyncBeginSend(buffer); | 1865 | // AsyncBeginSend(buffer); |
1890 | SyncSend(buffer); | 1866 | SyncSend(buffer); |
1867 | FreeUDPBuffer(buffer); | ||
1891 | } | 1868 | } |
1892 | 1869 | ||
1893 | protected bool IsClientAuthorized(UseCircuitCodePacket useCircuitCode, out AuthenticateResponse sessionInfo) | 1870 | protected bool IsClientAuthorized(UseCircuitCodePacket useCircuitCode, out AuthenticateResponse sessionInfo) |
@@ -1982,21 +1959,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1982 | Scene.ThreadAlive(1); | 1959 | Scene.ThreadAlive(1); |
1983 | try | 1960 | try |
1984 | { | 1961 | { |
1985 | packetInbox.TryTake(out incomingPacket, 250); | 1962 | packetInbox.TryTake(out incomingPacket, 4500); |
1986 | 1963 | ||
1987 | if (incomingPacket != null && IsRunningInbound) | 1964 | if (incomingPacket != null && IsRunningInbound) |
1988 | { | 1965 | { |
1989 | ProcessInPacket(incomingPacket); | 1966 | ProcessInPacket(incomingPacket); |
1990 | |||
1991 | if (UsePools) | ||
1992 | { | ||
1993 | incomingPacket.Client = null; | ||
1994 | m_incomingPacketPool.ReturnObject(incomingPacket); | ||
1995 | } | ||
1996 | incomingPacket = null; | 1967 | incomingPacket = null; |
1997 | } | 1968 | } |
1998 | } | 1969 | } |
1999 | catch(Exception ex) | 1970 | catch (ThreadAbortException) |
1971 | { | ||
1972 | Thread.ResetAbort(); | ||
1973 | } | ||
1974 | catch (Exception ex) | ||
2000 | { | 1975 | { |
2001 | m_log.Error("[LLUDPSERVER]: Error in the incoming packet handler loop: " + ex.Message, ex); | 1976 | m_log.Error("[LLUDPSERVER]: Error in the incoming packet handler loop: " + ex.Message, ex); |
2002 | } | 1977 | } |
@@ -2025,7 +2000,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2025 | { | 2000 | { |
2026 | Scene.ThreadAlive(2); | 2001 | Scene.ThreadAlive(2); |
2027 | 2002 | ||
2028 | |||
2029 | try | 2003 | try |
2030 | { | 2004 | { |
2031 | m_packetSent = false; | 2005 | m_packetSent = false; |
@@ -2080,7 +2054,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2080 | } | 2054 | } |
2081 | else if (!m_packetSent) | 2055 | else if (!m_packetSent) |
2082 | // Thread.Sleep((int)TickCountResolution); outch this is bad on linux | 2056 | // Thread.Sleep((int)TickCountResolution); outch this is bad on linux |
2083 | Thread.Sleep(15); // match the 16ms of windows7, dont ask 16 or win may decide to do 32ms. | 2057 | Thread.Sleep(15); // match the 16ms of windows, dont ask 16 or win may decide to do 32ms. |
2084 | 2058 | ||
2085 | Watchdog.UpdateThread(); | 2059 | Watchdog.UpdateThread(); |
2086 | } | 2060 | } |
@@ -2104,14 +2078,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2104 | 2078 | ||
2105 | if (udpClient.IsConnected) | 2079 | if (udpClient.IsConnected) |
2106 | { | 2080 | { |
2107 | if (m_resendUnacked) | 2081 | if (client.IsActive && m_resendUnacked) |
2108 | HandleUnacked(llClient); | 2082 | HandleUnacked(llClient); |
2109 | 2083 | ||
2110 | if (m_sendAcks) | 2084 | if (client.IsActive) |
2111 | SendAcks(udpClient); | 2085 | { |
2086 | if (m_sendAcks) | ||
2087 | SendAcks(udpClient); | ||
2112 | 2088 | ||
2113 | if (m_sendPing) | 2089 | if (m_sendPing) |
2114 | SendPing(udpClient); | 2090 | SendPing(udpClient); |
2091 | } | ||
2115 | 2092 | ||
2116 | // Dequeue any outgoing packets that are within the throttle limits | 2093 | // Dequeue any outgoing packets that are within the throttle limits |
2117 | if (udpClient.DequeueOutgoing()) | 2094 | if (udpClient.DequeueOutgoing()) |
@@ -2124,7 +2101,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2124 | m_log.Error( | 2101 | m_log.Error( |
2125 | string.Format("[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw ", client.Name), ex); | 2102 | string.Format("[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw ", client.Name), ex); |
2126 | } | 2103 | } |
2127 | client = null; | ||
2128 | } | 2104 | } |
2129 | 2105 | ||
2130 | #region Emergency Monitoring | 2106 | #region Emergency Monitoring |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs index 012a57d..a4d7eb9 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs | |||
@@ -777,41 +777,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
777 | m_udpServer.StopOutbound(); | 777 | m_udpServer.StopOutbound(); |
778 | } | 778 | } |
779 | 779 | ||
780 | private void HandlePoolCommand(string module, string[] args) | ||
781 | { | ||
782 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
783 | return; | ||
784 | |||
785 | if (args.Length != 4) | ||
786 | { | ||
787 | MainConsole.Instance.Output("Usage: debug lludp pool <on|off>"); | ||
788 | return; | ||
789 | } | ||
790 | |||
791 | string enabled = args[3]; | ||
792 | |||
793 | if (enabled == "on") | ||
794 | { | ||
795 | if (m_udpServer.EnablePools()) | ||
796 | { | ||
797 | m_udpServer.EnablePoolStats(); | ||
798 | MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", m_udpServer.Scene.Name); | ||
799 | } | ||
800 | } | ||
801 | else if (enabled == "off") | ||
802 | { | ||
803 | if (m_udpServer.DisablePools()) | ||
804 | { | ||
805 | m_udpServer.DisablePoolStats(); | ||
806 | MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", m_udpServer.Scene.Name); | ||
807 | } | ||
808 | } | ||
809 | else | ||
810 | { | ||
811 | MainConsole.Instance.Output("Usage: debug lludp pool <on|off>"); | ||
812 | } | ||
813 | } | ||
814 | |||
815 | private void HandleAgentUpdateCommand(string module, string[] args) | 780 | private void HandleAgentUpdateCommand(string module, string[] args) |
816 | { | 781 | { |
817 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | 782 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) |
@@ -834,8 +799,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
834 | MainConsole.Instance.OutputFormat( | 799 | MainConsole.Instance.OutputFormat( |
835 | "OUT LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningOutbound ? "enabled" : "disabled"); | 800 | "OUT LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningOutbound ? "enabled" : "disabled"); |
836 | 801 | ||
837 | MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_udpServer.Scene.Name, m_udpServer.UsePools ? "on" : "off"); | ||
838 | |||
839 | MainConsole.Instance.OutputFormat( | 802 | MainConsole.Instance.OutputFormat( |
840 | "Packet debug level for new clients is {0}", m_udpServer.DefaultClientPacketDebugLevel); | 803 | "Packet debug level for new clients is {0}", m_udpServer.DefaultClientPacketDebugLevel); |
841 | } | 804 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs new file mode 100644 index 0000000..8ed2cf1 --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs | |||
@@ -0,0 +1,279 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
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. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using OpenSim.Framework; | ||
30 | using Nini.Config; | ||
31 | using OpenMetaverse; | ||
32 | |||
33 | namespace OpenSim.Region.ClientStack.LindenUDP | ||
34 | { | ||
35 | public sealed class LLUDPZeroEncoder | ||
36 | { | ||
37 | private byte[] m_tmp = new byte[16]; | ||
38 | private byte[] m_dest; | ||
39 | private int zerocount; | ||
40 | private int pos; | ||
41 | |||
42 | public LLUDPZeroEncoder() | ||
43 | { | ||
44 | } | ||
45 | |||
46 | public LLUDPZeroEncoder(byte[] data) | ||
47 | { | ||
48 | m_dest = data; | ||
49 | zerocount = 0; | ||
50 | } | ||
51 | |||
52 | public byte[] Data | ||
53 | { | ||
54 | get | ||
55 | { | ||
56 | return m_dest; | ||
57 | } | ||
58 | set | ||
59 | { | ||
60 | m_dest = value; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | public int ZeroCount | ||
65 | { | ||
66 | get | ||
67 | { | ||
68 | return zerocount; | ||
69 | } | ||
70 | set | ||
71 | { | ||
72 | zerocount = value; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | public int Position | ||
77 | { | ||
78 | get | ||
79 | { | ||
80 | return pos; | ||
81 | } | ||
82 | set | ||
83 | { | ||
84 | pos = value; | ||
85 | } | ||
86 | } | ||
87 | |||
88 | public unsafe void AddZeros(int len) | ||
89 | { | ||
90 | zerocount += len; | ||
91 | while (zerocount > 255) | ||
92 | { | ||
93 | m_dest[pos++] = 0x00; | ||
94 | m_dest[pos++] = 0xff; | ||
95 | zerocount -= 256; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | public unsafe int Finish() | ||
100 | { | ||
101 | if(zerocount > 0) | ||
102 | { | ||
103 | m_dest[pos++] = 0x00; | ||
104 | m_dest[pos++] = (byte)zerocount; | ||
105 | } | ||
106 | return pos; | ||
107 | } | ||
108 | |||
109 | public unsafe void AddBytes(byte[] src, int srclen) | ||
110 | { | ||
111 | for (int i = 0; i < srclen; ++i) | ||
112 | { | ||
113 | if (src[i] == 0x00) | ||
114 | { | ||
115 | zerocount++; | ||
116 | if (zerocount == 0) | ||
117 | { | ||
118 | m_dest[pos++] = 0x00; | ||
119 | m_dest[pos++] = 0xff; | ||
120 | zerocount++; | ||
121 | } | ||
122 | } | ||
123 | else | ||
124 | { | ||
125 | if (zerocount != 0) | ||
126 | { | ||
127 | m_dest[pos++] = 0x00; | ||
128 | m_dest[pos++] = (byte)zerocount; | ||
129 | zerocount = 0; | ||
130 | } | ||
131 | |||
132 | m_dest[pos++] = src[i]; | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | public unsafe void AddByte(byte v) | ||
138 | { | ||
139 | if (v == 0x00) | ||
140 | { | ||
141 | zerocount++; | ||
142 | if (zerocount == 0) | ||
143 | { | ||
144 | m_dest[pos++] = 0x00; | ||
145 | m_dest[pos++] = 0xff; | ||
146 | zerocount++; | ||
147 | } | ||
148 | } | ||
149 | else | ||
150 | { | ||
151 | if (zerocount != 0) | ||
152 | { | ||
153 | m_dest[pos++] = 0x00; | ||
154 | m_dest[pos++] = (byte)zerocount; | ||
155 | zerocount = 0; | ||
156 | } | ||
157 | |||
158 | m_dest[pos++] = v; | ||
159 | } | ||
160 | } | ||
161 | |||
162 | public void AddInt16(short v) | ||
163 | { | ||
164 | if (v == 0) | ||
165 | AddZeros(2); | ||
166 | else | ||
167 | { | ||
168 | Utils.Int16ToBytes(v, m_tmp, 0); | ||
169 | AddBytes(m_tmp, 2); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | public void AddUInt16(ushort v) | ||
174 | { | ||
175 | if (v == 0) | ||
176 | AddZeros(2); | ||
177 | else | ||
178 | { | ||
179 | Utils.UInt16ToBytes(v, m_tmp, 0); | ||
180 | AddBytes(m_tmp, 2); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | public void AddInt(int v) | ||
185 | { | ||
186 | if (v == 0) | ||
187 | AddZeros(4); | ||
188 | else | ||
189 | { | ||
190 | Utils.IntToBytesSafepos(v, m_tmp, 0); | ||
191 | AddBytes(m_tmp, 4); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | public unsafe void AddUInt(uint v) | ||
196 | { | ||
197 | if (v == 0) | ||
198 | AddZeros(4); | ||
199 | else | ||
200 | { | ||
201 | Utils.UIntToBytesSafepos(v, m_tmp, 0); | ||
202 | AddBytes(m_tmp, 4); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | public void AddFloatToUInt16(float v, float range) | ||
207 | { | ||
208 | Utils.FloatToUInt16Bytes(v, range, m_tmp, 0); | ||
209 | AddBytes(m_tmp, 2); | ||
210 | } | ||
211 | |||
212 | public void AddFloat(float v) | ||
213 | { | ||
214 | if (v == 0f) | ||
215 | AddZeros(4); | ||
216 | else | ||
217 | { | ||
218 | Utils.FloatToBytesSafepos(v, m_tmp, 0); | ||
219 | AddBytes(m_tmp, 4); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | public void AddInt64(long v) | ||
224 | { | ||
225 | if (v == 0) | ||
226 | AddZeros(8); | ||
227 | else | ||
228 | { | ||
229 | Utils.Int64ToBytesSafepos(v, m_tmp, 0); | ||
230 | AddBytes(m_tmp, 8); | ||
231 | } | ||
232 | } | ||
233 | |||
234 | public void AddUInt64(ulong v) | ||
235 | { | ||
236 | if (v == 0) | ||
237 | AddZeros(8); | ||
238 | else | ||
239 | { | ||
240 | Utils.UInt64ToBytesSafepos(v, m_tmp, 0); | ||
241 | AddBytes(m_tmp, 8); | ||
242 | } | ||
243 | } | ||
244 | |||
245 | public void AddVector3(Vector3 v) | ||
246 | { | ||
247 | if (v == Vector3.Zero) | ||
248 | AddZeros(12); | ||
249 | else | ||
250 | { | ||
251 | v.ToBytes(m_tmp, 0); | ||
252 | AddBytes(m_tmp, 12); | ||
253 | } | ||
254 | } | ||
255 | |||
256 | public void AddVector4(Vector4 v) | ||
257 | { | ||
258 | if (v == Vector4.Zero) | ||
259 | AddZeros(16); | ||
260 | else | ||
261 | { | ||
262 | v.ToBytes(m_tmp, 0); | ||
263 | AddBytes(m_tmp, 16); | ||
264 | } | ||
265 | } | ||
266 | |||
267 | public void AddNormQuat(Quaternion v) | ||
268 | { | ||
269 | v.ToBytes(m_tmp, 0); | ||
270 | AddBytes(m_tmp, 12); | ||
271 | } | ||
272 | |||
273 | public void AddUUID(UUID v) | ||
274 | { | ||
275 | v.ToBytes(m_tmp, 0); | ||
276 | AddBytes(m_tmp, 16); | ||
277 | } | ||
278 | } | ||
279 | } | ||
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index f362b06..49aca3c 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs | |||
@@ -26,6 +26,7 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections.Concurrent; | ||
29 | using System.Net; | 30 | using System.Net; |
30 | using System.Net.Sockets; | 31 | using System.Net.Sockets; |
31 | using System.Threading; | 32 | using System.Threading; |
@@ -57,15 +58,9 @@ namespace OpenMetaverse | |||
57 | /// <summary>UDP socket, used in either client or server mode</summary> | 58 | /// <summary>UDP socket, used in either client or server mode</summary> |
58 | private Socket m_udpSocket; | 59 | private Socket m_udpSocket; |
59 | 60 | ||
60 | /// <summary> | 61 | public static Object m_udpBuffersPoolLock = new Object(); |
61 | /// Are we to use object pool(s) to reduce memory churn when receiving data? | 62 | public static UDPPacketBuffer[] m_udpBuffersPool = new UDPPacketBuffer[1000]; |
62 | /// </summary> | 63 | public static int m_udpBuffersPoolPtr = -1; |
63 | public bool UsePools { get; protected set; } | ||
64 | |||
65 | /// <summary> | ||
66 | /// Pool to use for handling data. May be null if UsePools = false; | ||
67 | /// </summary> | ||
68 | protected OpenSim.Framework.Pool<UDPPacketBuffer> Pool { get; private set; } | ||
69 | 64 | ||
70 | /// <summary>Returns true if the server is currently listening for inbound packets, otherwise false</summary> | 65 | /// <summary>Returns true if the server is currently listening for inbound packets, otherwise false</summary> |
71 | public bool IsRunningInbound { get; private set; } | 66 | public bool IsRunningInbound { get; private set; } |
@@ -186,6 +181,37 @@ namespace OpenMetaverse | |||
186 | if(m_udpSocket !=null) | 181 | if(m_udpSocket !=null) |
187 | try { m_udpSocket.Close(); } catch { } | 182 | try { m_udpSocket.Close(); } catch { } |
188 | } | 183 | } |
184 | |||
185 | public UDPPacketBuffer GetNewUDPBuffer(IPEndPoint remoteEndpoint) | ||
186 | { | ||
187 | lock (m_udpBuffersPoolLock) | ||
188 | { | ||
189 | if (m_udpBuffersPoolPtr >= 0) | ||
190 | { | ||
191 | UDPPacketBuffer buf = m_udpBuffersPool[m_udpBuffersPoolPtr]; | ||
192 | m_udpBuffersPool[m_udpBuffersPoolPtr] = null; | ||
193 | m_udpBuffersPoolPtr--; | ||
194 | buf.RemoteEndPoint = remoteEndpoint; | ||
195 | return buf; | ||
196 | } | ||
197 | } | ||
198 | return new UDPPacketBuffer(remoteEndpoint); | ||
199 | } | ||
200 | |||
201 | public void FreeUDPBuffer(UDPPacketBuffer buf) | ||
202 | { | ||
203 | lock (m_udpBuffersPoolLock) | ||
204 | { | ||
205 | if (m_udpBuffersPoolPtr < 999) | ||
206 | { | ||
207 | buf.RemoteEndPoint = null; | ||
208 | buf.DataLength = 0; | ||
209 | m_udpBuffersPoolPtr++; | ||
210 | m_udpBuffersPool[m_udpBuffersPoolPtr] = buf; | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | |||
189 | /// <summary> | 215 | /// <summary> |
190 | /// Start inbound UDP packet handling. | 216 | /// Start inbound UDP packet handling. |
191 | /// </summary> | 217 | /// </summary> |
@@ -202,6 +228,7 @@ namespace OpenMetaverse | |||
202 | /// manner (not throwing an exception when the remote side resets the | 228 | /// manner (not throwing an exception when the remote side resets the |
203 | /// connection). This call is ignored on Mono where the flag is not | 229 | /// connection). This call is ignored on Mono where the flag is not |
204 | /// necessary</remarks> | 230 | /// necessary</remarks> |
231 | |||
205 | public virtual void StartInbound(int recvBufferSize) | 232 | public virtual void StartInbound(int recvBufferSize) |
206 | { | 233 | { |
207 | if (!IsRunningInbound) | 234 | if (!IsRunningInbound) |
@@ -306,102 +333,56 @@ namespace OpenMetaverse | |||
306 | IsRunningOutbound = false; | 333 | IsRunningOutbound = false; |
307 | } | 334 | } |
308 | 335 | ||
309 | public virtual bool EnablePools() | 336 | private void AsyncBeginReceive() |
310 | { | 337 | { |
311 | if (!UsePools) | 338 | if (!IsRunningInbound) |
312 | { | 339 | return; |
313 | Pool = new Pool<UDPPacketBuffer>(() => new UDPPacketBuffer(), 500); | ||
314 | |||
315 | UsePools = true; | ||
316 | |||
317 | return true; | ||
318 | } | ||
319 | |||
320 | return false; | ||
321 | } | ||
322 | 340 | ||
323 | public virtual bool DisablePools() | 341 | UDPPacketBuffer buf = GetNewUDPBuffer(new IPEndPoint(IPAddress.Any, 0)); // we need a fresh one here, for now at least |
324 | { | 342 | try |
325 | if (UsePools) | ||
326 | { | 343 | { |
327 | UsePools = false; | 344 | // kick off an async read |
328 | 345 | m_udpSocket.BeginReceiveFrom( | |
329 | // We won't null out the pool to avoid a race condition with code that may be in the middle of using it. | 346 | buf.Data, |
330 | 347 | 0, | |
331 | return true; | 348 | buf.Data.Length, |
349 | SocketFlags.None, | ||
350 | ref buf.RemoteEndPoint, | ||
351 | AsyncEndReceive, | ||
352 | buf); | ||
332 | } | 353 | } |
333 | 354 | catch (SocketException e) | |
334 | return false; | ||
335 | } | ||
336 | |||
337 | private void AsyncBeginReceive() | ||
338 | { | ||
339 | UDPPacketBuffer buf; | ||
340 | |||
341 | // FIXME: Disabled for now as this causes issues with reused packet objects interfering with each other | ||
342 | // on Windows with m_asyncPacketHandling = true, though this has not been seen on Linux. | ||
343 | // Possibly some unexpected issue with fetching UDP data concurrently with multiple threads. Requires more investigation. | ||
344 | // if (UsePools) | ||
345 | // buf = Pool.GetObject(); | ||
346 | // else | ||
347 | buf = new UDPPacketBuffer(); | ||
348 | |||
349 | if (IsRunningInbound) | ||
350 | { | 355 | { |
351 | try | 356 | if (e.SocketErrorCode == SocketError.ConnectionReset) |
352 | { | ||
353 | // kick off an async read | ||
354 | m_udpSocket.BeginReceiveFrom( | ||
355 | //wrappedBuffer.Instance.Data, | ||
356 | buf.Data, | ||
357 | 0, | ||
358 | UDPPacketBuffer.BUFFER_SIZE, | ||
359 | SocketFlags.None, | ||
360 | ref buf.RemoteEndPoint, | ||
361 | AsyncEndReceive, | ||
362 | //wrappedBuffer); | ||
363 | buf); | ||
364 | } | ||
365 | catch (SocketException e) | ||
366 | { | 357 | { |
367 | if (e.SocketErrorCode == SocketError.ConnectionReset) | 358 | m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort); |
359 | bool salvaged = false; | ||
360 | while (!salvaged) | ||
368 | { | 361 | { |
369 | m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort); | 362 | try |
370 | bool salvaged = false; | ||
371 | while (!salvaged) | ||
372 | { | 363 | { |
373 | try | 364 | m_udpSocket.BeginReceiveFrom( |
374 | { | 365 | buf.Data, |
375 | m_udpSocket.BeginReceiveFrom( | 366 | 0, |
376 | //wrappedBuffer.Instance.Data, | 367 | buf.Data.Length, |
377 | buf.Data, | 368 | SocketFlags.None, |
378 | 0, | 369 | ref buf.RemoteEndPoint, |
379 | UDPPacketBuffer.BUFFER_SIZE, | 370 | AsyncEndReceive, |
380 | SocketFlags.None, | 371 | buf); |
381 | ref buf.RemoteEndPoint, | 372 | salvaged = true; |
382 | AsyncEndReceive, | ||
383 | //wrappedBuffer); | ||
384 | buf); | ||
385 | salvaged = true; | ||
386 | } | ||
387 | catch (SocketException) { } | ||
388 | catch (ObjectDisposedException) { return; } | ||
389 | } | 373 | } |
390 | 374 | catch (SocketException) { } | |
391 | m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); | 375 | catch (ObjectDisposedException) { return; } |
392 | } | 376 | } |
393 | } | 377 | |
394 | catch (ObjectDisposedException e) | 378 | m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); |
395 | { | ||
396 | m_log.Error( | ||
397 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); | ||
398 | } | ||
399 | catch (Exception e) | ||
400 | { | ||
401 | m_log.Error( | ||
402 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); | ||
403 | } | 379 | } |
404 | } | 380 | } |
381 | catch (Exception e) | ||
382 | { | ||
383 | m_log.Error( | ||
384 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); | ||
385 | } | ||
405 | } | 386 | } |
406 | 387 | ||
407 | private void AsyncEndReceive(IAsyncResult iar) | 388 | private void AsyncEndReceive(IAsyncResult iar) |
@@ -453,11 +434,6 @@ namespace OpenMetaverse | |||
453 | UdpReceives, se.ErrorCode), | 434 | UdpReceives, se.ErrorCode), |
454 | se); | 435 | se); |
455 | } | 436 | } |
456 | catch (ObjectDisposedException e) | ||
457 | { | ||
458 | m_log.Error( | ||
459 | string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e); | ||
460 | } | ||
461 | catch (Exception e) | 437 | catch (Exception e) |
462 | { | 438 | { |
463 | m_log.Error( | 439 | m_log.Error( |
@@ -465,14 +441,12 @@ namespace OpenMetaverse | |||
465 | } | 441 | } |
466 | finally | 442 | finally |
467 | { | 443 | { |
468 | // if (UsePools) | ||
469 | // Pool.ReturnObject(buffer); | ||
470 | |||
471 | AsyncBeginReceive(); | 444 | AsyncBeginReceive(); |
472 | } | 445 | } |
473 | } | 446 | } |
474 | } | 447 | } |
475 | 448 | ||
449 | /* not in use | ||
476 | public void AsyncBeginSend(UDPPacketBuffer buf) | 450 | public void AsyncBeginSend(UDPPacketBuffer buf) |
477 | { | 451 | { |
478 | // if (IsRunningOutbound) | 452 | // if (IsRunningOutbound) |
@@ -511,9 +485,11 @@ namespace OpenMetaverse | |||
511 | catch (SocketException) { } | 485 | catch (SocketException) { } |
512 | catch (ObjectDisposedException) { } | 486 | catch (ObjectDisposedException) { } |
513 | } | 487 | } |
514 | 488 | */ | |
515 | public void SyncSend(UDPPacketBuffer buf) | 489 | public void SyncSend(UDPPacketBuffer buf) |
516 | { | 490 | { |
491 | if(buf.RemoteEndPoint == null) | ||
492 | return; // already expired | ||
517 | try | 493 | try |
518 | { | 494 | { |
519 | m_udpSocket.SendTo( | 495 | m_udpSocket.SendTo( |
@@ -527,7 +503,7 @@ namespace OpenMetaverse | |||
527 | } | 503 | } |
528 | catch (SocketException e) | 504 | catch (SocketException e) |
529 | { | 505 | { |
530 | m_log.Warn("[UDPBASE]: sync send SocketException {0} " + e.Message); | 506 | m_log.WarnFormat("[UDPBASE]: sync send SocketException {0} {1}", buf.RemoteEndPoint, e.Message); |
531 | } | 507 | } |
532 | catch (ObjectDisposedException) { } | 508 | catch (ObjectDisposedException) { } |
533 | } | 509 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs index f8ec97a..3277638 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs | |||
@@ -66,9 +66,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
66 | /// </summary> | 66 | /// </summary> |
67 | public Int64 MinimumAdaptiveThrottleRate; | 67 | public Int64 MinimumAdaptiveThrottleRate; |
68 | 68 | ||
69 | /// <summary>Amount of the texture throttle to steal for the task throttle</summary> | ||
70 | public double CannibalizeTextureRate; | ||
71 | |||
72 | public int ClientMaxRate; | 69 | public int ClientMaxRate; |
73 | public float BrustTime; | 70 | public float BrustTime; |
74 | 71 | ||
@@ -104,12 +101,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
104 | // AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false); | 101 | // AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false); |
105 | AdaptiveThrottlesEnabled = false; | 102 | AdaptiveThrottlesEnabled = false; |
106 | MinimumAdaptiveThrottleRate = throttleConfig.GetInt("adaptive_throttle_min_bps", 32000); | 103 | MinimumAdaptiveThrottleRate = throttleConfig.GetInt("adaptive_throttle_min_bps", 32000); |
107 | |||
108 | // http textures do use udp bandwidth setting | ||
109 | // CannibalizeTextureRate = (double)throttleConfig.GetFloat("CannibalizeTextureRate", 0.0f); | ||
110 | // CannibalizeTextureRate = Util.Clamp<double>(CannibalizeTextureRate,0.0, 0.9); | ||
111 | CannibalizeTextureRate = 0f; | ||
112 | |||
113 | } | 104 | } |
114 | catch (Exception) { } | 105 | catch (Exception) { } |
115 | } | 106 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs index 76f4c6f..1f978e1 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs | |||
@@ -189,8 +189,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
189 | // Process all the pending adds | 189 | // Process all the pending adds |
190 | OutgoingPacket pendingAdd; | 190 | OutgoingPacket pendingAdd; |
191 | while (m_pendingAdds.TryDequeue(out pendingAdd)) | 191 | while (m_pendingAdds.TryDequeue(out pendingAdd)) |
192 | { | ||
192 | if (pendingAdd != null) | 193 | if (pendingAdd != null) |
193 | m_packets[pendingAdd.SequenceNumber] = pendingAdd; | 194 | m_packets[pendingAdd.SequenceNumber] = pendingAdd; |
195 | } | ||
194 | 196 | ||
195 | // Process all the pending removes, including updating statistics and round-trip times | 197 | // Process all the pending removes, including updating statistics and round-trip times |
196 | PendingAck pendingAcknowledgement; | 198 | PendingAck pendingAcknowledgement; |
@@ -204,13 +206,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
204 | { | 206 | { |
205 | m_packets.Remove(pendingAcknowledgement.SequenceNumber); | 207 | m_packets.Remove(pendingAcknowledgement.SequenceNumber); |
206 | 208 | ||
209 | // Update stats | ||
210 | Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); | ||
211 | |||
212 | ackedPacket.Client.FreeUDPBuffer(ackedPacket.Buffer); | ||
213 | ackedPacket.Buffer = null; | ||
214 | |||
207 | // As with other network applications, assume that an acknowledged packet is an | 215 | // As with other network applications, assume that an acknowledged packet is an |
208 | // indication that the network can handle a little more load, speed up the transmission | 216 | // indication that the network can handle a little more load, speed up the transmission |
209 | ackedPacket.Client.FlowThrottle.AcknowledgePackets(1); | 217 | ackedPacket.Client.FlowThrottle.AcknowledgePackets(1); |
210 | 218 | ||
211 | // Update stats | ||
212 | Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); | ||
213 | |||
214 | if (!pendingAcknowledgement.FromResend) | 219 | if (!pendingAcknowledgement.FromResend) |
215 | { | 220 | { |
216 | // Calculate the round-trip time for this packet and its ACK | 221 | // Calculate the round-trip time for this packet and its ACK |
@@ -244,6 +249,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
244 | 249 | ||
245 | // Update stats | 250 | // Update stats |
246 | Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength); | 251 | Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength); |
252 | |||
253 | removedPacket.Client.FreeUDPBuffer(removedPacket.Buffer); | ||
254 | removedPacket.Buffer = null; | ||
247 | } | 255 | } |
248 | } | 256 | } |
249 | } | 257 | } |