diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | 785 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 83 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 39 |
3 files changed, 580 insertions, 327 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 05a2a63..43c3c7c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | |||
@@ -51,6 +51,44 @@ using Nini.Config; | |||
51 | 51 | ||
52 | namespace OpenSim.Region.ClientStack.LindenUDP | 52 | namespace OpenSim.Region.ClientStack.LindenUDP |
53 | { | 53 | { |
54 | #region Enums | ||
55 | |||
56 | /// <summary> | ||
57 | /// Specifies the fields that have been changed when sending a prim or | ||
58 | /// avatar update | ||
59 | /// </summary> | ||
60 | [Flags] | ||
61 | public enum PrimUpdateFlags : uint | ||
62 | { | ||
63 | None = 0, | ||
64 | AttachmentPoint = 1 << 0, | ||
65 | Material = 1 << 1, | ||
66 | ClickAction = 1 << 2, | ||
67 | Scale = 1 << 3, | ||
68 | ParentID = 1 << 4, | ||
69 | PrimFlags = 1 << 5, | ||
70 | PrimData = 1 << 6, | ||
71 | MediaURL = 1 << 7, | ||
72 | ScratchPad = 1 << 8, | ||
73 | Textures = 1 << 9, | ||
74 | TextureAnim = 1 << 10, | ||
75 | NameValue = 1 << 11, | ||
76 | Position = 1 << 12, | ||
77 | Rotation = 1 << 13, | ||
78 | Velocity = 1 << 14, | ||
79 | Acceleration = 1 << 15, | ||
80 | AngularVelocity = 1 << 16, | ||
81 | CollisionPlane = 1 << 17, | ||
82 | Text = 1 << 18, | ||
83 | Particles = 1 << 19, | ||
84 | ExtraData = 1 << 20, | ||
85 | Sound = 1 << 21, | ||
86 | Joint = 1 << 22, | ||
87 | FullUpdate = UInt32.MaxValue | ||
88 | } | ||
89 | |||
90 | #endregion Enums | ||
91 | |||
54 | public delegate bool PacketMethod(IClientAPI simClient, Packet packet); | 92 | public delegate bool PacketMethod(IClientAPI simClient, Packet packet); |
55 | 93 | ||
56 | /// <summary> | 94 | /// <summary> |
@@ -282,12 +320,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
282 | private readonly IGroupsModule m_GroupsModule; | 320 | private readonly IGroupsModule m_GroupsModule; |
283 | 321 | ||
284 | private int m_cachedTextureSerial; | 322 | private int m_cachedTextureSerial; |
285 | private Timer m_avatarTerseUpdateTimer; | 323 | private PriorityQueue<double, ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_avatarTerseUpdates = |
286 | private List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_avatarTerseUpdates = new List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>(); | 324 | new PriorityQueue<double, ImprovedTerseObjectUpdatePacket.ObjectDataBlock>(); |
287 | private Timer m_primTerseUpdateTimer; | 325 | private PriorityQueue<double, ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_primTerseUpdates = |
288 | private List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_primTerseUpdates = new List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>(); | 326 | new PriorityQueue<double, ImprovedTerseObjectUpdatePacket.ObjectDataBlock>(); |
289 | private Timer m_primFullUpdateTimer; | 327 | private PriorityQueue<double, ObjectUpdatePacket.ObjectDataBlock> m_primFullUpdates = |
290 | private List<ObjectUpdatePacket.ObjectDataBlock> m_primFullUpdates = new List<ObjectUpdatePacket.ObjectDataBlock>(); | 328 | new PriorityQueue<double, ObjectUpdatePacket.ObjectDataBlock>(); |
291 | private int m_moneyBalance; | 329 | private int m_moneyBalance; |
292 | private int m_animationSequenceNumber = 1; | 330 | private int m_animationSequenceNumber = 1; |
293 | private bool m_SendLogoutPacketWhenClosing = true; | 331 | private bool m_SendLogoutPacketWhenClosing = true; |
@@ -309,13 +347,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
309 | protected Dictionary<UUID,ulong> m_groupPowers = new Dictionary<UUID, ulong>(); | 347 | protected Dictionary<UUID,ulong> m_groupPowers = new Dictionary<UUID, ulong>(); |
310 | protected int m_terrainCheckerCount; | 348 | protected int m_terrainCheckerCount; |
311 | 349 | ||
312 | // LL uses these limits, apparently. Compressed terse would be 23, but we don't have that yet | 350 | // These numbers are guesses at a decent tradeoff between responsiveness |
313 | protected int m_primTerseUpdatesPerPacket = 10; | 351 | // of the interest list and throughput. Lower is more responsive, higher |
314 | protected int m_primFullUpdatesPerPacket = 14; | 352 | // is better throughput |
315 | protected int m_primTerseUpdateRate = 10; | 353 | protected int m_primTerseUpdatesPerPacket = 25; |
316 | protected int m_primFullUpdateRate = 14; | 354 | protected int m_primFullUpdatesPerPacket = 100; |
317 | protected int m_avatarTerseUpdateRate = 50; | 355 | protected int m_avatarTerseUpdatesPerPacket = 10; |
318 | protected int m_avatarTerseUpdatesPerPacket = 5; | ||
319 | /// <summary>Number of texture packets to put on the queue each time the | 356 | /// <summary>Number of texture packets to put on the queue each time the |
320 | /// OnQueueEmpty event is triggered for the texture category</summary> | 357 | /// OnQueueEmpty event is triggered for the texture category</summary> |
321 | protected int m_textureSendLimit = 20; | 358 | protected int m_textureSendLimit = 20; |
@@ -438,25 +475,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
438 | // Remove ourselves from the scene | 475 | // Remove ourselves from the scene |
439 | m_scene.RemoveClient(AgentId); | 476 | m_scene.RemoveClient(AgentId); |
440 | 477 | ||
441 | // Shut down timers. Thread Context of this method is murky. Lock all timers | ||
442 | if (m_avatarTerseUpdateTimer.Enabled) | ||
443 | lock (m_avatarTerseUpdateTimer) | ||
444 | m_avatarTerseUpdateTimer.Stop(); | ||
445 | if (m_primTerseUpdateTimer.Enabled) | ||
446 | lock (m_primTerseUpdateTimer) | ||
447 | m_primTerseUpdateTimer.Stop(); | ||
448 | if (m_primFullUpdateTimer.Enabled) | ||
449 | lock (m_primFullUpdateTimer) | ||
450 | m_primFullUpdateTimer.Stop(); | ||
451 | |||
452 | // We can't reach into other scenes and close the connection | 478 | // We can't reach into other scenes and close the connection |
453 | // We need to do this over grid communications | 479 | // We need to do this over grid communications |
454 | //m_scene.CloseAllAgents(CircuitCode); | 480 | //m_scene.CloseAllAgents(CircuitCode); |
455 | 481 | ||
456 | m_avatarTerseUpdateTimer.Dispose(); | ||
457 | m_primTerseUpdateTimer.Dispose(); | ||
458 | m_primFullUpdateTimer.Dispose(); | ||
459 | |||
460 | // Disable UDP handling for this client | 482 | // Disable UDP handling for this client |
461 | m_udpClient.Shutdown(); | 483 | m_udpClient.Shutdown(); |
462 | 484 | ||
@@ -483,18 +505,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
483 | 505 | ||
484 | public void Stop() | 506 | public void Stop() |
485 | { | 507 | { |
486 | // Shut down timers. Thread Context is Murky, lock all timers! | ||
487 | if (m_avatarTerseUpdateTimer.Enabled) | ||
488 | lock (m_avatarTerseUpdateTimer) | ||
489 | m_avatarTerseUpdateTimer.Stop(); | ||
490 | |||
491 | if (m_primTerseUpdateTimer.Enabled) | ||
492 | lock (m_primTerseUpdateTimer) | ||
493 | m_primTerseUpdateTimer.Stop(); | ||
494 | 508 | ||
495 | if (m_primFullUpdateTimer.Enabled) | ||
496 | lock (m_primFullUpdateTimer) | ||
497 | m_primFullUpdateTimer.Stop(); | ||
498 | } | 509 | } |
499 | 510 | ||
500 | #endregion Client Methods | 511 | #endregion Client Methods |
@@ -590,18 +601,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
590 | 601 | ||
591 | public virtual void Start() | 602 | public virtual void Start() |
592 | { | 603 | { |
593 | m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate); | ||
594 | m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); | ||
595 | m_avatarTerseUpdateTimer.AutoReset = false; | ||
596 | |||
597 | m_primTerseUpdateTimer = new Timer(m_primTerseUpdateRate); | ||
598 | m_primTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimTerseUpdates); | ||
599 | m_primTerseUpdateTimer.AutoReset = false; | ||
600 | |||
601 | m_primFullUpdateTimer = new Timer(m_primFullUpdateRate); | ||
602 | m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates); | ||
603 | m_primFullUpdateTimer.AutoReset = false; | ||
604 | |||
605 | m_scene.AddNewClient(this); | 604 | m_scene.AddNewClient(this); |
606 | 605 | ||
607 | RefreshGroupMembership(); | 606 | RefreshGroupMembership(); |
@@ -3127,7 +3126,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3127 | 3126 | ||
3128 | avp.Sender.IsTrial = false; | 3127 | avp.Sender.IsTrial = false; |
3129 | avp.Sender.ID = agentID; | 3128 | avp.Sender.ID = agentID; |
3130 | OutPacket(avp, ThrottleOutPacketType.State); | 3129 | OutPacket(avp, ThrottleOutPacketType.Task); |
3131 | } | 3130 | } |
3132 | 3131 | ||
3133 | public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) | 3132 | public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) |
@@ -3159,33 +3158,221 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3159 | 3158 | ||
3160 | #endregion | 3159 | #endregion |
3161 | 3160 | ||
3161 | #region Prim/Avatar Updates | ||
3162 | |||
3163 | /*void SendObjectUpdate(SceneObjectPart obj, PrimFlags creatorFlags, PrimUpdateFlags updateFlags) | ||
3164 | { | ||
3165 | bool canUseCompressed, canUseImproved; | ||
3166 | UpdateFlagsToPacketType(creatorFlags, updateFlags, out canUseCompressed, out canUseImproved); | ||
3167 | |||
3168 | if (!canUseImproved && !canUseCompressed) | ||
3169 | SendFullObjectUpdate(obj, creatorFlags, updateFlags); | ||
3170 | else if (!canUseImproved) | ||
3171 | SendObjectUpdateCompressed(obj, creatorFlags, updateFlags); | ||
3172 | else | ||
3173 | SendImprovedTerseObjectUpdate(obj, creatorFlags, updateFlags); | ||
3174 | } | ||
3175 | |||
3176 | void SendFullObjectUpdate(SceneObjectPart obj, PrimFlags creatorFlags, PrimUpdateFlags updateFlags) | ||
3177 | { | ||
3178 | IClientAPI owner; | ||
3179 | if (m_scene.ClientManager.TryGetValue(obj.OwnerID, out owner) && owner is LLClientView) | ||
3180 | { | ||
3181 | LLClientView llOwner = (LLClientView)owner; | ||
3182 | |||
3183 | // Send an update out to the owner | ||
3184 | ObjectUpdatePacket updateToOwner = new ObjectUpdatePacket(); | ||
3185 | updateToOwner.RegionData.RegionHandle = obj.RegionHandle; | ||
3186 | //updateToOwner.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue); | ||
3187 | updateToOwner.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; | ||
3188 | updateToOwner.ObjectData[0] = BuildUpdateBlock(obj, obj.Flags | creatorFlags | PrimFlags.ObjectYouOwner, 0); | ||
3189 | |||
3190 | m_udpServer.SendPacket(llOwner.UDPClient, updateToOwner, ThrottleOutPacketType.State, true); | ||
3191 | } | ||
3192 | |||
3193 | // Send an update out to everyone else | ||
3194 | ObjectUpdatePacket updateToOthers = new ObjectUpdatePacket(); | ||
3195 | updateToOthers.RegionData.RegionHandle = obj.RegionHandle; | ||
3196 | //updateToOthers.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue); | ||
3197 | updateToOthers.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; | ||
3198 | updateToOthers.ObjectData[0] = BuildUpdateBlock(obj, obj.Flags, 0); | ||
3199 | |||
3200 | m_scene.ClientManager.ForEach( | ||
3201 | delegate(IClientAPI client) | ||
3202 | { | ||
3203 | if (client.AgentId != obj.OwnerID && client is LLClientView) | ||
3204 | { | ||
3205 | LLClientView llClient = (LLClientView)client; | ||
3206 | m_udpServer.SendPacket(llClient.UDPClient, updateToOthers, ThrottleOutPacketType.State, true); | ||
3207 | } | ||
3208 | } | ||
3209 | ); | ||
3210 | } | ||
3211 | |||
3212 | void SendObjectUpdateCompressed(SceneObjectPart obj, PrimFlags creatorFlags, PrimUpdateFlags updateFlags) | ||
3213 | { | ||
3214 | } | ||
3215 | |||
3216 | void SendImprovedTerseObjectUpdate(SceneObjectPart obj, PrimFlags creatorFlags, PrimUpdateFlags updateFlags) | ||
3217 | { | ||
3218 | } | ||
3219 | |||
3220 | void UpdateFlagsToPacketType(PrimFlags creatorFlags, PrimUpdateFlags updateFlags, out bool canUseCompressed, out bool canUseImproved) | ||
3221 | { | ||
3222 | canUseCompressed = true; | ||
3223 | canUseImproved = true; | ||
3224 | |||
3225 | if ((updateFlags & PrimUpdateFlags.FullUpdate) == PrimUpdateFlags.FullUpdate || creatorFlags != PrimFlags.None) | ||
3226 | { | ||
3227 | canUseCompressed = false; | ||
3228 | canUseImproved = false; | ||
3229 | } | ||
3230 | else | ||
3231 | { | ||
3232 | if ((updateFlags & PrimUpdateFlags.Velocity) != 0 || | ||
3233 | (updateFlags & PrimUpdateFlags.Acceleration) != 0 || | ||
3234 | (updateFlags & PrimUpdateFlags.CollisionPlane) != 0 || | ||
3235 | (updateFlags & PrimUpdateFlags.Joint) != 0) | ||
3236 | { | ||
3237 | canUseCompressed = false; | ||
3238 | } | ||
3239 | |||
3240 | if ((updateFlags & PrimUpdateFlags.PrimFlags) != 0 || | ||
3241 | (updateFlags & PrimUpdateFlags.ParentID) != 0 || | ||
3242 | (updateFlags & PrimUpdateFlags.Scale) != 0 || | ||
3243 | (updateFlags & PrimUpdateFlags.PrimData) != 0 || | ||
3244 | (updateFlags & PrimUpdateFlags.Text) != 0 || | ||
3245 | (updateFlags & PrimUpdateFlags.NameValue) != 0 || | ||
3246 | (updateFlags & PrimUpdateFlags.ExtraData) != 0 || | ||
3247 | (updateFlags & PrimUpdateFlags.TextureAnim) != 0 || | ||
3248 | (updateFlags & PrimUpdateFlags.Sound) != 0 || | ||
3249 | (updateFlags & PrimUpdateFlags.Particles) != 0 || | ||
3250 | (updateFlags & PrimUpdateFlags.Material) != 0 || | ||
3251 | (updateFlags & PrimUpdateFlags.ClickAction) != 0 || | ||
3252 | (updateFlags & PrimUpdateFlags.MediaURL) != 0 || | ||
3253 | (updateFlags & PrimUpdateFlags.Joint) != 0) | ||
3254 | { | ||
3255 | canUseImproved = false; | ||
3256 | } | ||
3257 | } | ||
3258 | } | ||
3259 | |||
3260 | static ObjectUpdatePacket.ObjectDataBlock BuildUpdateBlockFromPrim(SceneObjectPart prim, UUID assetID, PrimFlags flags, uint crc) | ||
3261 | { | ||
3262 | byte[] objectData = new byte[60]; | ||
3263 | prim.OffsetPosition.ToBytes(objectData, 0); | ||
3264 | prim.Velocity.ToBytes(objectData, 12); | ||
3265 | prim.Acceleration.ToBytes(objectData, 24); | ||
3266 | prim.RotationOffset.ToBytes(objectData, 36); | ||
3267 | prim.AngularVelocity.ToBytes(objectData, 48); | ||
3268 | |||
3269 | ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); | ||
3270 | update.ClickAction = (byte)prim.ClickAction; | ||
3271 | update.CRC = crc; | ||
3272 | update.ExtraParams = prim.Shape.ExtraParams ?? Utils.EmptyBytes; | ||
3273 | update.Flags = (byte)flags; | ||
3274 | update.FullID = prim.UUID; | ||
3275 | update.ID = prim.LocalId; | ||
3276 | //update.JointAxisOrAnchor = Vector3.Zero; // These are deprecated | ||
3277 | //update.JointPivot = Vector3.Zero; | ||
3278 | //update.JointType = 0; | ||
3279 | update.Material = prim.Material; | ||
3280 | update.MediaURL = Utils.EmptyBytes; // FIXME: Support this in OpenSim | ||
3281 | if (prim.IsAttachment) | ||
3282 | update.NameValue = Util.StringToBytes256("AttachItemID STRING RW SV " + assetID); | ||
3283 | else | ||
3284 | update.NameValue = Utils.EmptyBytes; | ||
3285 | update.ObjectData = objectData; | ||
3286 | update.ParentID = prim.ParentID; | ||
3287 | update.PathBegin = prim.Shape.PathBegin; | ||
3288 | update.PathCurve = prim.Shape.PathCurve; | ||
3289 | update.PathEnd = prim.Shape.PathEnd; | ||
3290 | update.PathRadiusOffset = prim.Shape.PathRadiusOffset; | ||
3291 | update.PathRevolutions = prim.Shape.PathRevolutions; | ||
3292 | update.PathScaleX = prim.Shape.PathScaleX; | ||
3293 | update.PathScaleY = prim.Shape.PathScaleY; | ||
3294 | update.PathShearX = prim.Shape.PathShearX; | ||
3295 | update.PathShearY = prim.Shape.PathShearY; | ||
3296 | update.PathSkew = prim.Shape.PathSkew; | ||
3297 | update.PathTaperX = prim.Shape.PathTaperX; | ||
3298 | update.PathTaperY = prim.Shape.PathTaperY; | ||
3299 | update.PathTwist = prim.Shape.PathTwist; | ||
3300 | update.PathTwistBegin = prim.Shape.PathTwistBegin; | ||
3301 | update.PCode = prim.Shape.PCode; | ||
3302 | update.ProfileBegin = prim.Shape.ProfileBegin; | ||
3303 | update.ProfileCurve = prim.Shape.ProfileCurve; | ||
3304 | update.ProfileEnd = prim.Shape.ProfileEnd; | ||
3305 | update.ProfileHollow = prim.Shape.ProfileHollow; | ||
3306 | update.PSBlock = prim.ParticleSystem ?? Utils.EmptyBytes; | ||
3307 | update.TextColor = new Color4(prim.Color).GetBytes(true); | ||
3308 | update.TextureAnim = prim.TextureAnimation ?? Utils.EmptyBytes; | ||
3309 | update.TextureEntry = prim.Shape.TextureEntry ?? Utils.EmptyBytes; | ||
3310 | update.Scale = prim.Scale; | ||
3311 | update.State = prim.Shape.State; | ||
3312 | update.Text = Util.StringToBytes256(prim.Text); | ||
3313 | update.UpdateFlags = (uint)flags; | ||
3314 | |||
3315 | if (prim.Sound != UUID.Zero) | ||
3316 | { | ||
3317 | update.Sound = prim.Sound; | ||
3318 | update.OwnerID = prim.OwnerID; | ||
3319 | update.Gain = (float)prim.SoundGain; | ||
3320 | update.Radius = (float)prim.SoundRadius; | ||
3321 | } | ||
3322 | |||
3323 | switch ((PCode)prim.Shape.PCode) | ||
3324 | { | ||
3325 | case PCode.Grass: | ||
3326 | case PCode.Tree: | ||
3327 | case PCode.NewTree: | ||
3328 | update.Data = new byte[] { prim.Shape.State }; | ||
3329 | break; | ||
3330 | default: | ||
3331 | // TODO: Support ScratchPad | ||
3332 | //if (prim.ScratchPad != null) | ||
3333 | //{ | ||
3334 | // update.Data = new byte[prim.ScratchPad.Length]; | ||
3335 | // Buffer.BlockCopy(prim.ScratchPad, 0, update.Data, 0, update.Data.Length); | ||
3336 | //} | ||
3337 | //else | ||
3338 | //{ | ||
3339 | // update.Data = Utils.EmptyBytes; | ||
3340 | //} | ||
3341 | update.Data = Utils.EmptyBytes; | ||
3342 | break; | ||
3343 | } | ||
3344 | |||
3345 | return update; | ||
3346 | }*/ | ||
3347 | |||
3348 | #endregion Prim/Avatar Updates | ||
3349 | |||
3162 | #region Avatar Packet/data sending Methods | 3350 | #region Avatar Packet/data sending Methods |
3163 | 3351 | ||
3164 | /// <summary> | 3352 | /// <summary> |
3165 | /// send a objectupdate packet with information about the clients avatar | 3353 | /// send a objectupdate packet with information about the clients avatar |
3166 | /// </summary> | 3354 | /// </summary> |
3167 | public void SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, | 3355 | public void SendAvatarData(SendAvatarData data) |
3168 | uint avatarLocalID, Vector3 Pos, byte[] textureEntry, uint parentID, Quaternion rotation) | ||
3169 | { | 3356 | { |
3170 | ObjectUpdatePacket objupdate = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); | 3357 | ObjectUpdatePacket objupdate = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); |
3171 | // TODO: don't create new blocks if recycling an old packet | 3358 | // TODO: don't create new blocks if recycling an old packet |
3172 | objupdate.RegionData.RegionHandle = regionHandle; | 3359 | objupdate.RegionData.RegionHandle = data.regionHandle; |
3173 | objupdate.RegionData.TimeDilation = ushort.MaxValue; | 3360 | objupdate.RegionData.TimeDilation = ushort.MaxValue; |
3174 | objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; | 3361 | objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; |
3175 | objupdate.ObjectData[0] = CreateDefaultAvatarPacket(textureEntry); | 3362 | objupdate.ObjectData[0] = CreateDefaultAvatarPacket(data.textureEntry); |
3176 | 3363 | ||
3177 | //give this avatar object a local id and assign the user a name | 3364 | //give this avatar object a local id and assign the user a name |
3178 | objupdate.ObjectData[0].ID = avatarLocalID; | 3365 | objupdate.ObjectData[0].ID = data.avatarLocalID; |
3179 | objupdate.ObjectData[0].FullID = avatarID; | 3366 | objupdate.ObjectData[0].FullID = data.avatarID; |
3180 | objupdate.ObjectData[0].ParentID = parentID; | 3367 | objupdate.ObjectData[0].ParentID = data.parentID; |
3181 | objupdate.ObjectData[0].NameValue = | 3368 | objupdate.ObjectData[0].NameValue = |
3182 | Utils.StringToBytes("FirstName STRING RW SV " + firstName + "\nLastName STRING RW SV " + lastName + "\nTitle STRING RW SV " + grouptitle); | 3369 | Utils.StringToBytes("FirstName STRING RW SV " + data.firstName + "\nLastName STRING RW SV " + data.lastName + "\nTitle STRING RW SV " + data.grouptitle); |
3183 | 3370 | ||
3184 | Vector3 pos2 = new Vector3(Pos.X, Pos.Y, Pos.Z); | 3371 | Vector3 pos2 = new Vector3(data.Pos.X, data.Pos.Y, data.Pos.Z); |
3185 | byte[] pb = pos2.GetBytes(); | 3372 | byte[] pb = pos2.GetBytes(); |
3186 | Array.Copy(pb, 0, objupdate.ObjectData[0].ObjectData, 16, pb.Length); | 3373 | Array.Copy(pb, 0, objupdate.ObjectData[0].ObjectData, 16, pb.Length); |
3187 | 3374 | ||
3188 | byte[] rot = rotation.GetBytes(); | 3375 | byte[] rot = data.rotation.GetBytes(); |
3189 | Array.Copy(rot, 0, objupdate.ObjectData[0].ObjectData, 52, rot.Length); | 3376 | Array.Copy(rot, 0, objupdate.ObjectData[0].ObjectData, 52, rot.Length); |
3190 | 3377 | ||
3191 | objupdate.Header.Zerocoded = true; | 3378 | objupdate.Header.Zerocoded = true; |
@@ -3196,38 +3383,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3196 | /// Send a terse positional/rotation/velocity update about an avatar | 3383 | /// Send a terse positional/rotation/velocity update about an avatar |
3197 | /// to the client. This avatar can be that of the client itself. | 3384 | /// to the client. This avatar can be that of the client itself. |
3198 | /// </summary> | 3385 | /// </summary> |
3199 | public virtual void SendAvatarTerseUpdate(ulong regionHandle, | 3386 | public virtual void SendAvatarTerseUpdate(SendAvatarTerseData data) |
3200 | ushort timeDilation, uint localID, Vector3 position, | ||
3201 | Vector3 velocity, Quaternion rotation, UUID agentid) | ||
3202 | { | 3387 | { |
3388 | if (data.priority == double.NaN) | ||
3389 | { | ||
3390 | m_log.Error("[LLClientView] SendAvatarTerseUpdate received a NaN priority, dropping update"); | ||
3391 | return; | ||
3392 | } | ||
3393 | |||
3394 | Quaternion rotation = data.rotation; | ||
3395 | |||
3203 | if (rotation.X == rotation.Y && | 3396 | if (rotation.X == rotation.Y && |
3204 | rotation.Y == rotation.Z && | 3397 | rotation.Y == rotation.Z && |
3205 | rotation.Z == rotation.W && rotation.W == 0) | 3398 | rotation.Z == rotation.W && rotation.W == 0) |
3206 | rotation = Quaternion.Identity; | 3399 | rotation = Quaternion.Identity; |
3207 | 3400 | ||
3208 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock terseBlock = | 3401 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock terseBlock = |
3209 | CreateAvatarImprovedBlock(localID, position, velocity,rotation); | 3402 | CreateAvatarImprovedBlock(data.localID, data.position, data.velocity, rotation); |
3210 | 3403 | ||
3211 | lock (m_avatarTerseUpdates) | 3404 | lock (m_avatarTerseUpdates.SyncRoot) |
3212 | { | 3405 | m_avatarTerseUpdates.Enqueue(data.priority, terseBlock, data.localID); |
3213 | m_avatarTerseUpdates.Add(terseBlock); | ||
3214 | |||
3215 | // If packet is full or own movement packet, send it. | ||
3216 | if (m_avatarTerseUpdates.Count >= m_avatarTerseUpdatesPerPacket) | ||
3217 | { | ||
3218 | ProcessAvatarTerseUpdates(this, null); | ||
3219 | } | ||
3220 | else if (m_avatarTerseUpdates.Count == 1) | ||
3221 | { | ||
3222 | lock (m_avatarTerseUpdateTimer) | ||
3223 | m_avatarTerseUpdateTimer.Start(); | ||
3224 | } | ||
3225 | } | ||
3226 | } | 3406 | } |
3227 | 3407 | ||
3228 | private void ProcessAvatarTerseUpdates(object sender, ElapsedEventArgs e) | 3408 | private void ProcessAvatarTerseUpdates() |
3229 | { | 3409 | { |
3230 | lock (m_avatarTerseUpdates) | 3410 | lock (m_avatarTerseUpdates.SyncRoot) |
3231 | { | 3411 | { |
3232 | ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); | 3412 | ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); |
3233 | 3413 | ||
@@ -3237,44 +3417,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3237 | terse.RegionData.TimeDilation = | 3417 | terse.RegionData.TimeDilation = |
3238 | (ushort)(Scene.TimeDilation * ushort.MaxValue); | 3418 | (ushort)(Scene.TimeDilation * ushort.MaxValue); |
3239 | 3419 | ||
3240 | int max = m_avatarTerseUpdatesPerPacket; | 3420 | int count = Math.Min(m_avatarTerseUpdates.Count, m_avatarTerseUpdatesPerPacket); |
3241 | if (max > m_avatarTerseUpdates.Count) | ||
3242 | max = m_avatarTerseUpdates.Count; | ||
3243 | |||
3244 | int count = 0; | ||
3245 | int size = 0; | ||
3246 | |||
3247 | byte[] zerobuffer = new byte[1024]; | ||
3248 | byte[] blockbuffer = new byte[1024]; | ||
3249 | |||
3250 | for (count = 0 ; count < max ; count++) | ||
3251 | { | ||
3252 | int length = 0; | ||
3253 | m_avatarTerseUpdates[count].ToBytes(blockbuffer, ref length); | ||
3254 | length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); | ||
3255 | if (size + length > Packet.MTU) | ||
3256 | break; | ||
3257 | size += length; | ||
3258 | } | ||
3259 | 3421 | ||
3260 | terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; | 3422 | terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; |
3261 | 3423 | for (int i = 0; i < count; i++) | |
3262 | for (int i = 0 ; i < count ; i++) | 3424 | terse.ObjectData[i] = m_avatarTerseUpdates.Dequeue(); |
3263 | { | ||
3264 | terse.ObjectData[i] = m_avatarTerseUpdates[0]; | ||
3265 | m_avatarTerseUpdates.RemoveAt(0); | ||
3266 | } | ||
3267 | 3425 | ||
3268 | terse.Header.Reliable = false; | 3426 | terse.Header.Reliable = false; |
3269 | terse.Header.Zerocoded = true; | 3427 | terse.Header.Zerocoded = true; |
3270 | // FIXME: Move this to ThrottleOutPacketType.State when the real prioritization code is committed | ||
3271 | OutPacket(terse, ThrottleOutPacketType.Task); | ||
3272 | 3428 | ||
3273 | if (m_avatarTerseUpdates.Count == 0) | 3429 | OutPacket(terse, ThrottleOutPacketType.Task); |
3274 | { | ||
3275 | lock (m_avatarTerseUpdateTimer) | ||
3276 | m_avatarTerseUpdateTimer.Stop(); | ||
3277 | } | ||
3278 | } | 3430 | } |
3279 | } | 3431 | } |
3280 | 3432 | ||
@@ -3342,54 +3494,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3342 | OutPacket(attach, ThrottleOutPacketType.Task); | 3494 | OutPacket(attach, ThrottleOutPacketType.Task); |
3343 | } | 3495 | } |
3344 | 3496 | ||
3345 | public void SendPrimitiveToClient( | 3497 | public void SendPrimitiveToClient(SendPrimitiveData data) |
3346 | ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, | ||
3347 | Vector3 pos, Vector3 vel, Vector3 acc, Quaternion rotation, Vector3 rvel, | ||
3348 | uint flags, UUID objectID, UUID ownerID, string text, byte[] color, | ||
3349 | uint parentID, byte[] particleSystem, byte clickAction, byte material) | ||
3350 | { | 3498 | { |
3351 | byte[] textureanim = new byte[0]; | 3499 | if (data.priority == double.NaN) |
3352 | 3500 | { | |
3353 | SendPrimitiveToClient(regionHandle, timeDilation, localID, primShape, pos, vel, | 3501 | m_log.Error("[LLClientView] SendPrimitiveToClient received a NaN priority, dropping update"); |
3354 | acc, rotation, rvel, flags, | 3502 | return; |
3355 | objectID, ownerID, text, color, parentID, particleSystem, | 3503 | } |
3356 | clickAction, material, textureanim, false, 0, UUID.Zero, UUID.Zero, 0, 0, 0); | ||
3357 | } | ||
3358 | 3504 | ||
3359 | public void SendPrimitiveToClient( | 3505 | Quaternion rotation = data.rotation; |
3360 | ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, | ||
3361 | Vector3 pos, Vector3 velocity, Vector3 acceleration, Quaternion rotation, Vector3 rotational_velocity, | ||
3362 | uint flags, | ||
3363 | UUID objectID, UUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, | ||
3364 | byte clickAction, byte material, byte[] textureanim, bool attachment, uint AttachPoint, UUID AssetId, UUID SoundId, double SoundGain, byte SoundFlags, double SoundRadius) | ||
3365 | { | ||
3366 | 3506 | ||
3367 | if (AttachPoint > 30 && ownerID != AgentId) // Someone else's HUD | 3507 | if (data.AttachPoint > 30 && data.ownerID != AgentId) // Someone else's HUD |
3368 | return; | 3508 | return; |
3369 | if (primShape.PCode == 9 && primShape.State != 0 && parentID == 0) | 3509 | if (data.primShape.PCode == 9 && data.primShape.State != 0 && data.parentID == 0) |
3370 | return; | 3510 | return; |
3371 | 3511 | ||
3372 | if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) | 3512 | if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0.0f) |
3373 | rotation = Quaternion.Identity; | 3513 | rotation = Quaternion.Identity; |
3374 | 3514 | ||
3375 | ObjectUpdatePacket.ObjectDataBlock objectData = CreatePrimUpdateBlock(primShape, flags); | 3515 | ObjectUpdatePacket.ObjectDataBlock objectData = CreatePrimUpdateBlock(data.primShape, data.flags); |
3376 | 3516 | ||
3377 | objectData.ID = localID; | 3517 | objectData.ID = data.localID; |
3378 | objectData.FullID = objectID; | 3518 | objectData.FullID = data.objectID; |
3379 | objectData.OwnerID = ownerID; | 3519 | objectData.OwnerID = data.ownerID; |
3380 | 3520 | ||
3381 | objectData.Text = Util.StringToBytes256(text); | 3521 | objectData.Text = Util.StringToBytes256(data.text); |
3382 | objectData.TextColor[0] = color[0]; | 3522 | objectData.TextColor[0] = data.color[0]; |
3383 | objectData.TextColor[1] = color[1]; | 3523 | objectData.TextColor[1] = data.color[1]; |
3384 | objectData.TextColor[2] = color[2]; | 3524 | objectData.TextColor[2] = data.color[2]; |
3385 | objectData.TextColor[3] = color[3]; | 3525 | objectData.TextColor[3] = data.color[3]; |
3386 | objectData.ParentID = parentID; | 3526 | objectData.ParentID = data.parentID; |
3387 | objectData.PSBlock = particleSystem; | 3527 | objectData.PSBlock = data.particleSystem; |
3388 | objectData.ClickAction = clickAction; | 3528 | objectData.ClickAction = data.clickAction; |
3389 | objectData.Material = material; | 3529 | objectData.Material = data.material; |
3390 | objectData.Flags = 0; | 3530 | objectData.Flags = 0; |
3391 | 3531 | ||
3392 | if (attachment) | 3532 | if (data.attachment) |
3393 | { | 3533 | { |
3394 | // Necessary??? | 3534 | // Necessary??? |
3395 | objectData.JointAxisOrAnchor = new Vector3(0, 0, 2); | 3535 | objectData.JointAxisOrAnchor = new Vector3(0, 0, 2); |
@@ -3397,14 +3537,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3397 | 3537 | ||
3398 | // Item from inventory??? | 3538 | // Item from inventory??? |
3399 | objectData.NameValue = | 3539 | objectData.NameValue = |
3400 | Utils.StringToBytes("AttachItemID STRING RW SV " + AssetId.Guid); | 3540 | Utils.StringToBytes("AttachItemID STRING RW SV " + data.AssetId.Guid); |
3401 | objectData.State = (byte)((AttachPoint % 16) * 16 + (AttachPoint / 16)); | 3541 | objectData.State = (byte)((data.AttachPoint % 16) * 16 + (data.AttachPoint / 16)); |
3402 | } | 3542 | } |
3403 | 3543 | ||
3404 | // Xantor 20080528: Send sound info as well | 3544 | // Xantor 20080528: Send sound info as well |
3405 | // Xantor 20080530: Zero out everything if there's no SoundId, so zerocompression will work again | 3545 | // Xantor 20080530: Zero out everything if there's no SoundId, so zerocompression will work again |
3406 | objectData.Sound = SoundId; | 3546 | objectData.Sound = data.SoundId; |
3407 | if (SoundId == UUID.Zero) | 3547 | if (data.SoundId == UUID.Zero) |
3408 | { | 3548 | { |
3409 | objectData.OwnerID = UUID.Zero; | 3549 | objectData.OwnerID = UUID.Zero; |
3410 | objectData.Gain = 0.0f; | 3550 | objectData.Gain = 0.0f; |
@@ -3413,48 +3553,68 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3413 | } | 3553 | } |
3414 | else | 3554 | else |
3415 | { | 3555 | { |
3416 | objectData.OwnerID = ownerID; | 3556 | objectData.OwnerID = data.ownerID; |
3417 | objectData.Gain = (float)SoundGain; | 3557 | objectData.Gain = (float)data.SoundVolume; |
3418 | objectData.Radius = (float)SoundRadius; | 3558 | objectData.Radius = (float)data.SoundRadius; |
3419 | objectData.Flags = SoundFlags; | 3559 | objectData.Flags = data.SoundFlags; |
3420 | } | 3560 | } |
3421 | 3561 | ||
3422 | byte[] pb = pos.GetBytes(); | 3562 | byte[] pb = data.pos.GetBytes(); |
3423 | Array.Copy(pb, 0, objectData.ObjectData, 0, pb.Length); | 3563 | Buffer.BlockCopy(pb, 0, objectData.ObjectData, 0, pb.Length); |
3424 | 3564 | ||
3425 | byte[] vel = velocity.GetBytes(); | 3565 | byte[] vel = data.vel.GetBytes(); |
3426 | Array.Copy(vel, 0, objectData.ObjectData, pb.Length, vel.Length); | 3566 | Buffer.BlockCopy(vel, 0, objectData.ObjectData, pb.Length, vel.Length); |
3427 | 3567 | ||
3428 | byte[] rot = rotation.GetBytes(); | 3568 | byte[] rot = rotation.GetBytes(); |
3429 | Array.Copy(rot, 0, objectData.ObjectData, 36, rot.Length); | 3569 | Buffer.BlockCopy(rot, 0, objectData.ObjectData, 36, rot.Length); |
3430 | 3570 | ||
3431 | byte[] rvel = rotational_velocity.GetBytes(); | 3571 | byte[] rvel = data.rvel.GetBytes(); |
3432 | Array.Copy(rvel, 0, objectData.ObjectData, 36 + rot.Length, rvel.Length); | 3572 | Buffer.BlockCopy(rvel, 0, objectData.ObjectData, 36 + rot.Length, rvel.Length); |
3433 | 3573 | ||
3434 | if (textureanim.Length > 0) | 3574 | if (data.textureanim.Length > 0) |
3435 | { | 3575 | { |
3436 | objectData.TextureAnim = textureanim; | 3576 | objectData.TextureAnim = data.textureanim; |
3437 | } | 3577 | } |
3438 | 3578 | ||
3439 | lock (m_primFullUpdates) | 3579 | lock (m_primFullUpdates.SyncRoot) |
3440 | { | 3580 | m_primFullUpdates.Enqueue(data.priority, objectData, data.localID); |
3441 | if (m_primFullUpdates.Count == 0) | ||
3442 | m_primFullUpdateTimer.Start(); | ||
3443 | |||
3444 | m_primFullUpdates.Add(objectData); | ||
3445 | |||
3446 | if (m_primFullUpdates.Count >= m_primFullUpdatesPerPacket) | ||
3447 | ProcessPrimFullUpdates(this, null); | ||
3448 | } | ||
3449 | } | 3581 | } |
3450 | 3582 | ||
3451 | void HandleQueueEmpty(ThrottleOutPacketType queue) | 3583 | void HandleQueueEmpty(ThrottleOutPacketType queue) |
3452 | { | 3584 | { |
3585 | int count = 0; | ||
3586 | |||
3453 | switch (queue) | 3587 | switch (queue) |
3454 | { | 3588 | { |
3455 | case ThrottleOutPacketType.Texture: | 3589 | case ThrottleOutPacketType.Texture: |
3456 | ProcessTextureRequests(); | 3590 | ProcessTextureRequests(); |
3457 | break; | 3591 | break; |
3592 | case ThrottleOutPacketType.Task: | ||
3593 | lock (m_avatarTerseUpdates.SyncRoot) | ||
3594 | count = m_avatarTerseUpdates.Count; | ||
3595 | if (count > 0) | ||
3596 | { | ||
3597 | ProcessAvatarTerseUpdates(); | ||
3598 | return; | ||
3599 | } | ||
3600 | break; | ||
3601 | case ThrottleOutPacketType.State: | ||
3602 | lock (m_primFullUpdates.SyncRoot) | ||
3603 | count = m_primFullUpdates.Count; | ||
3604 | if (count > 0) | ||
3605 | { | ||
3606 | ProcessPrimFullUpdates(); | ||
3607 | return; | ||
3608 | } | ||
3609 | |||
3610 | lock (m_primTerseUpdates.SyncRoot) | ||
3611 | count = m_primTerseUpdates.Count; | ||
3612 | if (count > 0) | ||
3613 | { | ||
3614 | ProcessPrimTerseUpdates(); | ||
3615 | return; | ||
3616 | } | ||
3617 | break; | ||
3458 | } | 3618 | } |
3459 | } | 3619 | } |
3460 | 3620 | ||
@@ -3464,18 +3624,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3464 | m_imageManager.ProcessImageQueue(m_textureSendLimit); | 3624 | m_imageManager.ProcessImageQueue(m_textureSendLimit); |
3465 | } | 3625 | } |
3466 | 3626 | ||
3467 | void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e) | 3627 | void ProcessPrimFullUpdates() |
3468 | { | 3628 | { |
3469 | lock (m_primFullUpdates) | 3629 | lock (m_primFullUpdates.SyncRoot) |
3470 | { | 3630 | { |
3471 | if (m_primFullUpdates.Count == 0 && m_primFullUpdateTimer.Enabled) | ||
3472 | { | ||
3473 | lock (m_primFullUpdateTimer) | ||
3474 | m_primFullUpdateTimer.Stop(); | ||
3475 | |||
3476 | return; | ||
3477 | } | ||
3478 | |||
3479 | ObjectUpdatePacket outPacket = | 3631 | ObjectUpdatePacket outPacket = |
3480 | (ObjectUpdatePacket)PacketPool.Instance.GetPacket( | 3632 | (ObjectUpdatePacket)PacketPool.Instance.GetPacket( |
3481 | PacketType.ObjectUpdate); | 3633 | PacketType.ObjectUpdate); |
@@ -3485,84 +3637,50 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3485 | outPacket.RegionData.TimeDilation = | 3637 | outPacket.RegionData.TimeDilation = |
3486 | (ushort)(Scene.TimeDilation * ushort.MaxValue); | 3638 | (ushort)(Scene.TimeDilation * ushort.MaxValue); |
3487 | 3639 | ||
3488 | int max = m_primFullUpdates.Count; | 3640 | int count = Math.Min(m_primFullUpdates.Count, m_primFullUpdatesPerPacket); |
3489 | if (max > m_primFullUpdatesPerPacket) | ||
3490 | max = m_primFullUpdatesPerPacket; | ||
3491 | |||
3492 | int count = 0; | ||
3493 | int size = 0; | ||
3494 | |||
3495 | byte[] zerobuffer = new byte[1024]; | ||
3496 | byte[] blockbuffer = new byte[1024]; | ||
3497 | 3641 | ||
3498 | for (count = 0 ; count < max ; count++) | 3642 | outPacket.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[count]; |
3499 | { | 3643 | for (int i = 0; i < count; i++) |
3500 | int length = 0; | 3644 | outPacket.ObjectData[i] = m_primFullUpdates.Dequeue(); |
3501 | m_primFullUpdates[count].ToBytes(blockbuffer, ref length); | ||
3502 | length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); | ||
3503 | if (size + length > Packet.MTU) | ||
3504 | break; | ||
3505 | size += length; | ||
3506 | } | ||
3507 | |||
3508 | outPacket.ObjectData = | ||
3509 | new ObjectUpdatePacket.ObjectDataBlock[count]; | ||
3510 | |||
3511 | for (int index = 0 ; index < count ; index++) | ||
3512 | { | ||
3513 | outPacket.ObjectData[index] = m_primFullUpdates[0]; | ||
3514 | m_primFullUpdates.RemoveAt(0); | ||
3515 | } | ||
3516 | 3645 | ||
3517 | outPacket.Header.Zerocoded = true; | 3646 | outPacket.Header.Zerocoded = true; |
3518 | OutPacket(outPacket, ThrottleOutPacketType.State); | 3647 | OutPacket(outPacket, ThrottleOutPacketType.State); |
3519 | |||
3520 | if (m_primFullUpdates.Count == 0 && m_primFullUpdateTimer.Enabled) | ||
3521 | lock (m_primFullUpdateTimer) | ||
3522 | m_primFullUpdateTimer.Stop(); | ||
3523 | } | 3648 | } |
3524 | } | 3649 | } |
3525 | 3650 | ||
3526 | /// <summary> | 3651 | /// <summary> |
3527 | /// | 3652 | /// |
3528 | /// </summary> | 3653 | /// </summary> |
3529 | public void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, | 3654 | //public void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, |
3530 | Quaternion rotation, Vector3 velocity, Vector3 rotationalvelocity, byte state, UUID AssetId, UUID ownerID, int attachPoint) | 3655 | // Quaternion rotation, Vector3 velocity, Vector3 rotationalvelocity, byte state, UUID AssetId, UUID ownerID, int attachPoint) |
3656 | public void SendPrimTerseUpdate(SendPrimitiveTerseData data) | ||
3531 | { | 3657 | { |
3532 | if (attachPoint > 30 && ownerID != AgentId) // Someone else's HUD | 3658 | if (data.priority == double.NaN) |
3659 | { | ||
3660 | m_log.Error("[LLClientView] SendPrimTerseUpdate received a NaN priority, dropping update"); | ||
3661 | return; | ||
3662 | } | ||
3663 | |||
3664 | Quaternion rotation = data.rotation; | ||
3665 | |||
3666 | if (data.attachPoint > 30 && data.owner != AgentId) // Someone else's HUD | ||
3533 | return; | 3667 | return; |
3534 | 3668 | ||
3535 | if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) | 3669 | if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) |
3536 | rotation = Quaternion.Identity; | 3670 | rotation = Quaternion.Identity; |
3537 | 3671 | ||
3538 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock objectData = | 3672 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock objectData = |
3539 | CreatePrimImprovedBlock(localID, position, rotation, | 3673 | CreatePrimImprovedBlock(data.localID, data.position, rotation, |
3540 | velocity, rotationalvelocity, state); | 3674 | data.velocity, data.rotationalvelocity, data.state); |
3541 | |||
3542 | lock (m_primTerseUpdates) | ||
3543 | { | ||
3544 | if (m_primTerseUpdates.Count == 0) | ||
3545 | m_primTerseUpdateTimer.Start(); | ||
3546 | |||
3547 | m_primTerseUpdates.Add(objectData); | ||
3548 | 3675 | ||
3549 | if (m_primTerseUpdates.Count >= m_primTerseUpdatesPerPacket) | 3676 | lock (m_primTerseUpdates.SyncRoot) |
3550 | ProcessPrimTerseUpdates(this, null); | 3677 | m_primTerseUpdates.Enqueue(data.priority, objectData, data.localID); |
3551 | } | ||
3552 | } | 3678 | } |
3553 | 3679 | ||
3554 | void ProcessPrimTerseUpdates(object sender, ElapsedEventArgs e) | 3680 | void ProcessPrimTerseUpdates() |
3555 | { | 3681 | { |
3556 | lock (m_primTerseUpdates) | 3682 | lock (m_primTerseUpdates.SyncRoot) |
3557 | { | 3683 | { |
3558 | if (m_primTerseUpdates.Count == 0) | ||
3559 | { | ||
3560 | lock (m_primTerseUpdateTimer) | ||
3561 | m_primTerseUpdateTimer.Stop(); | ||
3562 | |||
3563 | return; | ||
3564 | } | ||
3565 | |||
3566 | ImprovedTerseObjectUpdatePacket outPacket = | 3684 | ImprovedTerseObjectUpdatePacket outPacket = |
3567 | (ImprovedTerseObjectUpdatePacket) | 3685 | (ImprovedTerseObjectUpdatePacket) |
3568 | PacketPool.Instance.GetPacket( | 3686 | PacketPool.Instance.GetPacket( |
@@ -3573,43 +3691,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3573 | outPacket.RegionData.TimeDilation = | 3691 | outPacket.RegionData.TimeDilation = |
3574 | (ushort)(Scene.TimeDilation * ushort.MaxValue); | 3692 | (ushort)(Scene.TimeDilation * ushort.MaxValue); |
3575 | 3693 | ||
3576 | int max = m_primTerseUpdates.Count; | 3694 | int count = Math.Min(m_primTerseUpdates.Count, m_primTerseUpdatesPerPacket); |
3577 | if (max > m_primTerseUpdatesPerPacket) | ||
3578 | max = m_primTerseUpdatesPerPacket; | ||
3579 | |||
3580 | int count = 0; | ||
3581 | int size = 0; | ||
3582 | 3695 | ||
3583 | byte[] zerobuffer = new byte[1024]; | 3696 | outPacket.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; |
3584 | byte[] blockbuffer = new byte[1024]; | 3697 | for (int i = 0; i < count; i++) |
3585 | 3698 | outPacket.ObjectData[i] = m_primTerseUpdates.Dequeue(); | |
3586 | for (count = 0 ; count < max ; count++) | ||
3587 | { | ||
3588 | int length = 0; | ||
3589 | m_primTerseUpdates[count].ToBytes(blockbuffer, ref length); | ||
3590 | length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); | ||
3591 | if (size + length > Packet.MTU) | ||
3592 | break; | ||
3593 | size += length; | ||
3594 | } | ||
3595 | |||
3596 | outPacket.ObjectData = | ||
3597 | new ImprovedTerseObjectUpdatePacket. | ||
3598 | ObjectDataBlock[count]; | ||
3599 | |||
3600 | for (int index = 0 ; index < count ; index++) | ||
3601 | { | ||
3602 | outPacket.ObjectData[index] = m_primTerseUpdates[0]; | ||
3603 | m_primTerseUpdates.RemoveAt(0); | ||
3604 | } | ||
3605 | 3699 | ||
3606 | outPacket.Header.Reliable = false; | 3700 | outPacket.Header.Reliable = false; |
3607 | outPacket.Header.Zerocoded = true; | 3701 | outPacket.Header.Zerocoded = true; |
3608 | OutPacket(outPacket, ThrottleOutPacketType.State); | 3702 | OutPacket(outPacket, ThrottleOutPacketType.State); |
3609 | |||
3610 | if (m_primTerseUpdates.Count == 0) | ||
3611 | lock (m_primTerseUpdateTimer) | ||
3612 | m_primTerseUpdateTimer.Stop(); | ||
3613 | } | 3703 | } |
3614 | } | 3704 | } |
3615 | 3705 | ||
@@ -3617,15 +3707,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3617 | { | 3707 | { |
3618 | while (m_primFullUpdates.Count > 0) | 3708 | while (m_primFullUpdates.Count > 0) |
3619 | { | 3709 | { |
3620 | ProcessPrimFullUpdates(this, null); | 3710 | ProcessPrimFullUpdates(); |
3621 | } | 3711 | } |
3622 | while (m_primTerseUpdates.Count > 0) | 3712 | while (m_primTerseUpdates.Count > 0) |
3623 | { | 3713 | { |
3624 | ProcessPrimTerseUpdates(this, null); | 3714 | ProcessPrimTerseUpdates(); |
3625 | } | 3715 | } |
3626 | while (m_avatarTerseUpdates.Count > 0) | 3716 | while (m_avatarTerseUpdates.Count > 0) |
3627 | { | 3717 | { |
3628 | ProcessAvatarTerseUpdates(this, null); | 3718 | ProcessAvatarTerseUpdates(); |
3629 | } | 3719 | } |
3630 | } | 3720 | } |
3631 | 3721 | ||
@@ -10371,5 +10461,136 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
10371 | pack.TextureData.TextureID = textureID; | 10461 | pack.TextureData.TextureID = textureID; |
10372 | OutPacket(pack, ThrottleOutPacketType.Task); | 10462 | OutPacket(pack, ThrottleOutPacketType.Task); |
10373 | } | 10463 | } |
10464 | |||
10465 | #region PriorityQueue | ||
10466 | private class PriorityQueue<TPriority, TValue> | ||
10467 | { | ||
10468 | private MinHeap<MinHeapItem>[] heaps = new MinHeap<MinHeapItem>[1]; | ||
10469 | private Dictionary<uint, LookupItem> lookup_table = new Dictionary<uint, LookupItem>(); | ||
10470 | private Comparison<TPriority> comparison; | ||
10471 | private object sync_root = new object(); | ||
10472 | |||
10473 | internal PriorityQueue() : | ||
10474 | this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY, Comparer<TPriority>.Default) { } | ||
10475 | internal PriorityQueue(int capacity) : | ||
10476 | this(capacity, Comparer<TPriority>.Default) { } | ||
10477 | internal PriorityQueue(IComparer<TPriority> comparer) : | ||
10478 | this(new Comparison<TPriority>(comparer.Compare)) { } | ||
10479 | internal PriorityQueue(Comparison<TPriority> comparison) : | ||
10480 | this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY, comparison) { } | ||
10481 | internal PriorityQueue(int capacity, IComparer<TPriority> comparer) : | ||
10482 | this(capacity, new Comparison<TPriority>(comparer.Compare)) { } | ||
10483 | internal PriorityQueue(int capacity, Comparison<TPriority> comparison) | ||
10484 | { | ||
10485 | for (int i = 0; i < heaps.Length; ++i) | ||
10486 | heaps[i] = new MinHeap<MinHeapItem>(capacity); | ||
10487 | this.comparison = comparison; | ||
10488 | } | ||
10489 | |||
10490 | internal object SyncRoot { get { return this.sync_root; } } | ||
10491 | internal int Count | ||
10492 | { | ||
10493 | get | ||
10494 | { | ||
10495 | int count = 0; | ||
10496 | for (int i = 0; i < heaps.Length; ++i) | ||
10497 | count = heaps[i].Count; | ||
10498 | return count; | ||
10499 | } | ||
10500 | } | ||
10501 | |||
10502 | internal bool Enqueue(TPriority priority, TValue value, uint local_id) | ||
10503 | { | ||
10504 | LookupItem item; | ||
10505 | |||
10506 | if (lookup_table.TryGetValue(local_id, out item)) | ||
10507 | { | ||
10508 | item.Heap[item.Handle] = new MinHeapItem(priority, value, local_id, this.comparison); | ||
10509 | return false; | ||
10510 | } | ||
10511 | else | ||
10512 | { | ||
10513 | item.Heap = heaps[0]; | ||
10514 | item.Heap.Add(new MinHeapItem(priority, value, local_id, this.comparison), ref item.Handle); | ||
10515 | lookup_table.Add(local_id, item); | ||
10516 | return true; | ||
10517 | } | ||
10518 | } | ||
10519 | |||
10520 | internal TValue Peek() | ||
10521 | { | ||
10522 | for (int i = 0; i < heaps.Length; ++i) | ||
10523 | if (heaps[i].Count > 0) | ||
10524 | return heaps[i].Min().Value; | ||
10525 | throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); | ||
10526 | } | ||
10527 | |||
10528 | internal TValue Dequeue() | ||
10529 | { | ||
10530 | for (int i = 0; i < heaps.Length; ++i) | ||
10531 | { | ||
10532 | if (heaps[i].Count > 0) | ||
10533 | { | ||
10534 | MinHeapItem item = heaps[i].RemoveMin(); | ||
10535 | lookup_table.Remove(item.LocalID); | ||
10536 | return item.Value; | ||
10537 | } | ||
10538 | } | ||
10539 | throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); | ||
10540 | } | ||
10541 | |||
10542 | #region MinHeapItem | ||
10543 | private struct MinHeapItem : IComparable<MinHeapItem> | ||
10544 | { | ||
10545 | private TPriority priority; | ||
10546 | private TValue value; | ||
10547 | private uint local_id; | ||
10548 | private Comparison<TPriority> comparison; | ||
10549 | |||
10550 | internal MinHeapItem(TPriority priority, TValue value, uint local_id) : | ||
10551 | this(priority, value, local_id, Comparer<TPriority>.Default) { } | ||
10552 | internal MinHeapItem(TPriority priority, TValue value, uint local_id, IComparer<TPriority> comparer) : | ||
10553 | this(priority, value, local_id, new Comparison<TPriority>(comparer.Compare)) { } | ||
10554 | internal MinHeapItem(TPriority priority, TValue value, uint local_id, Comparison<TPriority> comparison) | ||
10555 | { | ||
10556 | this.priority = priority; | ||
10557 | this.value = value; | ||
10558 | this.local_id = local_id; | ||
10559 | this.comparison = comparison; | ||
10560 | } | ||
10561 | |||
10562 | internal TPriority Priority { get { return this.priority; } } | ||
10563 | internal TValue Value { get { return this.value; } } | ||
10564 | internal uint LocalID { get { return this.local_id; } } | ||
10565 | |||
10566 | public override string ToString() | ||
10567 | { | ||
10568 | StringBuilder sb = new StringBuilder(); | ||
10569 | sb.Append("["); | ||
10570 | if (this.priority != null) | ||
10571 | sb.Append(this.priority.ToString()); | ||
10572 | sb.Append(","); | ||
10573 | if (this.value != null) | ||
10574 | sb.Append(this.value.ToString()); | ||
10575 | sb.Append("]"); | ||
10576 | return sb.ToString(); | ||
10577 | } | ||
10578 | |||
10579 | public int CompareTo(MinHeapItem other) | ||
10580 | { | ||
10581 | return this.comparison(this.priority, other.priority); | ||
10582 | } | ||
10583 | } | ||
10584 | #endregion | ||
10585 | |||
10586 | #region LookupItem | ||
10587 | private struct LookupItem { | ||
10588 | internal MinHeap<MinHeapItem> Heap; | ||
10589 | internal IHandle Handle; | ||
10590 | } | ||
10591 | #endregion | ||
10592 | } | ||
10593 | #endregion | ||
10594 | |||
10374 | } | 10595 | } |
10375 | } | 10596 | } |
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 4eee6b6..9476eed 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | |||
@@ -170,7 +170,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
170 | { | 170 | { |
171 | ThrottleOutPacketType type = (ThrottleOutPacketType)i; | 171 | ThrottleOutPacketType type = (ThrottleOutPacketType)i; |
172 | 172 | ||
173 | // Initialize the packet outboxes, where packets sit while they are waiting for tokens | ||
173 | m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); | 174 | m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); |
175 | // Initialize the token buckets that control the throttling for each category | ||
174 | m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); | 176 | m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); |
175 | } | 177 | } |
176 | 178 | ||
@@ -293,36 +295,54 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
293 | int state = (int)((float)task * STATE_TASK_PERCENTAGE); | 295 | int state = (int)((float)task * STATE_TASK_PERCENTAGE); |
294 | task -= state; | 296 | task -= state; |
295 | 297 | ||
296 | int ceiling = Int32.MaxValue; | 298 | // Make sure none of the throttles are set below our packet MTU, |
297 | if (m_defaultThrottleRates.Total != 0) | 299 | // otherwise a throttle could become permanently clogged |
298 | { | 300 | resend = Math.Max(resend, LLUDPServer.MTU); |
299 | ceiling = m_defaultThrottleRates.Total; | 301 | land = Math.Max(land, LLUDPServer.MTU); |
300 | if (ceiling < Packet.MTU) ceiling = Packet.MTU; | 302 | wind = Math.Max(wind, LLUDPServer.MTU); |
301 | } | 303 | cloud = Math.Max(cloud, LLUDPServer.MTU); |
302 | 304 | task = Math.Max(task, LLUDPServer.MTU); | |
303 | resend = Utils.Clamp(resend, Packet.MTU, ceiling); | 305 | texture = Math.Max(texture, LLUDPServer.MTU); |
304 | land = Utils.Clamp(land, Packet.MTU, ceiling); | 306 | asset = Math.Max(asset, LLUDPServer.MTU); |
305 | wind = Utils.Clamp(wind, Packet.MTU, ceiling); | 307 | state = Math.Max(state, LLUDPServer.MTU); |
306 | cloud = Utils.Clamp(cloud, Packet.MTU, ceiling); | ||
307 | task = Utils.Clamp(task, Packet.MTU, ceiling); | ||
308 | texture = Utils.Clamp(texture, Packet.MTU, ceiling); | ||
309 | asset = Utils.Clamp(asset, Packet.MTU, ceiling); | ||
310 | state = Utils.Clamp(state, Packet.MTU, ceiling); | ||
311 | 308 | ||
312 | int total = resend + land + wind + cloud + task + texture + asset + state; | 309 | int total = resend + land + wind + cloud + task + texture + asset + state; |
313 | int taskTotal = task + state; | ||
314 | 310 | ||
315 | m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}", | 311 | m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}", |
316 | AgentID, resend, land, wind, cloud, task, texture, asset, state, total); | 312 | AgentID, resend, land, wind, cloud, task, texture, asset, state, total); |
317 | 313 | ||
318 | SetThrottle(ThrottleOutPacketType.Resend, resend, resend); | 314 | // Update the token buckets with new throttle values |
319 | SetThrottle(ThrottleOutPacketType.Land, land, land); | 315 | TokenBucket bucket; |
320 | SetThrottle(ThrottleOutPacketType.Wind, wind, wind); | 316 | |
321 | SetThrottle(ThrottleOutPacketType.Cloud, cloud, cloud); | 317 | bucket = m_throttle; |
322 | SetThrottle(ThrottleOutPacketType.Task, task, taskTotal); | 318 | bucket.MaxBurst = total; |
323 | SetThrottle(ThrottleOutPacketType.Texture, texture, texture); | 319 | |
324 | SetThrottle(ThrottleOutPacketType.Asset, asset, asset); | 320 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; |
325 | SetThrottle(ThrottleOutPacketType.State, state, taskTotal); | 321 | bucket.DripRate = bucket.MaxBurst = resend; |
322 | |||
323 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; | ||
324 | bucket.DripRate = bucket.MaxBurst = land; | ||
325 | |||
326 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; | ||
327 | bucket.DripRate = bucket.MaxBurst = wind; | ||
328 | |||
329 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; | ||
330 | bucket.DripRate = bucket.MaxBurst = cloud; | ||
331 | |||
332 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; | ||
333 | bucket.DripRate = bucket.MaxBurst = asset; | ||
334 | |||
335 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; | ||
336 | bucket.DripRate = task + state + texture; | ||
337 | bucket.MaxBurst = task + state + texture; | ||
338 | |||
339 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; | ||
340 | bucket.DripRate = state + texture; | ||
341 | bucket.MaxBurst = state + texture; | ||
342 | |||
343 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; | ||
344 | bucket.DripRate = texture; | ||
345 | bucket.MaxBurst = texture; | ||
326 | } | 346 | } |
327 | 347 | ||
328 | public byte[] GetThrottlesPacked() | 348 | public byte[] GetThrottlesPacked() |
@@ -342,17 +362,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
342 | return data; | 362 | return data; |
343 | } | 363 | } |
344 | 364 | ||
345 | public void SetThrottle(ThrottleOutPacketType category, int rate, int maxBurst) | ||
346 | { | ||
347 | int i = (int)category; | ||
348 | if (i >= 0 && i < m_throttleCategories.Length) | ||
349 | { | ||
350 | TokenBucket bucket = m_throttleCategories[(int)category]; | ||
351 | bucket.DripRate = rate; | ||
352 | bucket.MaxBurst = maxBurst; | ||
353 | } | ||
354 | } | ||
355 | |||
356 | public bool EnqueueOutgoing(OutgoingPacket packet) | 365 | public bool EnqueueOutgoing(OutgoingPacket packet) |
357 | { | 366 | { |
358 | int category = (int)packet.Category; | 367 | int category = (int)packet.Category; |
@@ -395,9 +404,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
395 | TokenBucket bucket; | 404 | TokenBucket bucket; |
396 | bool packetSent = false; | 405 | bool packetSent = false; |
397 | 406 | ||
407 | //string queueDebugOutput = String.Empty; // Serious debug business | ||
408 | |||
398 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) | 409 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) |
399 | { | 410 | { |
400 | bucket = m_throttleCategories[i]; | 411 | bucket = m_throttleCategories[i]; |
412 | //queueDebugOutput += m_packetOutboxes[i].Count + " "; // Serious debug business | ||
401 | 413 | ||
402 | if (m_nextPackets[i] != null) | 414 | if (m_nextPackets[i] != null) |
403 | { | 415 | { |
@@ -449,6 +461,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
449 | } | 461 | } |
450 | } | 462 | } |
451 | 463 | ||
464 | //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business | ||
452 | return packetSent; | 465 | return packetSent; |
453 | } | 466 | } |
454 | 467 | ||
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 545a0bc..8bfab6a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | |||
@@ -89,6 +89,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
89 | /// </summary> | 89 | /// </summary> |
90 | public class LLUDPServer : OpenSimUDPBase | 90 | public class LLUDPServer : OpenSimUDPBase |
91 | { | 91 | { |
92 | /// <summary>Maximum transmission unit, or UDP packet size, for the LLUDP protocol</summary> | ||
93 | public const int MTU = 1400; | ||
94 | |||
92 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 95 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
93 | 96 | ||
94 | /// <summary>Handlers for incoming packets</summary> | 97 | /// <summary>Handlers for incoming packets</summary> |
@@ -267,38 +270,54 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
267 | { | 270 | { |
268 | int dataLength = data.Length; | 271 | int dataLength = data.Length; |
269 | bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; | 272 | bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; |
273 | bool doCopy = true; | ||
270 | 274 | ||
271 | // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. | 275 | // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. |
272 | // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting | 276 | // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting |
273 | // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here | 277 | // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here |
274 | // to accomodate for both common scenarios and provide ample room for ACK appending in both | 278 | // to accomodate for both common scenarios and provide ample room for ACK appending in both |
275 | int bufferSize = (dataLength > 180) ? Packet.MTU : 200; | 279 | int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; |
276 | 280 | ||
277 | UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); | 281 | UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); |
278 | 282 | ||
279 | // Zerocode if needed | 283 | // Zerocode if needed |
280 | if (doZerocode) | 284 | if (doZerocode) |
281 | { | 285 | { |
282 | try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); } | 286 | try |
287 | { | ||
288 | dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); | ||
289 | doCopy = false; | ||
290 | } | ||
283 | catch (IndexOutOfRangeException) | 291 | catch (IndexOutOfRangeException) |
284 | { | 292 | { |
285 | // The packet grew larger than the bufferSize while zerocoding. | 293 | // The packet grew larger than the bufferSize while zerocoding. |
286 | // Remove the MSG_ZEROCODED flag and send the unencoded data | 294 | // Remove the MSG_ZEROCODED flag and send the unencoded data |
287 | // instead | 295 | // instead |
288 | m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". Removing MSG_ZEROCODED flag"); | 296 | m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength + |
297 | " and BufferLength=" + buffer.Data.Length + ". Removing MSG_ZEROCODED flag"); | ||
289 | data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); | 298 | data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); |
290 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | ||
291 | } | 299 | } |
292 | } | 300 | } |
293 | else | 301 | |
302 | // If the packet data wasn't already copied during zerocoding, copy it now | ||
303 | if (doCopy) | ||
294 | { | 304 | { |
295 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | 305 | if (dataLength <= buffer.Data.Length) |
306 | { | ||
307 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | ||
308 | } | ||
309 | else | ||
310 | { | ||
311 | m_log.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + | ||
312 | type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length + ". Dropping packet"); | ||
313 | return; | ||
314 | } | ||
296 | } | 315 | } |
316 | |||
297 | buffer.DataLength = dataLength; | 317 | buffer.DataLength = dataLength; |
298 | 318 | ||
299 | #region Queue or Send | 319 | #region Queue or Send |
300 | 320 | ||
301 | // Look up the UDPClient this is going to | ||
302 | OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); | 321 | OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); |
303 | 322 | ||
304 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) | 323 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) |
@@ -513,7 +532,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
513 | IClientAPI client; | 532 | IClientAPI client; |
514 | if (!m_scene.ClientManager.TryGetValue(address, out client) || !(client is LLClientView)) | 533 | if (!m_scene.ClientManager.TryGetValue(address, out client) || !(client is LLClientView)) |
515 | { | 534 | { |
516 | m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + | 535 | m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + |
517 | " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_scene.ClientManager.Count + " clients"); | 536 | " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_scene.ClientManager.Count + " clients"); |
518 | return; | 537 | return; |
519 | } | 538 | } |
@@ -568,9 +587,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
568 | // client.BytesSinceLastACK. Lockless thread safety | 587 | // client.BytesSinceLastACK. Lockless thread safety |
569 | int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); | 588 | int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); |
570 | bytesSinceLastACK += buffer.DataLength; | 589 | bytesSinceLastACK += buffer.DataLength; |
571 | if (bytesSinceLastACK > Packet.MTU * 2) | 590 | if (bytesSinceLastACK > LLUDPServer.MTU * 2) |
572 | { | 591 | { |
573 | bytesSinceLastACK -= Packet.MTU * 2; | 592 | bytesSinceLastACK -= LLUDPServer.MTU * 2; |
574 | SendAcks(udpClient); | 593 | SendAcks(udpClient); |
575 | } | 594 | } |
576 | Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); | 595 | Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); |