diff options
Diffstat (limited to 'OpenSim/Region/ClientStack')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | 713 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 79 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 5 |
3 files changed, 547 insertions, 250 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 05a2a63..630b6e6 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; |
@@ -312,9 +350,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
312 | // LL uses these limits, apparently. Compressed terse would be 23, but we don't have that yet | 350 | // LL uses these limits, apparently. Compressed terse would be 23, but we don't have that yet |
313 | protected int m_primTerseUpdatesPerPacket = 10; | 351 | protected int m_primTerseUpdatesPerPacket = 10; |
314 | protected int m_primFullUpdatesPerPacket = 14; | 352 | protected int m_primFullUpdatesPerPacket = 14; |
315 | protected int m_primTerseUpdateRate = 10; | ||
316 | protected int m_primFullUpdateRate = 14; | ||
317 | protected int m_avatarTerseUpdateRate = 50; | ||
318 | protected int m_avatarTerseUpdatesPerPacket = 5; | 353 | protected int m_avatarTerseUpdatesPerPacket = 5; |
319 | /// <summary>Number of texture packets to put on the queue each time the | 354 | /// <summary>Number of texture packets to put on the queue each time the |
320 | /// OnQueueEmpty event is triggered for the texture category</summary> | 355 | /// OnQueueEmpty event is triggered for the texture category</summary> |
@@ -438,25 +473,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
438 | // Remove ourselves from the scene | 473 | // Remove ourselves from the scene |
439 | m_scene.RemoveClient(AgentId); | 474 | m_scene.RemoveClient(AgentId); |
440 | 475 | ||
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 | 476 | // We can't reach into other scenes and close the connection |
453 | // We need to do this over grid communications | 477 | // We need to do this over grid communications |
454 | //m_scene.CloseAllAgents(CircuitCode); | 478 | //m_scene.CloseAllAgents(CircuitCode); |
455 | 479 | ||
456 | m_avatarTerseUpdateTimer.Dispose(); | ||
457 | m_primTerseUpdateTimer.Dispose(); | ||
458 | m_primFullUpdateTimer.Dispose(); | ||
459 | |||
460 | // Disable UDP handling for this client | 480 | // Disable UDP handling for this client |
461 | m_udpClient.Shutdown(); | 481 | m_udpClient.Shutdown(); |
462 | 482 | ||
@@ -483,18 +503,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
483 | 503 | ||
484 | public void Stop() | 504 | public void Stop() |
485 | { | 505 | { |
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 | 506 | ||
491 | if (m_primTerseUpdateTimer.Enabled) | ||
492 | lock (m_primTerseUpdateTimer) | ||
493 | m_primTerseUpdateTimer.Stop(); | ||
494 | |||
495 | if (m_primFullUpdateTimer.Enabled) | ||
496 | lock (m_primFullUpdateTimer) | ||
497 | m_primFullUpdateTimer.Stop(); | ||
498 | } | 507 | } |
499 | 508 | ||
500 | #endregion Client Methods | 509 | #endregion Client Methods |
@@ -590,18 +599,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
590 | 599 | ||
591 | public virtual void Start() | 600 | public virtual void Start() |
592 | { | 601 | { |
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); | 602 | m_scene.AddNewClient(this); |
606 | 603 | ||
607 | RefreshGroupMembership(); | 604 | RefreshGroupMembership(); |
@@ -3127,7 +3124,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3127 | 3124 | ||
3128 | avp.Sender.IsTrial = false; | 3125 | avp.Sender.IsTrial = false; |
3129 | avp.Sender.ID = agentID; | 3126 | avp.Sender.ID = agentID; |
3130 | OutPacket(avp, ThrottleOutPacketType.State); | 3127 | OutPacket(avp, ThrottleOutPacketType.Task); |
3131 | } | 3128 | } |
3132 | 3129 | ||
3133 | public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) | 3130 | public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) |
@@ -3159,33 +3156,221 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3159 | 3156 | ||
3160 | #endregion | 3157 | #endregion |
3161 | 3158 | ||
3159 | #region Prim/Avatar Updates | ||
3160 | |||
3161 | /*void SendObjectUpdate(SceneObjectPart obj, PrimFlags creatorFlags, PrimUpdateFlags updateFlags) | ||
3162 | { | ||
3163 | bool canUseCompressed, canUseImproved; | ||
3164 | UpdateFlagsToPacketType(creatorFlags, updateFlags, out canUseCompressed, out canUseImproved); | ||
3165 | |||
3166 | if (!canUseImproved && !canUseCompressed) | ||
3167 | SendFullObjectUpdate(obj, creatorFlags, updateFlags); | ||
3168 | else if (!canUseImproved) | ||
3169 | SendObjectUpdateCompressed(obj, creatorFlags, updateFlags); | ||
3170 | else | ||
3171 | SendImprovedTerseObjectUpdate(obj, creatorFlags, updateFlags); | ||
3172 | } | ||
3173 | |||
3174 | void SendFullObjectUpdate(SceneObjectPart obj, PrimFlags creatorFlags, PrimUpdateFlags updateFlags) | ||
3175 | { | ||
3176 | IClientAPI owner; | ||
3177 | if (m_scene.ClientManager.TryGetValue(obj.OwnerID, out owner) && owner is LLClientView) | ||
3178 | { | ||
3179 | LLClientView llOwner = (LLClientView)owner; | ||
3180 | |||
3181 | // Send an update out to the owner | ||
3182 | ObjectUpdatePacket updateToOwner = new ObjectUpdatePacket(); | ||
3183 | updateToOwner.RegionData.RegionHandle = obj.RegionHandle; | ||
3184 | //updateToOwner.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue); | ||
3185 | updateToOwner.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; | ||
3186 | updateToOwner.ObjectData[0] = BuildUpdateBlock(obj, obj.Flags | creatorFlags | PrimFlags.ObjectYouOwner, 0); | ||
3187 | |||
3188 | m_udpServer.SendPacket(llOwner.UDPClient, updateToOwner, ThrottleOutPacketType.State, true); | ||
3189 | } | ||
3190 | |||
3191 | // Send an update out to everyone else | ||
3192 | ObjectUpdatePacket updateToOthers = new ObjectUpdatePacket(); | ||
3193 | updateToOthers.RegionData.RegionHandle = obj.RegionHandle; | ||
3194 | //updateToOthers.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue); | ||
3195 | updateToOthers.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; | ||
3196 | updateToOthers.ObjectData[0] = BuildUpdateBlock(obj, obj.Flags, 0); | ||
3197 | |||
3198 | m_scene.ClientManager.ForEach( | ||
3199 | delegate(IClientAPI client) | ||
3200 | { | ||
3201 | if (client.AgentId != obj.OwnerID && client is LLClientView) | ||
3202 | { | ||
3203 | LLClientView llClient = (LLClientView)client; | ||
3204 | m_udpServer.SendPacket(llClient.UDPClient, updateToOthers, ThrottleOutPacketType.State, true); | ||
3205 | } | ||
3206 | } | ||
3207 | ); | ||
3208 | } | ||
3209 | |||
3210 | void SendObjectUpdateCompressed(SceneObjectPart obj, PrimFlags creatorFlags, PrimUpdateFlags updateFlags) | ||
3211 | { | ||
3212 | } | ||
3213 | |||
3214 | void SendImprovedTerseObjectUpdate(SceneObjectPart obj, PrimFlags creatorFlags, PrimUpdateFlags updateFlags) | ||
3215 | { | ||
3216 | } | ||
3217 | |||
3218 | void UpdateFlagsToPacketType(PrimFlags creatorFlags, PrimUpdateFlags updateFlags, out bool canUseCompressed, out bool canUseImproved) | ||
3219 | { | ||
3220 | canUseCompressed = true; | ||
3221 | canUseImproved = true; | ||
3222 | |||
3223 | if ((updateFlags & PrimUpdateFlags.FullUpdate) == PrimUpdateFlags.FullUpdate || creatorFlags != PrimFlags.None) | ||
3224 | { | ||
3225 | canUseCompressed = false; | ||
3226 | canUseImproved = false; | ||
3227 | } | ||
3228 | else | ||
3229 | { | ||
3230 | if ((updateFlags & PrimUpdateFlags.Velocity) != 0 || | ||
3231 | (updateFlags & PrimUpdateFlags.Acceleration) != 0 || | ||
3232 | (updateFlags & PrimUpdateFlags.CollisionPlane) != 0 || | ||
3233 | (updateFlags & PrimUpdateFlags.Joint) != 0) | ||
3234 | { | ||
3235 | canUseCompressed = false; | ||
3236 | } | ||
3237 | |||
3238 | if ((updateFlags & PrimUpdateFlags.PrimFlags) != 0 || | ||
3239 | (updateFlags & PrimUpdateFlags.ParentID) != 0 || | ||
3240 | (updateFlags & PrimUpdateFlags.Scale) != 0 || | ||
3241 | (updateFlags & PrimUpdateFlags.PrimData) != 0 || | ||
3242 | (updateFlags & PrimUpdateFlags.Text) != 0 || | ||
3243 | (updateFlags & PrimUpdateFlags.NameValue) != 0 || | ||
3244 | (updateFlags & PrimUpdateFlags.ExtraData) != 0 || | ||
3245 | (updateFlags & PrimUpdateFlags.TextureAnim) != 0 || | ||
3246 | (updateFlags & PrimUpdateFlags.Sound) != 0 || | ||
3247 | (updateFlags & PrimUpdateFlags.Particles) != 0 || | ||
3248 | (updateFlags & PrimUpdateFlags.Material) != 0 || | ||
3249 | (updateFlags & PrimUpdateFlags.ClickAction) != 0 || | ||
3250 | (updateFlags & PrimUpdateFlags.MediaURL) != 0 || | ||
3251 | (updateFlags & PrimUpdateFlags.Joint) != 0) | ||
3252 | { | ||
3253 | canUseImproved = false; | ||
3254 | } | ||
3255 | } | ||
3256 | } | ||
3257 | |||
3258 | static ObjectUpdatePacket.ObjectDataBlock BuildUpdateBlockFromPrim(SceneObjectPart prim, UUID assetID, PrimFlags flags, uint crc) | ||
3259 | { | ||
3260 | byte[] objectData = new byte[60]; | ||
3261 | prim.OffsetPosition.ToBytes(objectData, 0); | ||
3262 | prim.Velocity.ToBytes(objectData, 12); | ||
3263 | prim.Acceleration.ToBytes(objectData, 24); | ||
3264 | prim.RotationOffset.ToBytes(objectData, 36); | ||
3265 | prim.AngularVelocity.ToBytes(objectData, 48); | ||
3266 | |||
3267 | ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); | ||
3268 | update.ClickAction = (byte)prim.ClickAction; | ||
3269 | update.CRC = crc; | ||
3270 | update.ExtraParams = prim.Shape.ExtraParams ?? Utils.EmptyBytes; | ||
3271 | update.Flags = (byte)flags; | ||
3272 | update.FullID = prim.UUID; | ||
3273 | update.ID = prim.LocalId; | ||
3274 | //update.JointAxisOrAnchor = Vector3.Zero; // These are deprecated | ||
3275 | //update.JointPivot = Vector3.Zero; | ||
3276 | //update.JointType = 0; | ||
3277 | update.Material = prim.Material; | ||
3278 | update.MediaURL = Utils.EmptyBytes; // FIXME: Support this in OpenSim | ||
3279 | if (prim.IsAttachment) | ||
3280 | update.NameValue = Util.StringToBytes256("AttachItemID STRING RW SV " + assetID); | ||
3281 | else | ||
3282 | update.NameValue = Utils.EmptyBytes; | ||
3283 | update.ObjectData = objectData; | ||
3284 | update.ParentID = prim.ParentID; | ||
3285 | update.PathBegin = prim.Shape.PathBegin; | ||
3286 | update.PathCurve = prim.Shape.PathCurve; | ||
3287 | update.PathEnd = prim.Shape.PathEnd; | ||
3288 | update.PathRadiusOffset = prim.Shape.PathRadiusOffset; | ||
3289 | update.PathRevolutions = prim.Shape.PathRevolutions; | ||
3290 | update.PathScaleX = prim.Shape.PathScaleX; | ||
3291 | update.PathScaleY = prim.Shape.PathScaleY; | ||
3292 | update.PathShearX = prim.Shape.PathShearX; | ||
3293 | update.PathShearY = prim.Shape.PathShearY; | ||
3294 | update.PathSkew = prim.Shape.PathSkew; | ||
3295 | update.PathTaperX = prim.Shape.PathTaperX; | ||
3296 | update.PathTaperY = prim.Shape.PathTaperY; | ||
3297 | update.PathTwist = prim.Shape.PathTwist; | ||
3298 | update.PathTwistBegin = prim.Shape.PathTwistBegin; | ||
3299 | update.PCode = prim.Shape.PCode; | ||
3300 | update.ProfileBegin = prim.Shape.ProfileBegin; | ||
3301 | update.ProfileCurve = prim.Shape.ProfileCurve; | ||
3302 | update.ProfileEnd = prim.Shape.ProfileEnd; | ||
3303 | update.ProfileHollow = prim.Shape.ProfileHollow; | ||
3304 | update.PSBlock = prim.ParticleSystem ?? Utils.EmptyBytes; | ||
3305 | update.TextColor = new Color4(prim.Color).GetBytes(true); | ||
3306 | update.TextureAnim = prim.TextureAnimation ?? Utils.EmptyBytes; | ||
3307 | update.TextureEntry = prim.Shape.TextureEntry ?? Utils.EmptyBytes; | ||
3308 | update.Scale = prim.Scale; | ||
3309 | update.State = prim.Shape.State; | ||
3310 | update.Text = Util.StringToBytes256(prim.Text); | ||
3311 | update.UpdateFlags = (uint)flags; | ||
3312 | |||
3313 | if (prim.Sound != UUID.Zero) | ||
3314 | { | ||
3315 | update.Sound = prim.Sound; | ||
3316 | update.OwnerID = prim.OwnerID; | ||
3317 | update.Gain = (float)prim.SoundGain; | ||
3318 | update.Radius = (float)prim.SoundRadius; | ||
3319 | } | ||
3320 | |||
3321 | switch ((PCode)prim.Shape.PCode) | ||
3322 | { | ||
3323 | case PCode.Grass: | ||
3324 | case PCode.Tree: | ||
3325 | case PCode.NewTree: | ||
3326 | update.Data = new byte[] { prim.Shape.State }; | ||
3327 | break; | ||
3328 | default: | ||
3329 | // TODO: Support ScratchPad | ||
3330 | //if (prim.ScratchPad != null) | ||
3331 | //{ | ||
3332 | // update.Data = new byte[prim.ScratchPad.Length]; | ||
3333 | // Buffer.BlockCopy(prim.ScratchPad, 0, update.Data, 0, update.Data.Length); | ||
3334 | //} | ||
3335 | //else | ||
3336 | //{ | ||
3337 | // update.Data = Utils.EmptyBytes; | ||
3338 | //} | ||
3339 | update.Data = Utils.EmptyBytes; | ||
3340 | break; | ||
3341 | } | ||
3342 | |||
3343 | return update; | ||
3344 | }*/ | ||
3345 | |||
3346 | #endregion Prim/Avatar Updates | ||
3347 | |||
3162 | #region Avatar Packet/data sending Methods | 3348 | #region Avatar Packet/data sending Methods |
3163 | 3349 | ||
3164 | /// <summary> | 3350 | /// <summary> |
3165 | /// send a objectupdate packet with information about the clients avatar | 3351 | /// send a objectupdate packet with information about the clients avatar |
3166 | /// </summary> | 3352 | /// </summary> |
3167 | public void SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, | 3353 | public void SendAvatarData(SendAvatarData data) |
3168 | uint avatarLocalID, Vector3 Pos, byte[] textureEntry, uint parentID, Quaternion rotation) | ||
3169 | { | 3354 | { |
3170 | ObjectUpdatePacket objupdate = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); | 3355 | ObjectUpdatePacket objupdate = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); |
3171 | // TODO: don't create new blocks if recycling an old packet | 3356 | // TODO: don't create new blocks if recycling an old packet |
3172 | objupdate.RegionData.RegionHandle = regionHandle; | 3357 | objupdate.RegionData.RegionHandle = data.regionHandle; |
3173 | objupdate.RegionData.TimeDilation = ushort.MaxValue; | 3358 | objupdate.RegionData.TimeDilation = ushort.MaxValue; |
3174 | objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; | 3359 | objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; |
3175 | objupdate.ObjectData[0] = CreateDefaultAvatarPacket(textureEntry); | 3360 | objupdate.ObjectData[0] = CreateDefaultAvatarPacket(data.textureEntry); |
3176 | 3361 | ||
3177 | //give this avatar object a local id and assign the user a name | 3362 | //give this avatar object a local id and assign the user a name |
3178 | objupdate.ObjectData[0].ID = avatarLocalID; | 3363 | objupdate.ObjectData[0].ID = data.avatarLocalID; |
3179 | objupdate.ObjectData[0].FullID = avatarID; | 3364 | objupdate.ObjectData[0].FullID = data.avatarID; |
3180 | objupdate.ObjectData[0].ParentID = parentID; | 3365 | objupdate.ObjectData[0].ParentID = data.parentID; |
3181 | objupdate.ObjectData[0].NameValue = | 3366 | objupdate.ObjectData[0].NameValue = |
3182 | Utils.StringToBytes("FirstName STRING RW SV " + firstName + "\nLastName STRING RW SV " + lastName + "\nTitle STRING RW SV " + grouptitle); | 3367 | Utils.StringToBytes("FirstName STRING RW SV " + data.firstName + "\nLastName STRING RW SV " + data.lastName + "\nTitle STRING RW SV " + data.grouptitle); |
3183 | 3368 | ||
3184 | Vector3 pos2 = new Vector3(Pos.X, Pos.Y, Pos.Z); | 3369 | Vector3 pos2 = new Vector3(data.Pos.X, data.Pos.Y, data.Pos.Z); |
3185 | byte[] pb = pos2.GetBytes(); | 3370 | byte[] pb = pos2.GetBytes(); |
3186 | Array.Copy(pb, 0, objupdate.ObjectData[0].ObjectData, 16, pb.Length); | 3371 | Array.Copy(pb, 0, objupdate.ObjectData[0].ObjectData, 16, pb.Length); |
3187 | 3372 | ||
3188 | byte[] rot = rotation.GetBytes(); | 3373 | byte[] rot = data.rotation.GetBytes(); |
3189 | Array.Copy(rot, 0, objupdate.ObjectData[0].ObjectData, 52, rot.Length); | 3374 | Array.Copy(rot, 0, objupdate.ObjectData[0].ObjectData, 52, rot.Length); |
3190 | 3375 | ||
3191 | objupdate.Header.Zerocoded = true; | 3376 | objupdate.Header.Zerocoded = true; |
@@ -3196,38 +3381,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3196 | /// Send a terse positional/rotation/velocity update about an avatar | 3381 | /// Send a terse positional/rotation/velocity update about an avatar |
3197 | /// to the client. This avatar can be that of the client itself. | 3382 | /// to the client. This avatar can be that of the client itself. |
3198 | /// </summary> | 3383 | /// </summary> |
3199 | public virtual void SendAvatarTerseUpdate(ulong regionHandle, | 3384 | public virtual void SendAvatarTerseUpdate(SendAvatarTerseData data) |
3200 | ushort timeDilation, uint localID, Vector3 position, | ||
3201 | Vector3 velocity, Quaternion rotation, UUID agentid) | ||
3202 | { | 3385 | { |
3386 | if (data.priority == double.NaN) | ||
3387 | { | ||
3388 | m_log.Error("[LLClientView] SendAvatarTerseUpdate received a NaN priority, dropping update"); | ||
3389 | return; | ||
3390 | } | ||
3391 | |||
3392 | Quaternion rotation = data.rotation; | ||
3393 | |||
3203 | if (rotation.X == rotation.Y && | 3394 | if (rotation.X == rotation.Y && |
3204 | rotation.Y == rotation.Z && | 3395 | rotation.Y == rotation.Z && |
3205 | rotation.Z == rotation.W && rotation.W == 0) | 3396 | rotation.Z == rotation.W && rotation.W == 0) |
3206 | rotation = Quaternion.Identity; | 3397 | rotation = Quaternion.Identity; |
3207 | 3398 | ||
3208 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock terseBlock = | 3399 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock terseBlock = |
3209 | CreateAvatarImprovedBlock(localID, position, velocity,rotation); | 3400 | CreateAvatarImprovedBlock(data.localID, data.position, data.velocity, rotation); |
3210 | 3401 | ||
3211 | lock (m_avatarTerseUpdates) | 3402 | lock (m_avatarTerseUpdates.SyncRoot) |
3212 | { | 3403 | 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 | } | 3404 | } |
3227 | 3405 | ||
3228 | private void ProcessAvatarTerseUpdates(object sender, ElapsedEventArgs e) | 3406 | private void ProcessAvatarTerseUpdates() |
3229 | { | 3407 | { |
3230 | lock (m_avatarTerseUpdates) | 3408 | lock (m_avatarTerseUpdates.SyncRoot) |
3231 | { | 3409 | { |
3232 | ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); | 3410 | ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); |
3233 | 3411 | ||
@@ -3247,34 +3425,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3247 | byte[] zerobuffer = new byte[1024]; | 3425 | byte[] zerobuffer = new byte[1024]; |
3248 | byte[] blockbuffer = new byte[1024]; | 3426 | byte[] blockbuffer = new byte[1024]; |
3249 | 3427 | ||
3428 | Queue<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> updates = new Queue<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>(); | ||
3429 | |||
3250 | for (count = 0 ; count < max ; count++) | 3430 | for (count = 0 ; count < max ; count++) |
3251 | { | 3431 | { |
3252 | int length = 0; | 3432 | int length = 0; |
3253 | m_avatarTerseUpdates[count].ToBytes(blockbuffer, ref length); | 3433 | m_avatarTerseUpdates.Peek().ToBytes(blockbuffer, ref length); |
3254 | length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); | 3434 | length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); |
3255 | if (size + length > Packet.MTU) | 3435 | if (size + length > Packet.MTU) |
3256 | break; | 3436 | break; |
3257 | size += length; | 3437 | size += length; |
3438 | updates.Enqueue(m_avatarTerseUpdates.Dequeue()); | ||
3258 | } | 3439 | } |
3259 | 3440 | ||
3260 | terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; | 3441 | terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; |
3261 | 3442 | ||
3262 | for (int i = 0 ; i < count ; i++) | 3443 | for (int i = 0 ; i < count ; i++) |
3263 | { | 3444 | terse.ObjectData[i] = updates.Dequeue(); |
3264 | terse.ObjectData[i] = m_avatarTerseUpdates[0]; | ||
3265 | m_avatarTerseUpdates.RemoveAt(0); | ||
3266 | } | ||
3267 | 3445 | ||
3268 | terse.Header.Reliable = false; | 3446 | terse.Header.Reliable = false; |
3269 | terse.Header.Zerocoded = true; | 3447 | terse.Header.Zerocoded = true; |
3270 | // FIXME: Move this to ThrottleOutPacketType.State when the real prioritization code is committed | ||
3271 | OutPacket(terse, ThrottleOutPacketType.Task); | ||
3272 | 3448 | ||
3273 | if (m_avatarTerseUpdates.Count == 0) | 3449 | OutPacket(terse, ThrottleOutPacketType.State); |
3274 | { | ||
3275 | lock (m_avatarTerseUpdateTimer) | ||
3276 | m_avatarTerseUpdateTimer.Stop(); | ||
3277 | } | ||
3278 | } | 3450 | } |
3279 | } | 3451 | } |
3280 | 3452 | ||
@@ -3342,54 +3514,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3342 | OutPacket(attach, ThrottleOutPacketType.Task); | 3514 | OutPacket(attach, ThrottleOutPacketType.Task); |
3343 | } | 3515 | } |
3344 | 3516 | ||
3345 | public void SendPrimitiveToClient( | 3517 | 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 | { | 3518 | { |
3351 | byte[] textureanim = new byte[0]; | 3519 | if (data.priority == double.NaN) |
3352 | 3520 | { | |
3353 | SendPrimitiveToClient(regionHandle, timeDilation, localID, primShape, pos, vel, | 3521 | m_log.Error("[LLClientView] SendPrimitiveToClient received a NaN priority, dropping update"); |
3354 | acc, rotation, rvel, flags, | 3522 | return; |
3355 | objectID, ownerID, text, color, parentID, particleSystem, | 3523 | } |
3356 | clickAction, material, textureanim, false, 0, UUID.Zero, UUID.Zero, 0, 0, 0); | ||
3357 | } | ||
3358 | 3524 | ||
3359 | public void SendPrimitiveToClient( | 3525 | 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 | 3526 | ||
3367 | if (AttachPoint > 30 && ownerID != AgentId) // Someone else's HUD | 3527 | if (data.AttachPoint > 30 && data.ownerID != AgentId) // Someone else's HUD |
3368 | return; | 3528 | return; |
3369 | if (primShape.PCode == 9 && primShape.State != 0 && parentID == 0) | 3529 | if (data.primShape.PCode == 9 && data.primShape.State != 0 && data.parentID == 0) |
3370 | return; | 3530 | return; |
3371 | 3531 | ||
3372 | if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) | 3532 | if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0.0f) |
3373 | rotation = Quaternion.Identity; | 3533 | rotation = Quaternion.Identity; |
3374 | 3534 | ||
3375 | ObjectUpdatePacket.ObjectDataBlock objectData = CreatePrimUpdateBlock(primShape, flags); | 3535 | ObjectUpdatePacket.ObjectDataBlock objectData = CreatePrimUpdateBlock(data.primShape, data.flags); |
3376 | 3536 | ||
3377 | objectData.ID = localID; | 3537 | objectData.ID = data.localID; |
3378 | objectData.FullID = objectID; | 3538 | objectData.FullID = data.objectID; |
3379 | objectData.OwnerID = ownerID; | 3539 | objectData.OwnerID = data.ownerID; |
3380 | 3540 | ||
3381 | objectData.Text = Util.StringToBytes256(text); | 3541 | objectData.Text = Util.StringToBytes256(data.text); |
3382 | objectData.TextColor[0] = color[0]; | 3542 | objectData.TextColor[0] = data.color[0]; |
3383 | objectData.TextColor[1] = color[1]; | 3543 | objectData.TextColor[1] = data.color[1]; |
3384 | objectData.TextColor[2] = color[2]; | 3544 | objectData.TextColor[2] = data.color[2]; |
3385 | objectData.TextColor[3] = color[3]; | 3545 | objectData.TextColor[3] = data.color[3]; |
3386 | objectData.ParentID = parentID; | 3546 | objectData.ParentID = data.parentID; |
3387 | objectData.PSBlock = particleSystem; | 3547 | objectData.PSBlock = data.particleSystem; |
3388 | objectData.ClickAction = clickAction; | 3548 | objectData.ClickAction = data.clickAction; |
3389 | objectData.Material = material; | 3549 | objectData.Material = data.material; |
3390 | objectData.Flags = 0; | 3550 | objectData.Flags = 0; |
3391 | 3551 | ||
3392 | if (attachment) | 3552 | if (data.attachment) |
3393 | { | 3553 | { |
3394 | // Necessary??? | 3554 | // Necessary??? |
3395 | objectData.JointAxisOrAnchor = new Vector3(0, 0, 2); | 3555 | objectData.JointAxisOrAnchor = new Vector3(0, 0, 2); |
@@ -3397,14 +3557,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3397 | 3557 | ||
3398 | // Item from inventory??? | 3558 | // Item from inventory??? |
3399 | objectData.NameValue = | 3559 | objectData.NameValue = |
3400 | Utils.StringToBytes("AttachItemID STRING RW SV " + AssetId.Guid); | 3560 | Utils.StringToBytes("AttachItemID STRING RW SV " + data.AssetId.Guid); |
3401 | objectData.State = (byte)((AttachPoint % 16) * 16 + (AttachPoint / 16)); | 3561 | objectData.State = (byte)((data.AttachPoint % 16) * 16 + (data.AttachPoint / 16)); |
3402 | } | 3562 | } |
3403 | 3563 | ||
3404 | // Xantor 20080528: Send sound info as well | 3564 | // Xantor 20080528: Send sound info as well |
3405 | // Xantor 20080530: Zero out everything if there's no SoundId, so zerocompression will work again | 3565 | // Xantor 20080530: Zero out everything if there's no SoundId, so zerocompression will work again |
3406 | objectData.Sound = SoundId; | 3566 | objectData.Sound = data.SoundId; |
3407 | if (SoundId == UUID.Zero) | 3567 | if (data.SoundId == UUID.Zero) |
3408 | { | 3568 | { |
3409 | objectData.OwnerID = UUID.Zero; | 3569 | objectData.OwnerID = UUID.Zero; |
3410 | objectData.Gain = 0.0f; | 3570 | objectData.Gain = 0.0f; |
@@ -3413,39 +3573,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3413 | } | 3573 | } |
3414 | else | 3574 | else |
3415 | { | 3575 | { |
3416 | objectData.OwnerID = ownerID; | 3576 | objectData.OwnerID = data.ownerID; |
3417 | objectData.Gain = (float)SoundGain; | 3577 | objectData.Gain = (float)data.SoundVolume; |
3418 | objectData.Radius = (float)SoundRadius; | 3578 | objectData.Radius = (float)data.SoundRadius; |
3419 | objectData.Flags = SoundFlags; | 3579 | objectData.Flags = data.SoundFlags; |
3420 | } | 3580 | } |
3421 | 3581 | ||
3422 | byte[] pb = pos.GetBytes(); | 3582 | byte[] pb = data.pos.GetBytes(); |
3423 | Array.Copy(pb, 0, objectData.ObjectData, 0, pb.Length); | 3583 | Buffer.BlockCopy(pb, 0, objectData.ObjectData, 0, pb.Length); |
3424 | 3584 | ||
3425 | byte[] vel = velocity.GetBytes(); | 3585 | byte[] vel = data.vel.GetBytes(); |
3426 | Array.Copy(vel, 0, objectData.ObjectData, pb.Length, vel.Length); | 3586 | Buffer.BlockCopy(vel, 0, objectData.ObjectData, pb.Length, vel.Length); |
3427 | 3587 | ||
3428 | byte[] rot = rotation.GetBytes(); | 3588 | byte[] rot = rotation.GetBytes(); |
3429 | Array.Copy(rot, 0, objectData.ObjectData, 36, rot.Length); | 3589 | Buffer.BlockCopy(rot, 0, objectData.ObjectData, 36, rot.Length); |
3430 | 3590 | ||
3431 | byte[] rvel = rotational_velocity.GetBytes(); | 3591 | byte[] rvel = data.rvel.GetBytes(); |
3432 | Array.Copy(rvel, 0, objectData.ObjectData, 36 + rot.Length, rvel.Length); | 3592 | Buffer.BlockCopy(rvel, 0, objectData.ObjectData, 36 + rot.Length, rvel.Length); |
3433 | 3593 | ||
3434 | if (textureanim.Length > 0) | 3594 | if (data.textureanim.Length > 0) |
3435 | { | 3595 | { |
3436 | objectData.TextureAnim = textureanim; | 3596 | objectData.TextureAnim = data.textureanim; |
3437 | } | 3597 | } |
3438 | 3598 | ||
3439 | lock (m_primFullUpdates) | 3599 | lock (m_primFullUpdates.SyncRoot) |
3440 | { | 3600 | 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 | } | 3601 | } |
3450 | 3602 | ||
3451 | void HandleQueueEmpty(ThrottleOutPacketType queue) | 3603 | void HandleQueueEmpty(ThrottleOutPacketType queue) |
@@ -3455,6 +3607,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3455 | case ThrottleOutPacketType.Texture: | 3607 | case ThrottleOutPacketType.Texture: |
3456 | ProcessTextureRequests(); | 3608 | ProcessTextureRequests(); |
3457 | break; | 3609 | break; |
3610 | case ThrottleOutPacketType.State: | ||
3611 | int count = 0; | ||
3612 | |||
3613 | lock (m_avatarTerseUpdates.SyncRoot) | ||
3614 | count = m_avatarTerseUpdates.Count; | ||
3615 | if (count > 0) | ||
3616 | { | ||
3617 | ProcessAvatarTerseUpdates(); | ||
3618 | return; | ||
3619 | } | ||
3620 | |||
3621 | lock (m_primFullUpdates.SyncRoot) | ||
3622 | count = m_primFullUpdates.Count; | ||
3623 | if (count > 0) | ||
3624 | { | ||
3625 | ProcessPrimFullUpdates(); | ||
3626 | return; | ||
3627 | } | ||
3628 | |||
3629 | lock (m_primTerseUpdates.SyncRoot) | ||
3630 | count = m_primTerseUpdates.Count; | ||
3631 | if (count > 0) | ||
3632 | { | ||
3633 | ProcessPrimTerseUpdates(); | ||
3634 | return; | ||
3635 | } | ||
3636 | break; | ||
3458 | } | 3637 | } |
3459 | } | 3638 | } |
3460 | 3639 | ||
@@ -3464,18 +3643,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3464 | m_imageManager.ProcessImageQueue(m_textureSendLimit); | 3643 | m_imageManager.ProcessImageQueue(m_textureSendLimit); |
3465 | } | 3644 | } |
3466 | 3645 | ||
3467 | void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e) | 3646 | void ProcessPrimFullUpdates() |
3468 | { | 3647 | { |
3469 | lock (m_primFullUpdates) | 3648 | lock (m_primFullUpdates.SyncRoot) |
3470 | { | 3649 | { |
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 = | 3650 | ObjectUpdatePacket outPacket = |
3480 | (ObjectUpdatePacket)PacketPool.Instance.GetPacket( | 3651 | (ObjectUpdatePacket)PacketPool.Instance.GetPacket( |
3481 | PacketType.ObjectUpdate); | 3652 | PacketType.ObjectUpdate); |
@@ -3495,74 +3666,63 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3495 | byte[] zerobuffer = new byte[1024]; | 3666 | byte[] zerobuffer = new byte[1024]; |
3496 | byte[] blockbuffer = new byte[1024]; | 3667 | byte[] blockbuffer = new byte[1024]; |
3497 | 3668 | ||
3669 | Queue<ObjectUpdatePacket.ObjectDataBlock> updates = new Queue<ObjectUpdatePacket.ObjectDataBlock>(); | ||
3670 | |||
3498 | for (count = 0 ; count < max ; count++) | 3671 | for (count = 0 ; count < max ; count++) |
3499 | { | 3672 | { |
3500 | int length = 0; | 3673 | int length = 0; |
3501 | m_primFullUpdates[count].ToBytes(blockbuffer, ref length); | 3674 | m_primFullUpdates.Peek().ToBytes(blockbuffer, ref length); |
3502 | length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); | 3675 | length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); |
3503 | if (size + length > Packet.MTU) | 3676 | if (size + length > Packet.MTU) |
3504 | break; | 3677 | break; |
3505 | size += length; | 3678 | size += length; |
3679 | updates.Enqueue(m_primFullUpdates.Dequeue()); | ||
3506 | } | 3680 | } |
3507 | 3681 | ||
3508 | outPacket.ObjectData = | 3682 | outPacket.ObjectData = |
3509 | new ObjectUpdatePacket.ObjectDataBlock[count]; | 3683 | new ObjectUpdatePacket.ObjectDataBlock[count]; |
3510 | 3684 | ||
3511 | for (int index = 0 ; index < count ; index++) | 3685 | for (int index = 0 ; index < count ; index++) |
3512 | { | 3686 | outPacket.ObjectData[index] = updates.Dequeue(); |
3513 | outPacket.ObjectData[index] = m_primFullUpdates[0]; | ||
3514 | m_primFullUpdates.RemoveAt(0); | ||
3515 | } | ||
3516 | 3687 | ||
3517 | outPacket.Header.Zerocoded = true; | 3688 | outPacket.Header.Zerocoded = true; |
3518 | OutPacket(outPacket, ThrottleOutPacketType.State); | 3689 | OutPacket(outPacket, ThrottleOutPacketType.State); |
3519 | |||
3520 | if (m_primFullUpdates.Count == 0 && m_primFullUpdateTimer.Enabled) | ||
3521 | lock (m_primFullUpdateTimer) | ||
3522 | m_primFullUpdateTimer.Stop(); | ||
3523 | } | 3690 | } |
3524 | } | 3691 | } |
3525 | 3692 | ||
3526 | /// <summary> | 3693 | /// <summary> |
3527 | /// | 3694 | /// |
3528 | /// </summary> | 3695 | /// </summary> |
3529 | public void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, | 3696 | //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) | 3697 | // Quaternion rotation, Vector3 velocity, Vector3 rotationalvelocity, byte state, UUID AssetId, UUID ownerID, int attachPoint) |
3698 | public void SendPrimTerseUpdate(SendPrimitiveTerseData data) | ||
3531 | { | 3699 | { |
3532 | if (attachPoint > 30 && ownerID != AgentId) // Someone else's HUD | 3700 | if (data.priority == double.NaN) |
3701 | { | ||
3702 | m_log.Error("[LLClientView] SendPrimTerseUpdate received a NaN priority, dropping update"); | ||
3703 | return; | ||
3704 | } | ||
3705 | |||
3706 | Quaternion rotation = data.rotation; | ||
3707 | |||
3708 | if (data.attachPoint > 30 && data.owner != AgentId) // Someone else's HUD | ||
3533 | return; | 3709 | return; |
3534 | 3710 | ||
3535 | if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) | 3711 | if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) |
3536 | rotation = Quaternion.Identity; | 3712 | rotation = Quaternion.Identity; |
3537 | 3713 | ||
3538 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock objectData = | 3714 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock objectData = |
3539 | CreatePrimImprovedBlock(localID, position, rotation, | 3715 | CreatePrimImprovedBlock(data.localID, data.position, rotation, |
3540 | velocity, rotationalvelocity, state); | 3716 | data.velocity, data.rotationalvelocity, data.state); |
3541 | 3717 | ||
3542 | lock (m_primTerseUpdates) | 3718 | lock (m_primTerseUpdates.SyncRoot) |
3543 | { | 3719 | m_primTerseUpdates.Enqueue(data.priority, objectData, data.localID); |
3544 | if (m_primTerseUpdates.Count == 0) | ||
3545 | m_primTerseUpdateTimer.Start(); | ||
3546 | |||
3547 | m_primTerseUpdates.Add(objectData); | ||
3548 | |||
3549 | if (m_primTerseUpdates.Count >= m_primTerseUpdatesPerPacket) | ||
3550 | ProcessPrimTerseUpdates(this, null); | ||
3551 | } | ||
3552 | } | 3720 | } |
3553 | 3721 | ||
3554 | void ProcessPrimTerseUpdates(object sender, ElapsedEventArgs e) | 3722 | void ProcessPrimTerseUpdates() |
3555 | { | 3723 | { |
3556 | lock (m_primTerseUpdates) | 3724 | lock (m_primTerseUpdates.SyncRoot) |
3557 | { | 3725 | { |
3558 | if (m_primTerseUpdates.Count == 0) | ||
3559 | { | ||
3560 | lock (m_primTerseUpdateTimer) | ||
3561 | m_primTerseUpdateTimer.Stop(); | ||
3562 | |||
3563 | return; | ||
3564 | } | ||
3565 | |||
3566 | ImprovedTerseObjectUpdatePacket outPacket = | 3726 | ImprovedTerseObjectUpdatePacket outPacket = |
3567 | (ImprovedTerseObjectUpdatePacket) | 3727 | (ImprovedTerseObjectUpdatePacket) |
3568 | PacketPool.Instance.GetPacket( | 3728 | PacketPool.Instance.GetPacket( |
@@ -3583,14 +3743,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3583 | byte[] zerobuffer = new byte[1024]; | 3743 | byte[] zerobuffer = new byte[1024]; |
3584 | byte[] blockbuffer = new byte[1024]; | 3744 | byte[] blockbuffer = new byte[1024]; |
3585 | 3745 | ||
3746 | Queue<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> updates = new Queue<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>(); | ||
3747 | |||
3586 | for (count = 0 ; count < max ; count++) | 3748 | for (count = 0 ; count < max ; count++) |
3587 | { | 3749 | { |
3588 | int length = 0; | 3750 | int length = 0; |
3589 | m_primTerseUpdates[count].ToBytes(blockbuffer, ref length); | 3751 | m_primTerseUpdates.Peek().ToBytes(blockbuffer, ref length); |
3590 | length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); | 3752 | length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); |
3591 | if (size + length > Packet.MTU) | 3753 | if (size + length > Packet.MTU) |
3592 | break; | 3754 | break; |
3593 | size += length; | 3755 | size += length; |
3756 | updates.Enqueue(m_primTerseUpdates.Dequeue()); | ||
3594 | } | 3757 | } |
3595 | 3758 | ||
3596 | outPacket.ObjectData = | 3759 | outPacket.ObjectData = |
@@ -3598,18 +3761,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3598 | ObjectDataBlock[count]; | 3761 | ObjectDataBlock[count]; |
3599 | 3762 | ||
3600 | for (int index = 0 ; index < count ; index++) | 3763 | for (int index = 0 ; index < count ; index++) |
3601 | { | 3764 | outPacket.ObjectData[index] = updates.Dequeue(); |
3602 | outPacket.ObjectData[index] = m_primTerseUpdates[0]; | ||
3603 | m_primTerseUpdates.RemoveAt(0); | ||
3604 | } | ||
3605 | 3765 | ||
3606 | outPacket.Header.Reliable = false; | 3766 | outPacket.Header.Reliable = false; |
3607 | outPacket.Header.Zerocoded = true; | 3767 | outPacket.Header.Zerocoded = true; |
3608 | OutPacket(outPacket, ThrottleOutPacketType.State); | 3768 | OutPacket(outPacket, ThrottleOutPacketType.State); |
3609 | |||
3610 | if (m_primTerseUpdates.Count == 0) | ||
3611 | lock (m_primTerseUpdateTimer) | ||
3612 | m_primTerseUpdateTimer.Stop(); | ||
3613 | } | 3769 | } |
3614 | } | 3770 | } |
3615 | 3771 | ||
@@ -3617,15 +3773,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3617 | { | 3773 | { |
3618 | while (m_primFullUpdates.Count > 0) | 3774 | while (m_primFullUpdates.Count > 0) |
3619 | { | 3775 | { |
3620 | ProcessPrimFullUpdates(this, null); | 3776 | ProcessPrimFullUpdates(); |
3621 | } | 3777 | } |
3622 | while (m_primTerseUpdates.Count > 0) | 3778 | while (m_primTerseUpdates.Count > 0) |
3623 | { | 3779 | { |
3624 | ProcessPrimTerseUpdates(this, null); | 3780 | ProcessPrimTerseUpdates(); |
3625 | } | 3781 | } |
3626 | while (m_avatarTerseUpdates.Count > 0) | 3782 | while (m_avatarTerseUpdates.Count > 0) |
3627 | { | 3783 | { |
3628 | ProcessAvatarTerseUpdates(this, null); | 3784 | ProcessAvatarTerseUpdates(); |
3629 | } | 3785 | } |
3630 | } | 3786 | } |
3631 | 3787 | ||
@@ -10371,5 +10527,136 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
10371 | pack.TextureData.TextureID = textureID; | 10527 | pack.TextureData.TextureID = textureID; |
10372 | OutPacket(pack, ThrottleOutPacketType.Task); | 10528 | OutPacket(pack, ThrottleOutPacketType.Task); |
10373 | } | 10529 | } |
10530 | |||
10531 | #region PriorityQueue | ||
10532 | private class PriorityQueue<TPriority, TValue> | ||
10533 | { | ||
10534 | private MinHeap<MinHeapItem>[] heaps = new MinHeap<MinHeapItem>[1]; | ||
10535 | private Dictionary<uint, LookupItem> lookup_table = new Dictionary<uint, LookupItem>(); | ||
10536 | private Comparison<TPriority> comparison; | ||
10537 | private object sync_root = new object(); | ||
10538 | |||
10539 | internal PriorityQueue() : | ||
10540 | this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY, Comparer<TPriority>.Default) { } | ||
10541 | internal PriorityQueue(int capacity) : | ||
10542 | this(capacity, Comparer<TPriority>.Default) { } | ||
10543 | internal PriorityQueue(IComparer<TPriority> comparer) : | ||
10544 | this(new Comparison<TPriority>(comparer.Compare)) { } | ||
10545 | internal PriorityQueue(Comparison<TPriority> comparison) : | ||
10546 | this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY, comparison) { } | ||
10547 | internal PriorityQueue(int capacity, IComparer<TPriority> comparer) : | ||
10548 | this(capacity, new Comparison<TPriority>(comparer.Compare)) { } | ||
10549 | internal PriorityQueue(int capacity, Comparison<TPriority> comparison) | ||
10550 | { | ||
10551 | for (int i = 0; i < heaps.Length; ++i) | ||
10552 | heaps[i] = new MinHeap<MinHeapItem>(capacity); | ||
10553 | this.comparison = comparison; | ||
10554 | } | ||
10555 | |||
10556 | internal object SyncRoot { get { return this.sync_root; } } | ||
10557 | internal int Count | ||
10558 | { | ||
10559 | get | ||
10560 | { | ||
10561 | int count = 0; | ||
10562 | for (int i = 0; i < heaps.Length; ++i) | ||
10563 | count = heaps[i].Count; | ||
10564 | return count; | ||
10565 | } | ||
10566 | } | ||
10567 | |||
10568 | internal bool Enqueue(TPriority priority, TValue value, uint local_id) | ||
10569 | { | ||
10570 | LookupItem item; | ||
10571 | |||
10572 | if (lookup_table.TryGetValue(local_id, out item)) | ||
10573 | { | ||
10574 | item.Heap[item.Handle] = new MinHeapItem(priority, value, local_id, this.comparison); | ||
10575 | return false; | ||
10576 | } | ||
10577 | else | ||
10578 | { | ||
10579 | item.Heap = heaps[0]; | ||
10580 | item.Heap.Add(new MinHeapItem(priority, value, local_id, this.comparison), ref item.Handle); | ||
10581 | lookup_table.Add(local_id, item); | ||
10582 | return true; | ||
10583 | } | ||
10584 | } | ||
10585 | |||
10586 | internal TValue Peek() | ||
10587 | { | ||
10588 | for (int i = 0; i < heaps.Length; ++i) | ||
10589 | if (heaps[i].Count > 0) | ||
10590 | return heaps[i].Min().Value; | ||
10591 | throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); | ||
10592 | } | ||
10593 | |||
10594 | internal TValue Dequeue() | ||
10595 | { | ||
10596 | for (int i = 0; i < heaps.Length; ++i) | ||
10597 | { | ||
10598 | if (heaps[i].Count > 0) | ||
10599 | { | ||
10600 | MinHeapItem item = heaps[i].RemoveMin(); | ||
10601 | lookup_table.Remove(item.LocalID); | ||
10602 | return item.Value; | ||
10603 | } | ||
10604 | } | ||
10605 | throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); | ||
10606 | } | ||
10607 | |||
10608 | #region MinHeapItem | ||
10609 | private struct MinHeapItem : IComparable<MinHeapItem> | ||
10610 | { | ||
10611 | private TPriority priority; | ||
10612 | private TValue value; | ||
10613 | private uint local_id; | ||
10614 | private Comparison<TPriority> comparison; | ||
10615 | |||
10616 | internal MinHeapItem(TPriority priority, TValue value, uint local_id) : | ||
10617 | this(priority, value, local_id, Comparer<TPriority>.Default) { } | ||
10618 | internal MinHeapItem(TPriority priority, TValue value, uint local_id, IComparer<TPriority> comparer) : | ||
10619 | this(priority, value, local_id, new Comparison<TPriority>(comparer.Compare)) { } | ||
10620 | internal MinHeapItem(TPriority priority, TValue value, uint local_id, Comparison<TPriority> comparison) | ||
10621 | { | ||
10622 | this.priority = priority; | ||
10623 | this.value = value; | ||
10624 | this.local_id = local_id; | ||
10625 | this.comparison = comparison; | ||
10626 | } | ||
10627 | |||
10628 | internal TPriority Priority { get { return this.priority; } } | ||
10629 | internal TValue Value { get { return this.value; } } | ||
10630 | internal uint LocalID { get { return this.local_id; } } | ||
10631 | |||
10632 | public override string ToString() | ||
10633 | { | ||
10634 | StringBuilder sb = new StringBuilder(); | ||
10635 | sb.Append("["); | ||
10636 | if (this.priority != null) | ||
10637 | sb.Append(this.priority.ToString()); | ||
10638 | sb.Append(","); | ||
10639 | if (this.value != null) | ||
10640 | sb.Append(this.value.ToString()); | ||
10641 | sb.Append("]"); | ||
10642 | return sb.ToString(); | ||
10643 | } | ||
10644 | |||
10645 | public int CompareTo(MinHeapItem other) | ||
10646 | { | ||
10647 | return this.comparison(this.priority, other.priority); | ||
10648 | } | ||
10649 | } | ||
10650 | #endregion | ||
10651 | |||
10652 | #region LookupItem | ||
10653 | private struct LookupItem { | ||
10654 | internal MinHeap<MinHeapItem> Heap; | ||
10655 | internal IHandle Handle; | ||
10656 | } | ||
10657 | #endregion | ||
10658 | } | ||
10659 | #endregion | ||
10660 | |||
10374 | } | 10661 | } |
10375 | } | 10662 | } |
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 4eee6b6..8c42ca4 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, Packet.MTU); |
299 | ceiling = m_defaultThrottleRates.Total; | 301 | land = Math.Max(land, Packet.MTU); |
300 | if (ceiling < Packet.MTU) ceiling = Packet.MTU; | 302 | wind = Math.Max(wind, Packet.MTU); |
301 | } | 303 | cloud = Math.Max(cloud, Packet.MTU); |
302 | 304 | task = Math.Max(task, Packet.MTU); | |
303 | resend = Utils.Clamp(resend, Packet.MTU, ceiling); | 305 | texture = Math.Max(texture, Packet.MTU); |
304 | land = Utils.Clamp(land, Packet.MTU, ceiling); | 306 | asset = Math.Max(asset, Packet.MTU); |
305 | wind = Utils.Clamp(wind, Packet.MTU, ceiling); | 307 | state = Math.Max(state, Packet.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; |
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 545a0bc..b11a80d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | |||
@@ -285,7 +285,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
285 | // The packet grew larger than the bufferSize while zerocoding. | 285 | // The packet grew larger than the bufferSize while zerocoding. |
286 | // Remove the MSG_ZEROCODED flag and send the unencoded data | 286 | // Remove the MSG_ZEROCODED flag and send the unencoded data |
287 | // instead | 287 | // instead |
288 | m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". Removing MSG_ZEROCODED flag"); | 288 | m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength + |
289 | " and BufferLength=" + buffer.Data.Length + ". Removing MSG_ZEROCODED flag"); | ||
289 | data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); | 290 | data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); |
290 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | 291 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); |
291 | } | 292 | } |
@@ -513,7 +514,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
513 | IClientAPI client; | 514 | IClientAPI client; |
514 | if (!m_scene.ClientManager.TryGetValue(address, out client) || !(client is LLClientView)) | 515 | if (!m_scene.ClientManager.TryGetValue(address, out client) || !(client is LLClientView)) |
515 | { | 516 | { |
516 | m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + | 517 | 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"); | 518 | " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_scene.ClientManager.Count + " clients"); |
518 | return; | 519 | return; |
519 | } | 520 | } |