aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack
diff options
context:
space:
mode:
authorMic Bowman2011-03-28 10:00:53 -0700
committerMic Bowman2011-03-28 10:00:53 -0700
commit61371c7c16eea3b856a852b94c98b18b99ccf8fb (patch)
tree3c01cbe9bf9d487afeb37dcf4334c613ab53bdf8 /OpenSim/Region/ClientStack
parentThe actual change to README.txt about running on Linux. (diff)
downloadopensim-SC-61371c7c16eea3b856a852b94c98b18b99ccf8fb.zip
opensim-SC-61371c7c16eea3b856a852b94c98b18b99ccf8fb.tar.gz
opensim-SC-61371c7c16eea3b856a852b94c98b18b99ccf8fb.tar.bz2
opensim-SC-61371c7c16eea3b856a852b94c98b18b99ccf8fb.tar.xz
Implements adaptive queue management and fair queueing for
improved networking performance. Reprioritization algorithms need to be ported still. One is in place.
Diffstat (limited to 'OpenSim/Region/ClientStack')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs353
1 files changed, 235 insertions, 118 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 2c6795f..db149a1 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -49,6 +49,8 @@ using Timer = System.Timers.Timer;
49using AssetLandmark = OpenSim.Framework.AssetLandmark; 49using AssetLandmark = OpenSim.Framework.AssetLandmark;
50using Nini.Config; 50using Nini.Config;
51 51
52using System.IO;
53
52namespace OpenSim.Region.ClientStack.LindenUDP 54namespace OpenSim.Region.ClientStack.LindenUDP
53{ 55{
54 public delegate bool PacketMethod(IClientAPI simClient, Packet packet); 56 public delegate bool PacketMethod(IClientAPI simClient, Packet packet);
@@ -298,6 +300,77 @@ namespace OpenSim.Region.ClientStack.LindenUDP
298 /// <summary>Used to adjust Sun Orbit values so Linden based viewers properly position sun</summary> 300 /// <summary>Used to adjust Sun Orbit values so Linden based viewers properly position sun</summary>
299 private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; 301 private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f;
300 302
303 // First log file or time has expired, start writing to a new log file
304//<MIC>
305// -----------------------------------------------------------------
306// -----------------------------------------------------------------
307// THIS IS DEBUGGING CODE & SHOULD BE REMOVED
308// -----------------------------------------------------------------
309// -----------------------------------------------------------------
310 public class QueueLogger
311 {
312 public Int32 start = 0;
313 public StreamWriter Log = null;
314 private Dictionary<UUID,int> m_idMap = new Dictionary<UUID,int>();
315
316 public QueueLogger()
317 {
318 DateTime now = DateTime.Now;
319 String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss"));
320 Log = new StreamWriter(fname);
321
322 start = Util.EnvironmentTickCount();
323 }
324
325 public int LookupID(UUID uuid)
326 {
327 int localid;
328 if (! m_idMap.TryGetValue(uuid,out localid))
329 {
330 localid = m_idMap.Count + 1;
331 m_idMap[uuid] = localid;
332 }
333
334 return localid;
335 }
336 }
337
338 public static QueueLogger QueueLog = null;
339
340 // -----------------------------------------------------------------
341 public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue)
342 {
343 if (QueueLog == null)
344 QueueLog = new QueueLogger();
345
346 Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start);
347 lock(QueueLog)
348 {
349 int cid = QueueLog.LookupID(client);
350 int aid = QueueLog.LookupID(avatar);
351 QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue);
352 }
353 }
354
355 // -----------------------------------------------------------------
356 public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup)
357 {
358 if (QueueLog == null)
359 QueueLog = new QueueLogger();
360
361 Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start);
362 lock(QueueLog)
363 {
364 int cid = QueueLog.LookupID(client);
365 QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString());
366 }
367 }
368// -----------------------------------------------------------------
369// -----------------------------------------------------------------
370// -----------------------------------------------------------------
371// -----------------------------------------------------------------
372//</MIC>
373
301 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 374 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
302 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients 375 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients
303 376
@@ -3547,18 +3620,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3547 3620
3548 #region Primitive Packet/Data Sending Methods 3621 #region Primitive Packet/Data Sending Methods
3549 3622
3623
3550 /// <summary> 3624 /// <summary>
3551 /// Generate one of the object update packets based on PrimUpdateFlags 3625 /// Generate one of the object update packets based on PrimUpdateFlags
3552 /// and broadcast the packet to clients 3626 /// and broadcast the packet to clients
3553 /// </summary> 3627 /// </summary>
3554 public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) 3628 public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags)
3555 { 3629 {
3556 double priority = m_prioritizer.GetUpdatePriority(this, entity); 3630 //double priority = m_prioritizer.GetUpdatePriority(this, entity);
3631 uint priority = m_prioritizer.GetUpdatePriority(this, entity);
3557 3632
3558 lock (m_entityUpdates.SyncRoot) 3633 lock (m_entityUpdates.SyncRoot)
3559 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation), entity.LocalId); 3634 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
3560 } 3635 }
3561 3636
3637 private Int32 m_LastQueueFill = 0;
3638 private uint m_maxUpdates = 0;
3639
3562 private void ProcessEntityUpdates(int maxUpdates) 3640 private void ProcessEntityUpdates(int maxUpdates)
3563 { 3641 {
3564 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); 3642 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
@@ -3566,23 +3644,55 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3566 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3644 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3567 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3645 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3568 3646
3569 if (maxUpdates <= 0) maxUpdates = Int32.MaxValue; 3647 if (maxUpdates <= 0)
3648 {
3649 m_maxUpdates = Int32.MaxValue;
3650 }
3651 else
3652 {
3653 if (m_maxUpdates == 0 || m_LastQueueFill == 0)
3654 {
3655 m_maxUpdates = (uint)maxUpdates;
3656 }
3657 else
3658 {
3659 if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200)
3660 m_maxUpdates += 5;
3661 else
3662 m_maxUpdates = m_maxUpdates >> 1;
3663 }
3664 m_maxUpdates = Util.Clamp<uint>(m_maxUpdates,10,500);
3665 }
3666 m_LastQueueFill = Util.EnvironmentTickCount();
3667
3570 int updatesThisCall = 0; 3668 int updatesThisCall = 0;
3571 3669
3670//<MIC>
3671// DEBUGGING CODE... REMOVE
3672// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates);
3673//</MIC>
3572 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race 3674 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race
3573 // condition where a kill can be processed before an out-of-date update for the same object. 3675 // condition where a kill can be processed before an out-of-date update for the same object.
3574 lock (m_killRecord) 3676 lock (m_killRecord)
3575 { 3677 {
3576 float avgTimeDilation = 1.0f; 3678 float avgTimeDilation = 1.0f;
3577 EntityUpdate update; 3679 EntityUpdate update;
3578 while (updatesThisCall < maxUpdates) 3680 Int32 timeinqueue; // this is just debugging code & can be dropped later
3681
3682 while (updatesThisCall < m_maxUpdates)
3579 { 3683 {
3580 lock (m_entityUpdates.SyncRoot) 3684 lock (m_entityUpdates.SyncRoot)
3581 if (!m_entityUpdates.TryDequeue(out update)) 3685 if (!m_entityUpdates.TryDequeue(out update, out timeinqueue))
3582 break; 3686 break;
3583 avgTimeDilation += update.TimeDilation; 3687 avgTimeDilation += update.TimeDilation;
3584 avgTimeDilation *= 0.5f; 3688 avgTimeDilation *= 0.5f;
3585 3689
3690// <MIC>
3691// DEBUGGING CODE... REMOVE
3692// if (update.Entity is ScenePresence)
3693// LogAvatarUpdateEvent(this.m_agentId,update.Entity.UUID,timeinqueue);
3694// </MIC>
3695
3586 if (update.Entity is SceneObjectPart) 3696 if (update.Entity is SceneObjectPart)
3587 { 3697 {
3588 SceneObjectPart part = (SceneObjectPart)update.Entity; 3698 SceneObjectPart part = (SceneObjectPart)update.Entity;
@@ -3679,36 +3789,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3679 } 3789 }
3680 else 3790 else
3681 { 3791 {
3682 // if (update.Entity is SceneObjectPart && ((SceneObjectPart)update.Entity).IsAttachment) 3792 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3683 // {
3684 // SceneObjectPart sop = (SceneObjectPart)update.Entity;
3685 // string text = sop.Text;
3686 // if (text.IndexOf("\n") >= 0)
3687 // text = text.Remove(text.IndexOf("\n"));
3688 //
3689 // if (m_attachmentsSent.Contains(sop.ParentID))
3690 // {
3691 //// m_log.DebugFormat(
3692 //// "[CLIENT]: Sending full info about attached prim {0} text {1}",
3693 //// sop.LocalId, text);
3694 //
3695 // objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock(sop, this.m_agentId));
3696 //
3697 // m_attachmentsSent.Add(sop.LocalId);
3698 // }
3699 // else
3700 // {
3701 // m_log.DebugFormat(
3702 // "[CLIENT]: Requeueing full update of prim {0} text {1} since we haven't sent its parent {2} yet",
3703 // sop.LocalId, text, sop.ParentID);
3704 //
3705 // m_entityUpdates.Enqueue(double.MaxValue, update, sop.LocalId);
3706 // }
3707 // }
3708 // else
3709 // {
3710 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3711 // }
3712 } 3793 }
3713 } 3794 }
3714 else if (!canUseImproved) 3795 else if (!canUseImproved)
@@ -3802,26 +3883,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3802 3883
3803 public void ReprioritizeUpdates() 3884 public void ReprioritizeUpdates()
3804 { 3885 {
3805 //m_log.Debug("[CLIENT]: Reprioritizing prim updates for " + m_firstName + " " + m_lastName);
3806
3807 lock (m_entityUpdates.SyncRoot) 3886 lock (m_entityUpdates.SyncRoot)
3808 m_entityUpdates.Reprioritize(UpdatePriorityHandler); 3887 m_entityUpdates.Reprioritize(UpdatePriorityHandler);
3809 } 3888 }
3810 3889
3811 private bool UpdatePriorityHandler(ref double priority, uint localID) 3890 private bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity)
3812 { 3891 {
3813 EntityBase entity; 3892 if (entity != null)
3814 if (m_scene.Entities.TryGetValue(localID, out entity))
3815 { 3893 {
3816 priority = m_prioritizer.GetUpdatePriority(this, entity); 3894 priority = m_prioritizer.GetUpdatePriority(this, entity);
3895 return true;
3817 } 3896 }
3818 3897
3819 return priority != double.NaN; 3898 return false;
3820 } 3899 }
3821 3900
3822 public void FlushPrimUpdates() 3901 public void FlushPrimUpdates()
3823 { 3902 {
3824 m_log.Debug("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName); 3903 m_log.WarnFormat("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName);
3825 3904
3826 while (m_entityUpdates.Count > 0) 3905 while (m_entityUpdates.Count > 0)
3827 ProcessEntityUpdates(-1); 3906 ProcessEntityUpdates(-1);
@@ -11704,86 +11783,85 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11704 #region PriorityQueue 11783 #region PriorityQueue
11705 public class PriorityQueue 11784 public class PriorityQueue
11706 { 11785 {
11707 internal delegate bool UpdatePriorityHandler(ref double priority, uint local_id); 11786 internal delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity);
11787
11788 // Heap[0] for self updates
11789 // Heap[1..12] for entity updates
11708 11790
11709 private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[1]; 11791 internal const uint m_numberOfQueues = 12;
11792 private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[m_numberOfQueues];
11710 private Dictionary<uint, LookupItem> m_lookupTable; 11793 private Dictionary<uint, LookupItem> m_lookupTable;
11711 private Comparison<double> m_comparison;
11712 private object m_syncRoot = new object(); 11794 private object m_syncRoot = new object();
11713 11795 private uint m_nextQueue = 0;
11796 private UInt64 m_nextRequest = 0;
11797
11714 internal PriorityQueue() : 11798 internal PriorityQueue() :
11715 this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY, Comparer<double>.Default) { } 11799 this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY) { }
11716 internal PriorityQueue(int capacity) : 11800 internal PriorityQueue(int capacity)
11717 this(capacity, Comparer<double>.Default) { }
11718 internal PriorityQueue(IComparer<double> comparer) :
11719 this(new Comparison<double>(comparer.Compare)) { }
11720 internal PriorityQueue(Comparison<double> comparison) :
11721 this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY, comparison) { }
11722 internal PriorityQueue(int capacity, IComparer<double> comparer) :
11723 this(capacity, new Comparison<double>(comparer.Compare)) { }
11724 internal PriorityQueue(int capacity, Comparison<double> comparison)
11725 { 11801 {
11726 m_lookupTable = new Dictionary<uint, LookupItem>(capacity); 11802 m_lookupTable = new Dictionary<uint, LookupItem>(capacity);
11727 11803
11728 for (int i = 0; i < m_heaps.Length; ++i) 11804 for (int i = 0; i < m_heaps.Length; ++i)
11729 m_heaps[i] = new MinHeap<MinHeapItem>(capacity); 11805 m_heaps[i] = new MinHeap<MinHeapItem>(capacity);
11730 this.m_comparison = comparison;
11731 } 11806 }
11732 11807
11733 public object SyncRoot { get { return this.m_syncRoot; } } 11808 public object SyncRoot { get { return this.m_syncRoot; } }
11809
11734 internal int Count 11810 internal int Count
11735 { 11811 {
11736 get 11812 get
11737 { 11813 {
11738 int count = 0; 11814 int count = 0;
11739 for (int i = 0; i < m_heaps.Length; ++i) 11815 for (int i = 0; i < m_heaps.Length; ++i)
11740 count = m_heaps[i].Count; 11816 count += m_heaps[i].Count;
11741 return count; 11817 return count;
11742 } 11818 }
11743 } 11819 }
11744 11820
11745 public bool Enqueue(double priority, EntityUpdate value, uint local_id) 11821 public bool Enqueue(uint pqueue, EntityUpdate value)
11746 { 11822 {
11747 LookupItem item; 11823 LookupItem lookup;
11748 11824
11749 if (m_lookupTable.TryGetValue(local_id, out item)) 11825 uint localid = value.Entity.LocalId;
11826 UInt64 entry = m_nextRequest++;
11827 if (m_lookupTable.TryGetValue(localid, out lookup))
11750 { 11828 {
11751 // Combine flags 11829 entry = lookup.Heap[lookup.Handle].EntryOrder;
11752 value.Flags |= item.Heap[item.Handle].Value.Flags; 11830 value.Flags |= lookup.Heap[lookup.Handle].Value.Flags;
11753 11831 lookup.Heap.Remove(lookup.Handle);
11754 item.Heap[item.Handle] = new MinHeapItem(priority, value, local_id, this.m_comparison);
11755 return false;
11756 }
11757 else
11758 {
11759 item.Heap = m_heaps[0];
11760 item.Heap.Add(new MinHeapItem(priority, value, local_id, this.m_comparison), ref item.Handle);
11761 m_lookupTable.Add(local_id, item);
11762 return true;
11763 } 11832 }
11764 }
11765 11833
11766 internal EntityUpdate Peek() 11834 pqueue = Util.Clamp<uint>(pqueue, 0, m_numberOfQueues - 1);
11767 { 11835 lookup.Heap = m_heaps[pqueue];
11768 for (int i = 0; i < m_heaps.Length; ++i) 11836 lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle);
11769 if (m_heaps[i].Count > 0) 11837 m_lookupTable[localid] = lookup;
11770 return m_heaps[i].Min().Value; 11838
11771 throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); 11839 return true;
11772 } 11840 }
11773 11841
11774 internal bool TryDequeue(out EntityUpdate value) 11842 internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue)
11775 { 11843 {
11776 for (int i = 0; i < m_heaps.Length; ++i) 11844 for (int i = 0; i < m_numberOfQueues; ++i)
11777 { 11845 {
11778 if (m_heaps[i].Count > 0) 11846 // To get the fair queing, we cycle through each of the
11847 // queues when finding an element to dequeue, this code
11848 // assumes that the distribution of updates in the queues
11849 // is polynomial, probably quadractic (eg distance of PI * R^2)
11850 uint h = (uint)((m_nextQueue + i) % m_numberOfQueues);
11851 if (m_heaps[h].Count > 0)
11779 { 11852 {
11780 MinHeapItem item = m_heaps[i].RemoveMin(); 11853 m_nextQueue = (uint)((h + 1) % m_numberOfQueues);
11781 m_lookupTable.Remove(item.LocalID); 11854
11855 MinHeapItem item = m_heaps[h].RemoveMin();
11856 m_lookupTable.Remove(item.Value.Entity.LocalId);
11857 timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
11782 value = item.Value; 11858 value = item.Value;
11859
11783 return true; 11860 return true;
11784 } 11861 }
11785 } 11862 }
11786 11863
11864 timeinqueue = 0;
11787 value = default(EntityUpdate); 11865 value = default(EntityUpdate);
11788 return false; 11866 return false;
11789 } 11867 }
@@ -11791,68 +11869,107 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11791 internal void Reprioritize(UpdatePriorityHandler handler) 11869 internal void Reprioritize(UpdatePriorityHandler handler)
11792 { 11870 {
11793 MinHeapItem item; 11871 MinHeapItem item;
11794 double priority;
11795
11796 foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values)) 11872 foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values))
11797 { 11873 {
11798 if (lookup.Heap.TryGetValue(lookup.Handle, out item)) 11874 if (lookup.Heap.TryGetValue(lookup.Handle, out item))
11799 { 11875 {
11800 priority = item.Priority; 11876 uint pqueue = item.PriorityQueue;
11801 if (handler(ref priority, item.LocalID)) 11877 uint localid = item.Value.Entity.LocalId;
11878
11879 if (handler(ref pqueue, item.Value.Entity))
11802 { 11880 {
11803 if (lookup.Heap.ContainsHandle(lookup.Handle)) 11881 // unless the priority queue has changed, there is no need to modify
11804 lookup.Heap[lookup.Handle] = 11882 // the entry
11805 new MinHeapItem(priority, item.Value, item.LocalID, this.m_comparison); 11883 pqueue = Util.Clamp<uint>(pqueue, 0, m_numberOfQueues - 1);
11884 if (pqueue != item.PriorityQueue)
11885 {
11886 lookup.Heap.Remove(lookup.Handle);
11887
11888 LookupItem litem = lookup;
11889 litem.Heap = m_heaps[pqueue];
11890 litem.Heap.Add(new MinHeapItem(pqueue, item), ref litem.Handle);
11891 m_lookupTable[localid] = litem;
11892 }
11806 } 11893 }
11807 else 11894 else
11808 { 11895 {
11809 m_log.Warn("[LLCLIENTVIEW]: UpdatePriorityHandler returned false, dropping update"); 11896 m_log.WarnFormat("[LLCLIENTVIEW]: UpdatePriorityHandler returned false for {0}",item.Value.Entity.UUID);
11810 lookup.Heap.Remove(lookup.Handle); 11897 lookup.Heap.Remove(lookup.Handle);
11811 this.m_lookupTable.Remove(item.LocalID); 11898 this.m_lookupTable.Remove(localid);
11812 } 11899 }
11813 } 11900 }
11814 } 11901 }
11815 } 11902 }
11816 11903
11904 public override string ToString()
11905 {
11906 string s = "";
11907 for (int i = 0; i < m_numberOfQueues; i++)
11908 {
11909 if (s != "") s += ",";
11910 s += m_heaps[i].Count.ToString();
11911 }
11912 return s;
11913 }
11914
11817 #region MinHeapItem 11915 #region MinHeapItem
11818 private struct MinHeapItem : IComparable<MinHeapItem> 11916 private struct MinHeapItem : IComparable<MinHeapItem>
11819 { 11917 {
11820 private double priority;
11821 private EntityUpdate value; 11918 private EntityUpdate value;
11822 private uint local_id; 11919 internal EntityUpdate Value {
11823 private Comparison<double> comparison; 11920 get {
11921 return this.value;
11922 }
11923 }
11924
11925 private uint pqueue;
11926 internal uint PriorityQueue {
11927 get {
11928 return this.pqueue;
11929 }
11930 }
11824 11931
11825 internal MinHeapItem(double priority, EntityUpdate value, uint local_id) : 11932 private Int32 entrytime;
11826 this(priority, value, local_id, Comparer<double>.Default) { } 11933 internal Int32 EntryTime {
11827 internal MinHeapItem(double priority, EntityUpdate value, uint local_id, IComparer<double> comparer) : 11934 get {
11828 this(priority, value, local_id, new Comparison<double>(comparer.Compare)) { } 11935 return this.entrytime;
11829 internal MinHeapItem(double priority, EntityUpdate value, uint local_id, Comparison<double> comparison) 11936 }
11937 }
11938
11939 private UInt64 entryorder;
11940 internal UInt64 EntryOrder
11830 { 11941 {
11831 this.priority = priority; 11942 get {
11943 return this.entryorder;
11944 }
11945 }
11946
11947 internal MinHeapItem(uint pqueue, MinHeapItem other)
11948 {
11949 this.entrytime = other.entrytime;
11950 this.entryorder = other.entryorder;
11951 this.value = other.value;
11952 this.pqueue = pqueue;
11953 }
11954
11955 internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value)
11956 {
11957 this.entrytime = Util.EnvironmentTickCount();
11958 this.entryorder = entryorder;
11832 this.value = value; 11959 this.value = value;
11833 this.local_id = local_id; 11960 this.pqueue = pqueue;
11834 this.comparison = comparison;
11835 } 11961 }
11836 11962
11837 internal double Priority { get { return this.priority; } }
11838 internal EntityUpdate Value { get { return this.value; } }
11839 internal uint LocalID { get { return this.local_id; } }
11840
11841 public override string ToString() 11963 public override string ToString()
11842 { 11964 {
11843 StringBuilder sb = new StringBuilder(); 11965 return String.Format("[{0},{1},{2}]",pqueue,entryorder,value.Entity.LocalId);
11844 sb.Append("[");
11845 sb.Append(this.priority.ToString());
11846 sb.Append(",");
11847 if (this.value != null)
11848 sb.Append(this.value.ToString());
11849 sb.Append("]");
11850 return sb.ToString();
11851 } 11966 }
11852 11967
11853 public int CompareTo(MinHeapItem other) 11968 public int CompareTo(MinHeapItem other)
11854 { 11969 {
11855 return this.comparison(this.priority, other.priority); 11970 // I'm assuming that the root part of an SOG is added to the update queue
11971 // before the component parts
11972 return Comparer<UInt64>.Default.Compare(this.EntryOrder, other.EntryOrder);
11856 } 11973 }
11857 } 11974 }
11858 #endregion 11975 #endregion