aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs785
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs83
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs39
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
52namespace OpenSim.Region.ClientStack.LindenUDP 52namespace 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);