From b341bc090833695184276c48dd36cd946791a00c Mon Sep 17 00:00:00 2001 From: BlueWall Date: Thu, 13 Jan 2011 11:39:50 -0500 Subject: [PATCH 001/103] Make FireAndForgetWrapper a singleton class Made FireAndForgetWrapper a singleton class to allow us to drop dependancy on the BclExtras35 library. BclExtras is broken in Mono 2.8.2 and we used the library in only one function. --- OpenSim/Framework/Util.cs | 27 ++++++++++++++++++++++++--- bin/BclExtras35.dll | Bin 153600 -> 0 bytes prebuild.xml | 2 -- 3 files changed, 24 insertions(+), 5 deletions(-) delete mode 100644 bin/BclExtras35.dll diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 8d1671a885..d1d8736062 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -46,7 +46,7 @@ using System.Threading; using log4net; using Nini.Config; using Nwc.XmlRpc; -using BclExtras; +// using BclExtras; using OpenMetaverse; using OpenMetaverse.StructuredData; using Amib.Threading; @@ -1375,8 +1375,29 @@ namespace OpenSim.Framework /// /// Created to work around a limitation in Mono with nested delegates /// - private class FireAndForgetWrapper + private sealed class FireAndForgetWrapper { + private static volatile FireAndForgetWrapper instance; + private static object syncRoot = new Object(); + + public static FireAndForgetWrapper Instance { + get { + + if (instance == null) + { + lock (syncRoot) + { + if (instance == null) + { + instance = new FireAndForgetWrapper(); + } + } + } + + return instance; + } + } + public void FireAndForget(System.Threading.WaitCallback callback) { callback.BeginInvoke(null, EndFireAndForget, callback); @@ -1445,7 +1466,7 @@ namespace OpenSim.Framework ThreadPool.QueueUserWorkItem(callback, obj); break; case FireAndForgetMethod.BeginInvoke: - FireAndForgetWrapper wrapper = Singleton.GetInstance(); + FireAndForgetWrapper wrapper = FireAndForgetWrapper.Instance; wrapper.FireAndForget(callback, obj); break; case FireAndForgetMethod.SmartThreadPool: diff --git a/bin/BclExtras35.dll b/bin/BclExtras35.dll deleted file mode 100644 index 7a7480a3a174bb26fadd394ba7b8566d61cda0ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 153600 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 - @@ -1597,7 +1596,6 @@ - From b492f1ce99d27bcf29eb5805564c8778aa4df748 Mon Sep 17 00:00:00 2001 From: BlueWall Date: Sat, 12 Feb 2011 20:50:24 -0500 Subject: [PATCH 002/103] Fix unsitting of avatar on linked sets --- OpenSim/Region/Framework/Scenes/ScenePresence.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index cd70de8440..cb2543f03d 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -472,7 +472,7 @@ namespace OpenSim.Region.Framework.Scenes SceneObjectPart part = m_scene.GetSceneObjectPart(m_parentID); if (part != null) { - return m_parentPosition + (m_pos * part.GetWorldRotation()); + return m_parentPosition + (m_pos * part.RotationOffset); } else { From 0d495ced4c4077ab456119650693c979eeb9d1ea Mon Sep 17 00:00:00 2001 From: BlueWall Date: Sun, 13 Feb 2011 00:29:06 -0500 Subject: [PATCH 003/103] Revert "Fix unsitting of avatar on linked sets" This reverts commit b492f1ce99d27bcf29eb5805564c8778aa4df748. --- OpenSim/Region/Framework/Scenes/ScenePresence.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index cb2543f03d..cd70de8440 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -472,7 +472,7 @@ namespace OpenSim.Region.Framework.Scenes SceneObjectPart part = m_scene.GetSceneObjectPart(m_parentID); if (part != null) { - return m_parentPosition + (m_pos * part.RotationOffset); + return m_parentPosition + (m_pos * part.GetWorldRotation()); } else { From 6601771f812a360e6bc949da242878980ac0dd72 Mon Sep 17 00:00:00 2001 From: BlueWall Date: Sun, 13 Feb 2011 00:30:43 -0500 Subject: [PATCH 004/103] Set filter to send proper rotations for root part This allows the root prim, alone or in a set, to send it's rotation. This fixes unsitting the avatar on sit-offsest type teleports where the sit target is in the root prim of a linkset. --- OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 6a92378ea7..4d5eedfc95 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2099,7 +2099,7 @@ namespace OpenSim.Region.Framework.Scenes { Quaternion newRot; - if (this.LinkNum == 0) + if (this.LinkNum == 0 || this.LinkNum == 1) { newRot = RotationOffset; } From 61371c7c16eea3b856a852b94c98b18b99ccf8fb Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 28 Mar 2011 10:00:53 -0700 Subject: [PATCH 005/103] Implements adaptive queue management and fair queueing for improved networking performance. Reprioritization algorithms need to be ported still. One is in place. --- .../ClientStack/LindenUDP/LLClientView.cs | 359 ++++++++++++------ .../Region/Framework/Scenes/Prioritizer.cs | 71 +++- 2 files changed, 307 insertions(+), 123 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 2c6795fc3e..db149a1d26 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -49,6 +49,8 @@ using Timer = System.Timers.Timer; using AssetLandmark = OpenSim.Framework.AssetLandmark; using Nini.Config; +using System.IO; + namespace OpenSim.Region.ClientStack.LindenUDP { public delegate bool PacketMethod(IClientAPI simClient, Packet packet); @@ -298,6 +300,77 @@ namespace OpenSim.Region.ClientStack.LindenUDP ///

Used to adjust Sun Orbit values so Linden based viewers properly position sun private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; + // First log file or time has expired, start writing to a new log file +// +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- +// THIS IS DEBUGGING CODE & SHOULD BE REMOVED +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- + public class QueueLogger + { + public Int32 start = 0; + public StreamWriter Log = null; + private Dictionary m_idMap = new Dictionary(); + + public QueueLogger() + { + DateTime now = DateTime.Now; + String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss")); + Log = new StreamWriter(fname); + + start = Util.EnvironmentTickCount(); + } + + public int LookupID(UUID uuid) + { + int localid; + if (! m_idMap.TryGetValue(uuid,out localid)) + { + localid = m_idMap.Count + 1; + m_idMap[uuid] = localid; + } + + return localid; + } + } + + public static QueueLogger QueueLog = null; + + // ----------------------------------------------------------------- + public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue) + { + if (QueueLog == null) + QueueLog = new QueueLogger(); + + Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); + lock(QueueLog) + { + int cid = QueueLog.LookupID(client); + int aid = QueueLog.LookupID(avatar); + QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue); + } + } + + // ----------------------------------------------------------------- + public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup) + { + if (QueueLog == null) + QueueLog = new QueueLogger(); + + Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); + lock(QueueLog) + { + int cid = QueueLog.LookupID(client); + QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString()); + } + } +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- +// + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected static Dictionary PacketHandlers = new Dictionary(); //Global/static handlers for all clients @@ -3547,18 +3620,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Primitive Packet/Data Sending Methods + /// /// Generate one of the object update packets based on PrimUpdateFlags /// and broadcast the packet to clients /// public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) { - double priority = m_prioritizer.GetUpdatePriority(this, entity); + //double priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = m_prioritizer.GetUpdatePriority(this, entity); lock (m_entityUpdates.SyncRoot) - m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation), entity.LocalId); + m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); } + private Int32 m_LastQueueFill = 0; + private uint m_maxUpdates = 0; + private void ProcessEntityUpdates(int maxUpdates) { OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); @@ -3566,23 +3644,55 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.Lazy> terseUpdateBlocks = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy>(); - if (maxUpdates <= 0) maxUpdates = Int32.MaxValue; + if (maxUpdates <= 0) + { + m_maxUpdates = Int32.MaxValue; + } + else + { + if (m_maxUpdates == 0 || m_LastQueueFill == 0) + { + m_maxUpdates = (uint)maxUpdates; + } + else + { + if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) + m_maxUpdates += 5; + else + m_maxUpdates = m_maxUpdates >> 1; + } + m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); + } + m_LastQueueFill = Util.EnvironmentTickCount(); + int updatesThisCall = 0; +// +// DEBUGGING CODE... REMOVE +// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates); +// // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race // condition where a kill can be processed before an out-of-date update for the same object. lock (m_killRecord) { float avgTimeDilation = 1.0f; EntityUpdate update; - while (updatesThisCall < maxUpdates) + Int32 timeinqueue; // this is just debugging code & can be dropped later + + while (updatesThisCall < m_maxUpdates) { lock (m_entityUpdates.SyncRoot) - if (!m_entityUpdates.TryDequeue(out update)) + if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) break; avgTimeDilation += update.TimeDilation; avgTimeDilation *= 0.5f; +// +// DEBUGGING CODE... REMOVE +// if (update.Entity is ScenePresence) +// LogAvatarUpdateEvent(this.m_agentId,update.Entity.UUID,timeinqueue); +// + if (update.Entity is SceneObjectPart) { SceneObjectPart part = (SceneObjectPart)update.Entity; @@ -3679,36 +3789,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - // if (update.Entity is SceneObjectPart && ((SceneObjectPart)update.Entity).IsAttachment) - // { - // SceneObjectPart sop = (SceneObjectPart)update.Entity; - // string text = sop.Text; - // if (text.IndexOf("\n") >= 0) - // text = text.Remove(text.IndexOf("\n")); - // - // if (m_attachmentsSent.Contains(sop.ParentID)) - // { - //// m_log.DebugFormat( - //// "[CLIENT]: Sending full info about attached prim {0} text {1}", - //// sop.LocalId, text); - // - // objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock(sop, this.m_agentId)); - // - // m_attachmentsSent.Add(sop.LocalId); - // } - // else - // { - // m_log.DebugFormat( - // "[CLIENT]: Requeueing full update of prim {0} text {1} since we haven't sent its parent {2} yet", - // sop.LocalId, text, sop.ParentID); - // - // m_entityUpdates.Enqueue(double.MaxValue, update, sop.LocalId); - // } - // } - // else - // { - objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); - // } + objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); } } else if (!canUseImproved) @@ -3802,26 +3883,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void ReprioritizeUpdates() { - //m_log.Debug("[CLIENT]: Reprioritizing prim updates for " + m_firstName + " " + m_lastName); - lock (m_entityUpdates.SyncRoot) m_entityUpdates.Reprioritize(UpdatePriorityHandler); } - private bool UpdatePriorityHandler(ref double priority, uint localID) + private bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity) { - EntityBase entity; - if (m_scene.Entities.TryGetValue(localID, out entity)) + if (entity != null) { priority = m_prioritizer.GetUpdatePriority(this, entity); + return true; } - return priority != double.NaN; + return false; } public void FlushPrimUpdates() { - m_log.Debug("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName); + m_log.WarnFormat("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName); while (m_entityUpdates.Count > 0) ProcessEntityUpdates(-1); @@ -11704,86 +11783,85 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region PriorityQueue public class PriorityQueue { - internal delegate bool UpdatePriorityHandler(ref double priority, uint local_id); + internal delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity); - private MinHeap[] m_heaps = new MinHeap[1]; + // Heap[0] for self updates + // Heap[1..12] for entity updates + + internal const uint m_numberOfQueues = 12; + private MinHeap[] m_heaps = new MinHeap[m_numberOfQueues]; private Dictionary m_lookupTable; - private Comparison m_comparison; private object m_syncRoot = new object(); - + private uint m_nextQueue = 0; + private UInt64 m_nextRequest = 0; + internal PriorityQueue() : - this(MinHeap.DEFAULT_CAPACITY, Comparer.Default) { } - internal PriorityQueue(int capacity) : - this(capacity, Comparer.Default) { } - internal PriorityQueue(IComparer comparer) : - this(new Comparison(comparer.Compare)) { } - internal PriorityQueue(Comparison comparison) : - this(MinHeap.DEFAULT_CAPACITY, comparison) { } - internal PriorityQueue(int capacity, IComparer comparer) : - this(capacity, new Comparison(comparer.Compare)) { } - internal PriorityQueue(int capacity, Comparison comparison) + this(MinHeap.DEFAULT_CAPACITY) { } + internal PriorityQueue(int capacity) { m_lookupTable = new Dictionary(capacity); for (int i = 0; i < m_heaps.Length; ++i) m_heaps[i] = new MinHeap(capacity); - this.m_comparison = comparison; } public object SyncRoot { get { return this.m_syncRoot; } } + internal int Count { get { int count = 0; for (int i = 0; i < m_heaps.Length; ++i) - count = m_heaps[i].Count; + count += m_heaps[i].Count; return count; } } - public bool Enqueue(double priority, EntityUpdate value, uint local_id) + public bool Enqueue(uint pqueue, EntityUpdate value) { - LookupItem item; + LookupItem lookup; - if (m_lookupTable.TryGetValue(local_id, out item)) + uint localid = value.Entity.LocalId; + UInt64 entry = m_nextRequest++; + if (m_lookupTable.TryGetValue(localid, out lookup)) { - // Combine flags - value.Flags |= item.Heap[item.Handle].Value.Flags; + entry = lookup.Heap[lookup.Handle].EntryOrder; + value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; + lookup.Heap.Remove(lookup.Handle); + } - item.Heap[item.Handle] = new MinHeapItem(priority, value, local_id, this.m_comparison); - return false; - } - else - { - item.Heap = m_heaps[0]; - item.Heap.Add(new MinHeapItem(priority, value, local_id, this.m_comparison), ref item.Handle); - m_lookupTable.Add(local_id, item); - return true; - } + pqueue = Util.Clamp(pqueue, 0, m_numberOfQueues - 1); + lookup.Heap = m_heaps[pqueue]; + lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle); + m_lookupTable[localid] = lookup; + + return true; } - internal EntityUpdate Peek() + internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) { - for (int i = 0; i < m_heaps.Length; ++i) - if (m_heaps[i].Count > 0) - return m_heaps[i].Min().Value; - throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); - } - - internal bool TryDequeue(out EntityUpdate value) - { - for (int i = 0; i < m_heaps.Length; ++i) + for (int i = 0; i < m_numberOfQueues; ++i) { - if (m_heaps[i].Count > 0) + // To get the fair queing, we cycle through each of the + // queues when finding an element to dequeue, this code + // assumes that the distribution of updates in the queues + // is polynomial, probably quadractic (eg distance of PI * R^2) + uint h = (uint)((m_nextQueue + i) % m_numberOfQueues); + if (m_heaps[h].Count > 0) { - MinHeapItem item = m_heaps[i].RemoveMin(); - m_lookupTable.Remove(item.LocalID); + m_nextQueue = (uint)((h + 1) % m_numberOfQueues); + + MinHeapItem item = m_heaps[h].RemoveMin(); + m_lookupTable.Remove(item.Value.Entity.LocalId); + timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime); value = item.Value; + return true; } } + timeinqueue = 0; value = default(EntityUpdate); return false; } @@ -11791,68 +11869,107 @@ namespace OpenSim.Region.ClientStack.LindenUDP internal void Reprioritize(UpdatePriorityHandler handler) { MinHeapItem item; - double priority; - foreach (LookupItem lookup in new List(this.m_lookupTable.Values)) { if (lookup.Heap.TryGetValue(lookup.Handle, out item)) { - priority = item.Priority; - if (handler(ref priority, item.LocalID)) + uint pqueue = item.PriorityQueue; + uint localid = item.Value.Entity.LocalId; + + if (handler(ref pqueue, item.Value.Entity)) { - if (lookup.Heap.ContainsHandle(lookup.Handle)) - lookup.Heap[lookup.Handle] = - new MinHeapItem(priority, item.Value, item.LocalID, this.m_comparison); + // unless the priority queue has changed, there is no need to modify + // the entry + pqueue = Util.Clamp(pqueue, 0, m_numberOfQueues - 1); + if (pqueue != item.PriorityQueue) + { + lookup.Heap.Remove(lookup.Handle); + + LookupItem litem = lookup; + litem.Heap = m_heaps[pqueue]; + litem.Heap.Add(new MinHeapItem(pqueue, item), ref litem.Handle); + m_lookupTable[localid] = litem; + } } else { - m_log.Warn("[LLCLIENTVIEW]: UpdatePriorityHandler returned false, dropping update"); + m_log.WarnFormat("[LLCLIENTVIEW]: UpdatePriorityHandler returned false for {0}",item.Value.Entity.UUID); lookup.Heap.Remove(lookup.Handle); - this.m_lookupTable.Remove(item.LocalID); + this.m_lookupTable.Remove(localid); } } } } + public override string ToString() + { + string s = ""; + for (int i = 0; i < m_numberOfQueues; i++) + { + if (s != "") s += ","; + s += m_heaps[i].Count.ToString(); + } + return s; + } + #region MinHeapItem private struct MinHeapItem : IComparable { - private double priority; private EntityUpdate value; - private uint local_id; - private Comparison comparison; - - internal MinHeapItem(double priority, EntityUpdate value, uint local_id) : - this(priority, value, local_id, Comparer.Default) { } - internal MinHeapItem(double priority, EntityUpdate value, uint local_id, IComparer comparer) : - this(priority, value, local_id, new Comparison(comparer.Compare)) { } - internal MinHeapItem(double priority, EntityUpdate value, uint local_id, Comparison comparison) - { - this.priority = priority; - this.value = value; - this.local_id = local_id; - this.comparison = comparison; + internal EntityUpdate Value { + get { + return this.value; + } } - internal double Priority { get { return this.priority; } } - internal EntityUpdate Value { get { return this.value; } } - internal uint LocalID { get { return this.local_id; } } + private uint pqueue; + internal uint PriorityQueue { + get { + return this.pqueue; + } + } + + private Int32 entrytime; + internal Int32 EntryTime { + get { + return this.entrytime; + } + } + + private UInt64 entryorder; + internal UInt64 EntryOrder + { + get { + return this.entryorder; + } + } + + internal MinHeapItem(uint pqueue, MinHeapItem other) + { + this.entrytime = other.entrytime; + this.entryorder = other.entryorder; + this.value = other.value; + this.pqueue = pqueue; + } + + internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) + { + this.entrytime = Util.EnvironmentTickCount(); + this.entryorder = entryorder; + this.value = value; + this.pqueue = pqueue; + } public override string ToString() { - StringBuilder sb = new StringBuilder(); - sb.Append("["); - sb.Append(this.priority.ToString()); - sb.Append(","); - if (this.value != null) - sb.Append(this.value.ToString()); - sb.Append("]"); - return sb.ToString(); + return String.Format("[{0},{1},{2}]",pqueue,entryorder,value.Entity.LocalId); } public int CompareTo(MinHeapItem other) { - return this.comparison(this.priority, other.priority); + // I'm assuming that the root part of an SOG is added to the update queue + // before the component parts + return Comparer.Default.Compare(this.EntryOrder, other.EntryOrder); } } #endregion diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs index f9599f5a96..2764b05f1a 100644 --- a/OpenSim/Region/Framework/Scenes/Prioritizer.cs +++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs @@ -58,7 +58,7 @@ namespace OpenSim.Region.Framework.Scenes public class Prioritizer { -// private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); /// /// This is added to the priority of all child prims, to make sure that the root prim update is sent to the @@ -76,7 +76,74 @@ namespace OpenSim.Region.Framework.Scenes m_scene = scene; } - public double GetUpdatePriority(IClientAPI client, ISceneEntity entity) +// + public uint GetUpdatePriority(IClientAPI client, ISceneEntity entity) + { + if (entity == null) + { + m_log.WarnFormat("[PRIORITIZER] attempt to prioritize null entity"); + throw new InvalidOperationException("Prioritization entity not defined"); + } + + // If this is an update for our own avatar give it the highest priority + if (client.AgentId == entity.UUID) + return 0; + + // Get this agent's position + ScenePresence presence = m_scene.GetScenePresence(client.AgentId); + if (presence == null) + { + m_log.WarnFormat("[PRIORITIZER] attempt to prioritize agent no longer in the scene"); + throw new InvalidOperationException("Prioritization agent not defined"); + } + + // Use group position for child prims + Vector3 entityPos = entity.AbsolutePosition; + if (entity is SceneObjectPart) + { + SceneObjectGroup group = (entity as SceneObjectPart).ParentGroup; + if (group != null) + entityPos = group.AbsolutePosition; + } + + // Use the camera position for local agents and avatar position for remote agents + Vector3 presencePos = (presence.IsChildAgent) ? + presence.AbsolutePosition : + presence.CameraPosition; + + // Compute the distance... + double distance = Vector3.Distance(presencePos, entityPos); + + // And convert the distance to a priority queue, this computation gives queues + // at 10, 20, 40, 80, 160, 320, 640, and 1280m + uint pqueue = 1; + for (int i = 0; i < 8; i++) + { + if (distance < 10 * Math.Pow(2.0,i)) + break; + pqueue++; + } + + // If this is a root agent, then determine front & back + // Bump up the priority queue for any objects behind the avatar + if (! presence.IsChildAgent) + { + // Root agent, decrease priority for objects behind us + Vector3 camPosition = presence.CameraPosition; + Vector3 camAtAxis = presence.CameraAtAxis; + + // Plane equation + float d = -Vector3.Dot(camPosition, camAtAxis); + float p = Vector3.Dot(camAtAxis, entityPos) + d; + if (p < 0.0f) + pqueue++; + } + + return pqueue; + } +// + + public double bGetUpdatePriority(IClientAPI client, ISceneEntity entity) { double priority = 0; From 6885b7220c6f782034d7c0220762244adf00e3d3 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 28 Mar 2011 10:00:53 -0700 Subject: [PATCH 006/103] Implements adaptive queue management and fair queueing for improved networking performance. Reprioritization algorithms need to be ported still. One is in place. --- .../ClientStack/LindenUDP/LLClientView.cs | 359 ++++++++++++------ .../Region/Framework/Scenes/Prioritizer.cs | 71 +++- 2 files changed, 307 insertions(+), 123 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 2faffae58e..e9e1fa3611 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -49,6 +49,8 @@ using Timer = System.Timers.Timer; using AssetLandmark = OpenSim.Framework.AssetLandmark; using Nini.Config; +using System.IO; + namespace OpenSim.Region.ClientStack.LindenUDP { public delegate bool PacketMethod(IClientAPI simClient, Packet packet); @@ -298,6 +300,77 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Used to adjust Sun Orbit values so Linden based viewers properly position sun private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; + // First log file or time has expired, start writing to a new log file +// +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- +// THIS IS DEBUGGING CODE & SHOULD BE REMOVED +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- + public class QueueLogger + { + public Int32 start = 0; + public StreamWriter Log = null; + private Dictionary m_idMap = new Dictionary(); + + public QueueLogger() + { + DateTime now = DateTime.Now; + String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss")); + Log = new StreamWriter(fname); + + start = Util.EnvironmentTickCount(); + } + + public int LookupID(UUID uuid) + { + int localid; + if (! m_idMap.TryGetValue(uuid,out localid)) + { + localid = m_idMap.Count + 1; + m_idMap[uuid] = localid; + } + + return localid; + } + } + + public static QueueLogger QueueLog = null; + + // ----------------------------------------------------------------- + public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue) + { + if (QueueLog == null) + QueueLog = new QueueLogger(); + + Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); + lock(QueueLog) + { + int cid = QueueLog.LookupID(client); + int aid = QueueLog.LookupID(avatar); + QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue); + } + } + + // ----------------------------------------------------------------- + public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup) + { + if (QueueLog == null) + QueueLog = new QueueLogger(); + + Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); + lock(QueueLog) + { + int cid = QueueLog.LookupID(client); + QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString()); + } + } +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- +// + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected static Dictionary PacketHandlers = new Dictionary(); //Global/static handlers for all clients @@ -3547,18 +3620,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Primitive Packet/Data Sending Methods + /// /// Generate one of the object update packets based on PrimUpdateFlags /// and broadcast the packet to clients /// public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) { - double priority = m_prioritizer.GetUpdatePriority(this, entity); + //double priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = m_prioritizer.GetUpdatePriority(this, entity); lock (m_entityUpdates.SyncRoot) - m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation), entity.LocalId); + m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); } + private Int32 m_LastQueueFill = 0; + private uint m_maxUpdates = 0; + private void ProcessEntityUpdates(int maxUpdates) { OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); @@ -3566,23 +3644,55 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.Lazy> terseUpdateBlocks = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy>(); - if (maxUpdates <= 0) maxUpdates = Int32.MaxValue; + if (maxUpdates <= 0) + { + m_maxUpdates = Int32.MaxValue; + } + else + { + if (m_maxUpdates == 0 || m_LastQueueFill == 0) + { + m_maxUpdates = (uint)maxUpdates; + } + else + { + if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) + m_maxUpdates += 5; + else + m_maxUpdates = m_maxUpdates >> 1; + } + m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); + } + m_LastQueueFill = Util.EnvironmentTickCount(); + int updatesThisCall = 0; +// +// DEBUGGING CODE... REMOVE +// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates); +// // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race // condition where a kill can be processed before an out-of-date update for the same object. lock (m_killRecord) { float avgTimeDilation = 1.0f; EntityUpdate update; - while (updatesThisCall < maxUpdates) + Int32 timeinqueue; // this is just debugging code & can be dropped later + + while (updatesThisCall < m_maxUpdates) { lock (m_entityUpdates.SyncRoot) - if (!m_entityUpdates.TryDequeue(out update)) + if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) break; avgTimeDilation += update.TimeDilation; avgTimeDilation *= 0.5f; +// +// DEBUGGING CODE... REMOVE +// if (update.Entity is ScenePresence) +// LogAvatarUpdateEvent(this.m_agentId,update.Entity.UUID,timeinqueue); +// + if (update.Entity is SceneObjectPart) { SceneObjectPart part = (SceneObjectPart)update.Entity; @@ -3679,36 +3789,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - // if (update.Entity is SceneObjectPart && ((SceneObjectPart)update.Entity).IsAttachment) - // { - // SceneObjectPart sop = (SceneObjectPart)update.Entity; - // string text = sop.Text; - // if (text.IndexOf("\n") >= 0) - // text = text.Remove(text.IndexOf("\n")); - // - // if (m_attachmentsSent.Contains(sop.ParentID)) - // { - //// m_log.DebugFormat( - //// "[CLIENT]: Sending full info about attached prim {0} text {1}", - //// sop.LocalId, text); - // - // objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock(sop, this.m_agentId)); - // - // m_attachmentsSent.Add(sop.LocalId); - // } - // else - // { - // m_log.DebugFormat( - // "[CLIENT]: Requeueing full update of prim {0} text {1} since we haven't sent its parent {2} yet", - // sop.LocalId, text, sop.ParentID); - // - // m_entityUpdates.Enqueue(double.MaxValue, update, sop.LocalId); - // } - // } - // else - // { - objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); - // } + objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); } } else if (!canUseImproved) @@ -3802,26 +3883,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void ReprioritizeUpdates() { - //m_log.Debug("[CLIENT]: Reprioritizing prim updates for " + m_firstName + " " + m_lastName); - lock (m_entityUpdates.SyncRoot) m_entityUpdates.Reprioritize(UpdatePriorityHandler); } - private bool UpdatePriorityHandler(ref double priority, uint localID) + private bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity) { - EntityBase entity; - if (m_scene.Entities.TryGetValue(localID, out entity)) + if (entity != null) { priority = m_prioritizer.GetUpdatePriority(this, entity); + return true; } - return priority != double.NaN; + return false; } public void FlushPrimUpdates() { - m_log.Debug("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName); + m_log.WarnFormat("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName); while (m_entityUpdates.Count > 0) ProcessEntityUpdates(-1); @@ -11713,86 +11792,85 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region PriorityQueue public class PriorityQueue { - internal delegate bool UpdatePriorityHandler(ref double priority, uint local_id); + internal delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity); - private MinHeap[] m_heaps = new MinHeap[1]; + // Heap[0] for self updates + // Heap[1..12] for entity updates + + internal const uint m_numberOfQueues = 12; + private MinHeap[] m_heaps = new MinHeap[m_numberOfQueues]; private Dictionary m_lookupTable; - private Comparison m_comparison; private object m_syncRoot = new object(); - + private uint m_nextQueue = 0; + private UInt64 m_nextRequest = 0; + internal PriorityQueue() : - this(MinHeap.DEFAULT_CAPACITY, Comparer.Default) { } - internal PriorityQueue(int capacity) : - this(capacity, Comparer.Default) { } - internal PriorityQueue(IComparer comparer) : - this(new Comparison(comparer.Compare)) { } - internal PriorityQueue(Comparison comparison) : - this(MinHeap.DEFAULT_CAPACITY, comparison) { } - internal PriorityQueue(int capacity, IComparer comparer) : - this(capacity, new Comparison(comparer.Compare)) { } - internal PriorityQueue(int capacity, Comparison comparison) + this(MinHeap.DEFAULT_CAPACITY) { } + internal PriorityQueue(int capacity) { m_lookupTable = new Dictionary(capacity); for (int i = 0; i < m_heaps.Length; ++i) m_heaps[i] = new MinHeap(capacity); - this.m_comparison = comparison; } public object SyncRoot { get { return this.m_syncRoot; } } + internal int Count { get { int count = 0; for (int i = 0; i < m_heaps.Length; ++i) - count = m_heaps[i].Count; + count += m_heaps[i].Count; return count; } } - public bool Enqueue(double priority, EntityUpdate value, uint local_id) + public bool Enqueue(uint pqueue, EntityUpdate value) { - LookupItem item; + LookupItem lookup; - if (m_lookupTable.TryGetValue(local_id, out item)) + uint localid = value.Entity.LocalId; + UInt64 entry = m_nextRequest++; + if (m_lookupTable.TryGetValue(localid, out lookup)) { - // Combine flags - value.Flags |= item.Heap[item.Handle].Value.Flags; + entry = lookup.Heap[lookup.Handle].EntryOrder; + value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; + lookup.Heap.Remove(lookup.Handle); + } - item.Heap[item.Handle] = new MinHeapItem(priority, value, local_id, this.m_comparison); - return false; - } - else - { - item.Heap = m_heaps[0]; - item.Heap.Add(new MinHeapItem(priority, value, local_id, this.m_comparison), ref item.Handle); - m_lookupTable.Add(local_id, item); - return true; - } + pqueue = Util.Clamp(pqueue, 0, m_numberOfQueues - 1); + lookup.Heap = m_heaps[pqueue]; + lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle); + m_lookupTable[localid] = lookup; + + return true; } - internal EntityUpdate Peek() + internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) { - for (int i = 0; i < m_heaps.Length; ++i) - if (m_heaps[i].Count > 0) - return m_heaps[i].Min().Value; - throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); - } - - internal bool TryDequeue(out EntityUpdate value) - { - for (int i = 0; i < m_heaps.Length; ++i) + for (int i = 0; i < m_numberOfQueues; ++i) { - if (m_heaps[i].Count > 0) + // To get the fair queing, we cycle through each of the + // queues when finding an element to dequeue, this code + // assumes that the distribution of updates in the queues + // is polynomial, probably quadractic (eg distance of PI * R^2) + uint h = (uint)((m_nextQueue + i) % m_numberOfQueues); + if (m_heaps[h].Count > 0) { - MinHeapItem item = m_heaps[i].RemoveMin(); - m_lookupTable.Remove(item.LocalID); + m_nextQueue = (uint)((h + 1) % m_numberOfQueues); + + MinHeapItem item = m_heaps[h].RemoveMin(); + m_lookupTable.Remove(item.Value.Entity.LocalId); + timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime); value = item.Value; + return true; } } + timeinqueue = 0; value = default(EntityUpdate); return false; } @@ -11800,68 +11878,107 @@ namespace OpenSim.Region.ClientStack.LindenUDP internal void Reprioritize(UpdatePriorityHandler handler) { MinHeapItem item; - double priority; - foreach (LookupItem lookup in new List(this.m_lookupTable.Values)) { if (lookup.Heap.TryGetValue(lookup.Handle, out item)) { - priority = item.Priority; - if (handler(ref priority, item.LocalID)) + uint pqueue = item.PriorityQueue; + uint localid = item.Value.Entity.LocalId; + + if (handler(ref pqueue, item.Value.Entity)) { - if (lookup.Heap.ContainsHandle(lookup.Handle)) - lookup.Heap[lookup.Handle] = - new MinHeapItem(priority, item.Value, item.LocalID, this.m_comparison); + // unless the priority queue has changed, there is no need to modify + // the entry + pqueue = Util.Clamp(pqueue, 0, m_numberOfQueues - 1); + if (pqueue != item.PriorityQueue) + { + lookup.Heap.Remove(lookup.Handle); + + LookupItem litem = lookup; + litem.Heap = m_heaps[pqueue]; + litem.Heap.Add(new MinHeapItem(pqueue, item), ref litem.Handle); + m_lookupTable[localid] = litem; + } } else { - m_log.Warn("[LLCLIENTVIEW]: UpdatePriorityHandler returned false, dropping update"); + m_log.WarnFormat("[LLCLIENTVIEW]: UpdatePriorityHandler returned false for {0}",item.Value.Entity.UUID); lookup.Heap.Remove(lookup.Handle); - this.m_lookupTable.Remove(item.LocalID); + this.m_lookupTable.Remove(localid); } } } } + public override string ToString() + { + string s = ""; + for (int i = 0; i < m_numberOfQueues; i++) + { + if (s != "") s += ","; + s += m_heaps[i].Count.ToString(); + } + return s; + } + #region MinHeapItem private struct MinHeapItem : IComparable { - private double priority; private EntityUpdate value; - private uint local_id; - private Comparison comparison; - - internal MinHeapItem(double priority, EntityUpdate value, uint local_id) : - this(priority, value, local_id, Comparer.Default) { } - internal MinHeapItem(double priority, EntityUpdate value, uint local_id, IComparer comparer) : - this(priority, value, local_id, new Comparison(comparer.Compare)) { } - internal MinHeapItem(double priority, EntityUpdate value, uint local_id, Comparison comparison) - { - this.priority = priority; - this.value = value; - this.local_id = local_id; - this.comparison = comparison; + internal EntityUpdate Value { + get { + return this.value; + } } - internal double Priority { get { return this.priority; } } - internal EntityUpdate Value { get { return this.value; } } - internal uint LocalID { get { return this.local_id; } } + private uint pqueue; + internal uint PriorityQueue { + get { + return this.pqueue; + } + } + + private Int32 entrytime; + internal Int32 EntryTime { + get { + return this.entrytime; + } + } + + private UInt64 entryorder; + internal UInt64 EntryOrder + { + get { + return this.entryorder; + } + } + + internal MinHeapItem(uint pqueue, MinHeapItem other) + { + this.entrytime = other.entrytime; + this.entryorder = other.entryorder; + this.value = other.value; + this.pqueue = pqueue; + } + + internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) + { + this.entrytime = Util.EnvironmentTickCount(); + this.entryorder = entryorder; + this.value = value; + this.pqueue = pqueue; + } public override string ToString() { - StringBuilder sb = new StringBuilder(); - sb.Append("["); - sb.Append(this.priority.ToString()); - sb.Append(","); - if (this.value != null) - sb.Append(this.value.ToString()); - sb.Append("]"); - return sb.ToString(); + return String.Format("[{0},{1},{2}]",pqueue,entryorder,value.Entity.LocalId); } public int CompareTo(MinHeapItem other) { - return this.comparison(this.priority, other.priority); + // I'm assuming that the root part of an SOG is added to the update queue + // before the component parts + return Comparer.Default.Compare(this.EntryOrder, other.EntryOrder); } } #endregion diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs index f9599f5a96..2764b05f1a 100644 --- a/OpenSim/Region/Framework/Scenes/Prioritizer.cs +++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs @@ -58,7 +58,7 @@ namespace OpenSim.Region.Framework.Scenes public class Prioritizer { -// private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); /// /// This is added to the priority of all child prims, to make sure that the root prim update is sent to the @@ -76,7 +76,74 @@ namespace OpenSim.Region.Framework.Scenes m_scene = scene; } - public double GetUpdatePriority(IClientAPI client, ISceneEntity entity) +// + public uint GetUpdatePriority(IClientAPI client, ISceneEntity entity) + { + if (entity == null) + { + m_log.WarnFormat("[PRIORITIZER] attempt to prioritize null entity"); + throw new InvalidOperationException("Prioritization entity not defined"); + } + + // If this is an update for our own avatar give it the highest priority + if (client.AgentId == entity.UUID) + return 0; + + // Get this agent's position + ScenePresence presence = m_scene.GetScenePresence(client.AgentId); + if (presence == null) + { + m_log.WarnFormat("[PRIORITIZER] attempt to prioritize agent no longer in the scene"); + throw new InvalidOperationException("Prioritization agent not defined"); + } + + // Use group position for child prims + Vector3 entityPos = entity.AbsolutePosition; + if (entity is SceneObjectPart) + { + SceneObjectGroup group = (entity as SceneObjectPart).ParentGroup; + if (group != null) + entityPos = group.AbsolutePosition; + } + + // Use the camera position for local agents and avatar position for remote agents + Vector3 presencePos = (presence.IsChildAgent) ? + presence.AbsolutePosition : + presence.CameraPosition; + + // Compute the distance... + double distance = Vector3.Distance(presencePos, entityPos); + + // And convert the distance to a priority queue, this computation gives queues + // at 10, 20, 40, 80, 160, 320, 640, and 1280m + uint pqueue = 1; + for (int i = 0; i < 8; i++) + { + if (distance < 10 * Math.Pow(2.0,i)) + break; + pqueue++; + } + + // If this is a root agent, then determine front & back + // Bump up the priority queue for any objects behind the avatar + if (! presence.IsChildAgent) + { + // Root agent, decrease priority for objects behind us + Vector3 camPosition = presence.CameraPosition; + Vector3 camAtAxis = presence.CameraAtAxis; + + // Plane equation + float d = -Vector3.Dot(camPosition, camAtAxis); + float p = Vector3.Dot(camAtAxis, entityPos) + d; + if (p < 0.0f) + pqueue++; + } + + return pqueue; + } +// + + public double bGetUpdatePriority(IClientAPI client, ISceneEntity entity) { double priority = 0; From 15c4bbea59066c5d5ed3d76d253a096788ac295a Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 4 Apr 2011 13:19:45 -0700 Subject: [PATCH 007/103] Fixed the prioritizer functions for the new priority queues --- .../Region/Framework/Scenes/Prioritizer.cs | 306 +++++------------- 1 file changed, 89 insertions(+), 217 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs index 2764b05f1a..a14bb7001c 100644 --- a/OpenSim/Region/Framework/Scenes/Prioritizer.cs +++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs @@ -60,15 +60,6 @@ namespace OpenSim.Region.Framework.Scenes { private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - /// - /// This is added to the priority of all child prims, to make sure that the root prim update is sent to the - /// viewer before child prim updates. - /// The adjustment is added to child prims and subtracted from root prims, so the gap ends up - /// being double. We do it both ways so that there is a still a priority delta even if the priority is already - /// double.MinValue or double.MaxValue. - /// - private double m_childPrimAdjustmentFactor = 0.05; - private Scene m_scene; public Prioritizer(Scene scene) @@ -76,9 +67,19 @@ namespace OpenSim.Region.Framework.Scenes m_scene = scene; } -// + /// + /// Returns the priority queue into which the update should be placed. Updates within a + /// queue will be processed in arrival order. There are currently 12 priority queues + /// implemented in PriorityQueue class in LLClientView. Queue 0 is generally retained + /// for avatar updates. The fair queuing discipline for processing the priority queues + /// assumes that the number of entities in each priority queues increases exponentially. + /// So for example... if queue 1 contains all updates within 10m of the avatar or camera + /// then queue 2 at 20m is about 3X bigger in space & about 3X bigger in total number + /// of updates. + /// public uint GetUpdatePriority(IClientAPI client, ISceneEntity entity) { + // If entity is null we have a serious problem if (entity == null) { m_log.WarnFormat("[PRIORITIZER] attempt to prioritize null entity"); @@ -89,6 +90,78 @@ namespace OpenSim.Region.Framework.Scenes if (client.AgentId == entity.UUID) return 0; + uint priority; + + switch (m_scene.UpdatePrioritizationScheme) + { + case UpdatePrioritizationSchemes.Time: + priority = GetPriorityByTime(client, entity); + break; + case UpdatePrioritizationSchemes.Distance: + priority = GetPriorityByDistance(client, entity); + break; + case UpdatePrioritizationSchemes.SimpleAngularDistance: + priority = GetPriorityByDistance(client, entity); // TODO: Reimplement SimpleAngularDistance + break; + case UpdatePrioritizationSchemes.FrontBack: + priority = GetPriorityByFrontBack(client, entity); + break; + case UpdatePrioritizationSchemes.BestAvatarResponsiveness: + priority = GetPriorityByBestAvatarResponsiveness(client, entity); + break; + default: + throw new InvalidOperationException("UpdatePrioritizationScheme not defined."); + } + + return priority; + } + + + private uint GetPriorityByTime(IClientAPI client, ISceneEntity entity) + { + return 1; + } + + private uint GetPriorityByDistance(IClientAPI client, ISceneEntity entity) + { + return ComputeDistancePriority(client,entity,false); + } + + private uint GetPriorityByFrontBack(IClientAPI client, ISceneEntity entity) + { + return ComputeDistancePriority(client,entity,true); + } + + private uint GetPriorityByBestAvatarResponsiveness(IClientAPI client, ISceneEntity entity) + { + uint pqueue = ComputeDistancePriority(client,entity,true); + + ScenePresence presence = m_scene.GetScenePresence(client.AgentId); + if (presence != null) + { + if (!presence.IsChildAgent) + { + if (entity is SceneObjectPart) + { + // Non physical prims are lower priority than physical prims + PhysicsActor physActor = ((SceneObjectPart)entity).ParentGroup.RootPart.PhysActor; + if (physActor == null || !physActor.IsPhysical) + pqueue++; + + // Attachments are high priority, + // MIC: shouldn't these already be in the highest priority queue already + // since their root position is same as the avatars? + if (((SceneObjectPart)entity).ParentGroup.RootPart.IsAttachment) + pqueue = 1; + } + } + } + + return pqueue; + } + + private uint ComputeDistancePriority(IClientAPI client, ISceneEntity entity, bool useFrontBack) + { // Get this agent's position ScenePresence presence = m_scene.GetScenePresence(client.AgentId); if (presence == null) @@ -97,7 +170,10 @@ namespace OpenSim.Region.Framework.Scenes throw new InvalidOperationException("Prioritization agent not defined"); } - // Use group position for child prims + // Use group position for child prims, since we are putting child prims in + // the same queue with the root of the group, the root prim (which goes into + // the queue first) should always be sent first, no need to adjust child prim + // priorities Vector3 entityPos = entity.AbsolutePosition; if (entity is SceneObjectPart) { @@ -125,8 +201,8 @@ namespace OpenSim.Region.Framework.Scenes } // If this is a root agent, then determine front & back - // Bump up the priority queue for any objects behind the avatar - if (! presence.IsChildAgent) + // Bump up the priority queue (drop the priority) for any objects behind the avatar + if (useFrontBack && ! presence.IsChildAgent) { // Root agent, decrease priority for objects behind us Vector3 camPosition = presence.CameraPosition; @@ -141,210 +217,6 @@ namespace OpenSim.Region.Framework.Scenes return pqueue; } -// - public double bGetUpdatePriority(IClientAPI client, ISceneEntity entity) - { - double priority = 0; - - if (entity == null) - return 100000; - - switch (m_scene.UpdatePrioritizationScheme) - { - case UpdatePrioritizationSchemes.Time: - priority = GetPriorityByTime(); - break; - case UpdatePrioritizationSchemes.Distance: - priority = GetPriorityByDistance(client, entity); - break; - case UpdatePrioritizationSchemes.SimpleAngularDistance: - priority = GetPriorityByDistance(client, entity); // TODO: Reimplement SimpleAngularDistance - break; - case UpdatePrioritizationSchemes.FrontBack: - priority = GetPriorityByFrontBack(client, entity); - break; - case UpdatePrioritizationSchemes.BestAvatarResponsiveness: - priority = GetPriorityByBestAvatarResponsiveness(client, entity); - break; - default: - throw new InvalidOperationException("UpdatePrioritizationScheme not defined."); - } - - // Adjust priority so that root prims are sent to the viewer first. This is especially important for - // attachments acting as huds, since current viewers fail to display hud child prims if their updates - // arrive before the root one. - if (entity is SceneObjectPart) - { - SceneObjectPart sop = ((SceneObjectPart)entity); - - if (sop.IsRoot) - { - if (priority >= double.MinValue + m_childPrimAdjustmentFactor) - priority -= m_childPrimAdjustmentFactor; - } - else - { - if (priority <= double.MaxValue - m_childPrimAdjustmentFactor) - priority += m_childPrimAdjustmentFactor; - } - } - - return priority; - } - - private double GetPriorityByTime() - { - return DateTime.UtcNow.ToOADate(); - } - - private double GetPriorityByDistance(IClientAPI client, ISceneEntity entity) - { - ScenePresence presence = m_scene.GetScenePresence(client.AgentId); - if (presence != null) - { - // If this is an update for our own avatar give it the highest priority - if (presence == entity) - return 0.0; - - // Use the camera position for local agents and avatar position for remote agents - Vector3 presencePos = (presence.IsChildAgent) ? - presence.AbsolutePosition : - presence.CameraPosition; - - // Use group position for child prims - Vector3 entityPos; - if (entity is SceneObjectPart) - { - // Can't use Scene.GetGroupByPrim() here, since the entity may have been delete from the scene - // before its scheduled update was triggered - //entityPos = m_scene.GetGroupByPrim(entity.LocalId).AbsolutePosition; - entityPos = ((SceneObjectPart)entity).ParentGroup.AbsolutePosition; - } - else - { - entityPos = entity.AbsolutePosition; - } - - return Vector3.DistanceSquared(presencePos, entityPos); - } - - return double.NaN; - } - - private double GetPriorityByFrontBack(IClientAPI client, ISceneEntity entity) - { - ScenePresence presence = m_scene.GetScenePresence(client.AgentId); - if (presence != null) - { - // If this is an update for our own avatar give it the highest priority - if (presence == entity) - return 0.0; - - // Use group position for child prims - Vector3 entityPos = entity.AbsolutePosition; - if (entity is SceneObjectPart) - { - // Can't use Scene.GetGroupByPrim() here, since the entity may have been delete from the scene - // before its scheduled update was triggered - //entityPos = m_scene.GetGroupByPrim(entity.LocalId).AbsolutePosition; - entityPos = ((SceneObjectPart)entity).ParentGroup.AbsolutePosition; - } - else - { - entityPos = entity.AbsolutePosition; - } - - if (!presence.IsChildAgent) - { - // Root agent. Use distance from camera and a priority decrease for objects behind us - Vector3 camPosition = presence.CameraPosition; - Vector3 camAtAxis = presence.CameraAtAxis; - - // Distance - double priority = Vector3.DistanceSquared(camPosition, entityPos); - - // Plane equation - float d = -Vector3.Dot(camPosition, camAtAxis); - float p = Vector3.Dot(camAtAxis, entityPos) + d; - if (p < 0.0f) priority *= 2.0; - - return priority; - } - else - { - // Child agent. Use the normal distance method - Vector3 presencePos = presence.AbsolutePosition; - - return Vector3.DistanceSquared(presencePos, entityPos); - } - } - - return double.NaN; - } - - private double GetPriorityByBestAvatarResponsiveness(IClientAPI client, ISceneEntity entity) - { - // If this is an update for our own avatar give it the highest priority - if (client.AgentId == entity.UUID) - return 0.0; - if (entity == null) - return double.NaN; - - // Use group position for child prims - Vector3 entityPos = entity.AbsolutePosition; - if (entity is SceneObjectPart) - { - SceneObjectGroup group = (entity as SceneObjectPart).ParentGroup; - if (group != null) - entityPos = group.AbsolutePosition; - else - entityPos = entity.AbsolutePosition; - } - else - entityPos = entity.AbsolutePosition; - - ScenePresence presence = m_scene.GetScenePresence(client.AgentId); - if (presence != null) - { - if (!presence.IsChildAgent) - { - if (entity is ScenePresence) - return 1.0; - - // Root agent. Use distance from camera and a priority decrease for objects behind us - Vector3 camPosition = presence.CameraPosition; - Vector3 camAtAxis = presence.CameraAtAxis; - - // Distance - double priority = Vector3.DistanceSquared(camPosition, entityPos); - - // Plane equation - float d = -Vector3.Dot(camPosition, camAtAxis); - float p = Vector3.Dot(camAtAxis, entityPos) + d; - if (p < 0.0f) priority *= 2.0; - - if (entity is SceneObjectPart) - { - PhysicsActor physActor = ((SceneObjectPart)entity).ParentGroup.RootPart.PhysActor; - if (physActor == null || !physActor.IsPhysical) - priority += 100; - - if (((SceneObjectPart)entity).ParentGroup.RootPart.IsAttachment) - priority = 1.0; - } - return priority; - } - else - { - // Child agent. Use the normal distance method - Vector3 presencePos = presence.AbsolutePosition; - - return Vector3.DistanceSquared(presencePos, entityPos); - } - } - - return double.NaN; - } } } From 8b134f37f2c8cc7895153af2fdc79e785f3b93e2 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 4 Apr 2011 14:18:26 -0700 Subject: [PATCH 008/103] Fix a bug in the computation of the RTO. Basically... the RTO (the time to wait to retransmit packets) always maxed out (no retransmissions for 24 or 48 seconds. Note that this is going to cause faster (and more) retransmissions. Fix for dynamic throttling needs to go with this. --- OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 65a8fe3ca8..9a8bfd357c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -149,7 +149,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Caches packed throttle information private byte[] m_packedThrottles; - private int m_defaultRTO = 3000; + private int m_defaultRTO = 1000; // 1sec is the recommendation in the RFC private int m_maxRTO = 60000; /// @@ -557,7 +557,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int rto = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR)); // Clamp the retransmission timeout to manageable values - rto = Utils.Clamp(RTO, m_defaultRTO, m_maxRTO); + rto = Utils.Clamp(rto, m_defaultRTO, m_maxRTO); RTO = rto; From 707b6673c91733cd1298261f9f8150e7bc0c3970 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Sat, 9 Apr 2011 00:25:00 +0100 Subject: [PATCH 009/103] minor: remove mono compiler warnings --- OpenSim/Client/MXP/ClientStack/MXPClientView.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index 93c6c98469..d1a0440524 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -66,8 +66,8 @@ namespace OpenSim.Client.MXP.ClientStack private readonly IScene m_scene; private readonly string m_firstName; private readonly string m_lastName; - private int m_objectsToSynchronize = 0; - private int m_objectsSynchronized = -1; +// private int m_objectsToSynchronize = 0; +// private int m_objectsSynchronized = -1; private Vector3 m_startPosition=new Vector3(128f, 128f, 128f); #endregion @@ -462,8 +462,8 @@ namespace OpenSim.Client.MXP.ClientStack public void MXPSendSynchronizationBegin(int objectCount) { - m_objectsToSynchronize = objectCount; - m_objectsSynchronized = 0; +// m_objectsToSynchronize = objectCount; +// m_objectsSynchronized = 0; SynchronizationBeginEventMessage synchronizationBeginEventMessage = new SynchronizationBeginEventMessage(); synchronizationBeginEventMessage.ObjectCount = (uint)objectCount; Session.Send(synchronizationBeginEventMessage); From 77cf9405de10f016d85d67b6016ed27d28ed898f Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 28 Mar 2011 10:00:53 -0700 Subject: [PATCH 010/103] Implements adaptive queue management and fair queueing for improved networking performance. Reprioritization algorithms need to be ported still. One is in place. --- .../ClientStack/LindenUDP/LLClientView.cs | 359 ++++++++++++------ .../Region/Framework/Scenes/Prioritizer.cs | 71 +++- 2 files changed, 307 insertions(+), 123 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 34d72ac69b..a3f2c15951 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -49,6 +49,8 @@ using Timer = System.Timers.Timer; using AssetLandmark = OpenSim.Framework.AssetLandmark; using Nini.Config; +using System.IO; + namespace OpenSim.Region.ClientStack.LindenUDP { public delegate bool PacketMethod(IClientAPI simClient, Packet packet); @@ -298,6 +300,77 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Used to adjust Sun Orbit values so Linden based viewers properly position sun private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; + // First log file or time has expired, start writing to a new log file +// +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- +// THIS IS DEBUGGING CODE & SHOULD BE REMOVED +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- + public class QueueLogger + { + public Int32 start = 0; + public StreamWriter Log = null; + private Dictionary m_idMap = new Dictionary(); + + public QueueLogger() + { + DateTime now = DateTime.Now; + String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss")); + Log = new StreamWriter(fname); + + start = Util.EnvironmentTickCount(); + } + + public int LookupID(UUID uuid) + { + int localid; + if (! m_idMap.TryGetValue(uuid,out localid)) + { + localid = m_idMap.Count + 1; + m_idMap[uuid] = localid; + } + + return localid; + } + } + + public static QueueLogger QueueLog = null; + + // ----------------------------------------------------------------- + public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue) + { + if (QueueLog == null) + QueueLog = new QueueLogger(); + + Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); + lock(QueueLog) + { + int cid = QueueLog.LookupID(client); + int aid = QueueLog.LookupID(avatar); + QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue); + } + } + + // ----------------------------------------------------------------- + public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup) + { + if (QueueLog == null) + QueueLog = new QueueLogger(); + + Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); + lock(QueueLog) + { + int cid = QueueLog.LookupID(client); + QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString()); + } + } +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- +// ----------------------------------------------------------------- +// + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected static Dictionary PacketHandlers = new Dictionary(); //Global/static handlers for all clients @@ -3547,18 +3620,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Primitive Packet/Data Sending Methods + /// /// Generate one of the object update packets based on PrimUpdateFlags /// and broadcast the packet to clients /// public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) { - double priority = m_prioritizer.GetUpdatePriority(this, entity); + //double priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = m_prioritizer.GetUpdatePriority(this, entity); lock (m_entityUpdates.SyncRoot) - m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation), entity.LocalId); + m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); } + private Int32 m_LastQueueFill = 0; + private uint m_maxUpdates = 0; + private void ProcessEntityUpdates(int maxUpdates) { OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); @@ -3566,23 +3644,55 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.Lazy> terseUpdateBlocks = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy>(); - if (maxUpdates <= 0) maxUpdates = Int32.MaxValue; + if (maxUpdates <= 0) + { + m_maxUpdates = Int32.MaxValue; + } + else + { + if (m_maxUpdates == 0 || m_LastQueueFill == 0) + { + m_maxUpdates = (uint)maxUpdates; + } + else + { + if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) + m_maxUpdates += 5; + else + m_maxUpdates = m_maxUpdates >> 1; + } + m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); + } + m_LastQueueFill = Util.EnvironmentTickCount(); + int updatesThisCall = 0; +// +// DEBUGGING CODE... REMOVE +// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates); +// // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race // condition where a kill can be processed before an out-of-date update for the same object. lock (m_killRecord) { float avgTimeDilation = 1.0f; EntityUpdate update; - while (updatesThisCall < maxUpdates) + Int32 timeinqueue; // this is just debugging code & can be dropped later + + while (updatesThisCall < m_maxUpdates) { lock (m_entityUpdates.SyncRoot) - if (!m_entityUpdates.TryDequeue(out update)) + if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) break; avgTimeDilation += update.TimeDilation; avgTimeDilation *= 0.5f; +// +// DEBUGGING CODE... REMOVE +// if (update.Entity is ScenePresence) +// LogAvatarUpdateEvent(this.m_agentId,update.Entity.UUID,timeinqueue); +// + if (update.Entity is SceneObjectPart) { SceneObjectPart part = (SceneObjectPart)update.Entity; @@ -3679,36 +3789,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - // if (update.Entity is SceneObjectPart && ((SceneObjectPart)update.Entity).IsAttachment) - // { - // SceneObjectPart sop = (SceneObjectPart)update.Entity; - // string text = sop.Text; - // if (text.IndexOf("\n") >= 0) - // text = text.Remove(text.IndexOf("\n")); - // - // if (m_attachmentsSent.Contains(sop.ParentID)) - // { - //// m_log.DebugFormat( - //// "[CLIENT]: Sending full info about attached prim {0} text {1}", - //// sop.LocalId, text); - // - // objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock(sop, this.m_agentId)); - // - // m_attachmentsSent.Add(sop.LocalId); - // } - // else - // { - // m_log.DebugFormat( - // "[CLIENT]: Requeueing full update of prim {0} text {1} since we haven't sent its parent {2} yet", - // sop.LocalId, text, sop.ParentID); - // - // m_entityUpdates.Enqueue(double.MaxValue, update, sop.LocalId); - // } - // } - // else - // { - objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); - // } + objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); } } else if (!canUseImproved) @@ -3802,26 +3883,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void ReprioritizeUpdates() { - //m_log.Debug("[CLIENT]: Reprioritizing prim updates for " + m_firstName + " " + m_lastName); - lock (m_entityUpdates.SyncRoot) m_entityUpdates.Reprioritize(UpdatePriorityHandler); } - private bool UpdatePriorityHandler(ref double priority, uint localID) + private bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity) { - EntityBase entity; - if (m_scene.Entities.TryGetValue(localID, out entity)) + if (entity != null) { priority = m_prioritizer.GetUpdatePriority(this, entity); + return true; } - return priority != double.NaN; + return false; } public void FlushPrimUpdates() { - m_log.Debug("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName); + m_log.WarnFormat("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName); while (m_entityUpdates.Count > 0) ProcessEntityUpdates(-1); @@ -11729,86 +11808,85 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region PriorityQueue public class PriorityQueue { - internal delegate bool UpdatePriorityHandler(ref double priority, uint local_id); + internal delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity); - private MinHeap[] m_heaps = new MinHeap[1]; + // Heap[0] for self updates + // Heap[1..12] for entity updates + + internal const uint m_numberOfQueues = 12; + private MinHeap[] m_heaps = new MinHeap[m_numberOfQueues]; private Dictionary m_lookupTable; - private Comparison m_comparison; private object m_syncRoot = new object(); - + private uint m_nextQueue = 0; + private UInt64 m_nextRequest = 0; + internal PriorityQueue() : - this(MinHeap.DEFAULT_CAPACITY, Comparer.Default) { } - internal PriorityQueue(int capacity) : - this(capacity, Comparer.Default) { } - internal PriorityQueue(IComparer comparer) : - this(new Comparison(comparer.Compare)) { } - internal PriorityQueue(Comparison comparison) : - this(MinHeap.DEFAULT_CAPACITY, comparison) { } - internal PriorityQueue(int capacity, IComparer comparer) : - this(capacity, new Comparison(comparer.Compare)) { } - internal PriorityQueue(int capacity, Comparison comparison) + this(MinHeap.DEFAULT_CAPACITY) { } + internal PriorityQueue(int capacity) { m_lookupTable = new Dictionary(capacity); for (int i = 0; i < m_heaps.Length; ++i) m_heaps[i] = new MinHeap(capacity); - this.m_comparison = comparison; } public object SyncRoot { get { return this.m_syncRoot; } } + internal int Count { get { int count = 0; for (int i = 0; i < m_heaps.Length; ++i) - count = m_heaps[i].Count; + count += m_heaps[i].Count; return count; } } - public bool Enqueue(double priority, EntityUpdate value, uint local_id) + public bool Enqueue(uint pqueue, EntityUpdate value) { - LookupItem item; + LookupItem lookup; - if (m_lookupTable.TryGetValue(local_id, out item)) + uint localid = value.Entity.LocalId; + UInt64 entry = m_nextRequest++; + if (m_lookupTable.TryGetValue(localid, out lookup)) { - // Combine flags - value.Flags |= item.Heap[item.Handle].Value.Flags; + entry = lookup.Heap[lookup.Handle].EntryOrder; + value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; + lookup.Heap.Remove(lookup.Handle); + } - item.Heap[item.Handle] = new MinHeapItem(priority, value, local_id, this.m_comparison); - return false; - } - else - { - item.Heap = m_heaps[0]; - item.Heap.Add(new MinHeapItem(priority, value, local_id, this.m_comparison), ref item.Handle); - m_lookupTable.Add(local_id, item); - return true; - } + pqueue = Util.Clamp(pqueue, 0, m_numberOfQueues - 1); + lookup.Heap = m_heaps[pqueue]; + lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle); + m_lookupTable[localid] = lookup; + + return true; } - internal EntityUpdate Peek() + internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) { - for (int i = 0; i < m_heaps.Length; ++i) - if (m_heaps[i].Count > 0) - return m_heaps[i].Min().Value; - throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); - } - - internal bool TryDequeue(out EntityUpdate value) - { - for (int i = 0; i < m_heaps.Length; ++i) + for (int i = 0; i < m_numberOfQueues; ++i) { - if (m_heaps[i].Count > 0) + // To get the fair queing, we cycle through each of the + // queues when finding an element to dequeue, this code + // assumes that the distribution of updates in the queues + // is polynomial, probably quadractic (eg distance of PI * R^2) + uint h = (uint)((m_nextQueue + i) % m_numberOfQueues); + if (m_heaps[h].Count > 0) { - MinHeapItem item = m_heaps[i].RemoveMin(); - m_lookupTable.Remove(item.LocalID); + m_nextQueue = (uint)((h + 1) % m_numberOfQueues); + + MinHeapItem item = m_heaps[h].RemoveMin(); + m_lookupTable.Remove(item.Value.Entity.LocalId); + timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime); value = item.Value; + return true; } } + timeinqueue = 0; value = default(EntityUpdate); return false; } @@ -11816,68 +11894,107 @@ namespace OpenSim.Region.ClientStack.LindenUDP internal void Reprioritize(UpdatePriorityHandler handler) { MinHeapItem item; - double priority; - foreach (LookupItem lookup in new List(this.m_lookupTable.Values)) { if (lookup.Heap.TryGetValue(lookup.Handle, out item)) { - priority = item.Priority; - if (handler(ref priority, item.LocalID)) + uint pqueue = item.PriorityQueue; + uint localid = item.Value.Entity.LocalId; + + if (handler(ref pqueue, item.Value.Entity)) { - if (lookup.Heap.ContainsHandle(lookup.Handle)) - lookup.Heap[lookup.Handle] = - new MinHeapItem(priority, item.Value, item.LocalID, this.m_comparison); + // unless the priority queue has changed, there is no need to modify + // the entry + pqueue = Util.Clamp(pqueue, 0, m_numberOfQueues - 1); + if (pqueue != item.PriorityQueue) + { + lookup.Heap.Remove(lookup.Handle); + + LookupItem litem = lookup; + litem.Heap = m_heaps[pqueue]; + litem.Heap.Add(new MinHeapItem(pqueue, item), ref litem.Handle); + m_lookupTable[localid] = litem; + } } else { - m_log.Warn("[LLCLIENTVIEW]: UpdatePriorityHandler returned false, dropping update"); + m_log.WarnFormat("[LLCLIENTVIEW]: UpdatePriorityHandler returned false for {0}",item.Value.Entity.UUID); lookup.Heap.Remove(lookup.Handle); - this.m_lookupTable.Remove(item.LocalID); + this.m_lookupTable.Remove(localid); } } } } + public override string ToString() + { + string s = ""; + for (int i = 0; i < m_numberOfQueues; i++) + { + if (s != "") s += ","; + s += m_heaps[i].Count.ToString(); + } + return s; + } + #region MinHeapItem private struct MinHeapItem : IComparable { - private double priority; private EntityUpdate value; - private uint local_id; - private Comparison comparison; - - internal MinHeapItem(double priority, EntityUpdate value, uint local_id) : - this(priority, value, local_id, Comparer.Default) { } - internal MinHeapItem(double priority, EntityUpdate value, uint local_id, IComparer comparer) : - this(priority, value, local_id, new Comparison(comparer.Compare)) { } - internal MinHeapItem(double priority, EntityUpdate value, uint local_id, Comparison comparison) - { - this.priority = priority; - this.value = value; - this.local_id = local_id; - this.comparison = comparison; + internal EntityUpdate Value { + get { + return this.value; + } } - internal double Priority { get { return this.priority; } } - internal EntityUpdate Value { get { return this.value; } } - internal uint LocalID { get { return this.local_id; } } + private uint pqueue; + internal uint PriorityQueue { + get { + return this.pqueue; + } + } + + private Int32 entrytime; + internal Int32 EntryTime { + get { + return this.entrytime; + } + } + + private UInt64 entryorder; + internal UInt64 EntryOrder + { + get { + return this.entryorder; + } + } + + internal MinHeapItem(uint pqueue, MinHeapItem other) + { + this.entrytime = other.entrytime; + this.entryorder = other.entryorder; + this.value = other.value; + this.pqueue = pqueue; + } + + internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) + { + this.entrytime = Util.EnvironmentTickCount(); + this.entryorder = entryorder; + this.value = value; + this.pqueue = pqueue; + } public override string ToString() { - StringBuilder sb = new StringBuilder(); - sb.Append("["); - sb.Append(this.priority.ToString()); - sb.Append(","); - if (this.value != null) - sb.Append(this.value.ToString()); - sb.Append("]"); - return sb.ToString(); + return String.Format("[{0},{1},{2}]",pqueue,entryorder,value.Entity.LocalId); } public int CompareTo(MinHeapItem other) { - return this.comparison(this.priority, other.priority); + // I'm assuming that the root part of an SOG is added to the update queue + // before the component parts + return Comparer.Default.Compare(this.EntryOrder, other.EntryOrder); } } #endregion diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs index f9599f5a96..2764b05f1a 100644 --- a/OpenSim/Region/Framework/Scenes/Prioritizer.cs +++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs @@ -58,7 +58,7 @@ namespace OpenSim.Region.Framework.Scenes public class Prioritizer { -// private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); /// /// This is added to the priority of all child prims, to make sure that the root prim update is sent to the @@ -76,7 +76,74 @@ namespace OpenSim.Region.Framework.Scenes m_scene = scene; } - public double GetUpdatePriority(IClientAPI client, ISceneEntity entity) +// + public uint GetUpdatePriority(IClientAPI client, ISceneEntity entity) + { + if (entity == null) + { + m_log.WarnFormat("[PRIORITIZER] attempt to prioritize null entity"); + throw new InvalidOperationException("Prioritization entity not defined"); + } + + // If this is an update for our own avatar give it the highest priority + if (client.AgentId == entity.UUID) + return 0; + + // Get this agent's position + ScenePresence presence = m_scene.GetScenePresence(client.AgentId); + if (presence == null) + { + m_log.WarnFormat("[PRIORITIZER] attempt to prioritize agent no longer in the scene"); + throw new InvalidOperationException("Prioritization agent not defined"); + } + + // Use group position for child prims + Vector3 entityPos = entity.AbsolutePosition; + if (entity is SceneObjectPart) + { + SceneObjectGroup group = (entity as SceneObjectPart).ParentGroup; + if (group != null) + entityPos = group.AbsolutePosition; + } + + // Use the camera position for local agents and avatar position for remote agents + Vector3 presencePos = (presence.IsChildAgent) ? + presence.AbsolutePosition : + presence.CameraPosition; + + // Compute the distance... + double distance = Vector3.Distance(presencePos, entityPos); + + // And convert the distance to a priority queue, this computation gives queues + // at 10, 20, 40, 80, 160, 320, 640, and 1280m + uint pqueue = 1; + for (int i = 0; i < 8; i++) + { + if (distance < 10 * Math.Pow(2.0,i)) + break; + pqueue++; + } + + // If this is a root agent, then determine front & back + // Bump up the priority queue for any objects behind the avatar + if (! presence.IsChildAgent) + { + // Root agent, decrease priority for objects behind us + Vector3 camPosition = presence.CameraPosition; + Vector3 camAtAxis = presence.CameraAtAxis; + + // Plane equation + float d = -Vector3.Dot(camPosition, camAtAxis); + float p = Vector3.Dot(camAtAxis, entityPos) + d; + if (p < 0.0f) + pqueue++; + } + + return pqueue; + } +// + + public double bGetUpdatePriority(IClientAPI client, ISceneEntity entity) { double priority = 0; From 0bd06d8ba85595a1707d2093a08a901b950ca120 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 4 Apr 2011 13:19:45 -0700 Subject: [PATCH 011/103] Fixed the prioritizer functions for the new priority queues --- .../Region/Framework/Scenes/Prioritizer.cs | 306 +++++------------- 1 file changed, 89 insertions(+), 217 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs index 2764b05f1a..a14bb7001c 100644 --- a/OpenSim/Region/Framework/Scenes/Prioritizer.cs +++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs @@ -60,15 +60,6 @@ namespace OpenSim.Region.Framework.Scenes { private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - /// - /// This is added to the priority of all child prims, to make sure that the root prim update is sent to the - /// viewer before child prim updates. - /// The adjustment is added to child prims and subtracted from root prims, so the gap ends up - /// being double. We do it both ways so that there is a still a priority delta even if the priority is already - /// double.MinValue or double.MaxValue. - /// - private double m_childPrimAdjustmentFactor = 0.05; - private Scene m_scene; public Prioritizer(Scene scene) @@ -76,9 +67,19 @@ namespace OpenSim.Region.Framework.Scenes m_scene = scene; } -// + /// + /// Returns the priority queue into which the update should be placed. Updates within a + /// queue will be processed in arrival order. There are currently 12 priority queues + /// implemented in PriorityQueue class in LLClientView. Queue 0 is generally retained + /// for avatar updates. The fair queuing discipline for processing the priority queues + /// assumes that the number of entities in each priority queues increases exponentially. + /// So for example... if queue 1 contains all updates within 10m of the avatar or camera + /// then queue 2 at 20m is about 3X bigger in space & about 3X bigger in total number + /// of updates. + /// public uint GetUpdatePriority(IClientAPI client, ISceneEntity entity) { + // If entity is null we have a serious problem if (entity == null) { m_log.WarnFormat("[PRIORITIZER] attempt to prioritize null entity"); @@ -89,6 +90,78 @@ namespace OpenSim.Region.Framework.Scenes if (client.AgentId == entity.UUID) return 0; + uint priority; + + switch (m_scene.UpdatePrioritizationScheme) + { + case UpdatePrioritizationSchemes.Time: + priority = GetPriorityByTime(client, entity); + break; + case UpdatePrioritizationSchemes.Distance: + priority = GetPriorityByDistance(client, entity); + break; + case UpdatePrioritizationSchemes.SimpleAngularDistance: + priority = GetPriorityByDistance(client, entity); // TODO: Reimplement SimpleAngularDistance + break; + case UpdatePrioritizationSchemes.FrontBack: + priority = GetPriorityByFrontBack(client, entity); + break; + case UpdatePrioritizationSchemes.BestAvatarResponsiveness: + priority = GetPriorityByBestAvatarResponsiveness(client, entity); + break; + default: + throw new InvalidOperationException("UpdatePrioritizationScheme not defined."); + } + + return priority; + } + + + private uint GetPriorityByTime(IClientAPI client, ISceneEntity entity) + { + return 1; + } + + private uint GetPriorityByDistance(IClientAPI client, ISceneEntity entity) + { + return ComputeDistancePriority(client,entity,false); + } + + private uint GetPriorityByFrontBack(IClientAPI client, ISceneEntity entity) + { + return ComputeDistancePriority(client,entity,true); + } + + private uint GetPriorityByBestAvatarResponsiveness(IClientAPI client, ISceneEntity entity) + { + uint pqueue = ComputeDistancePriority(client,entity,true); + + ScenePresence presence = m_scene.GetScenePresence(client.AgentId); + if (presence != null) + { + if (!presence.IsChildAgent) + { + if (entity is SceneObjectPart) + { + // Non physical prims are lower priority than physical prims + PhysicsActor physActor = ((SceneObjectPart)entity).ParentGroup.RootPart.PhysActor; + if (physActor == null || !physActor.IsPhysical) + pqueue++; + + // Attachments are high priority, + // MIC: shouldn't these already be in the highest priority queue already + // since their root position is same as the avatars? + if (((SceneObjectPart)entity).ParentGroup.RootPart.IsAttachment) + pqueue = 1; + } + } + } + + return pqueue; + } + + private uint ComputeDistancePriority(IClientAPI client, ISceneEntity entity, bool useFrontBack) + { // Get this agent's position ScenePresence presence = m_scene.GetScenePresence(client.AgentId); if (presence == null) @@ -97,7 +170,10 @@ namespace OpenSim.Region.Framework.Scenes throw new InvalidOperationException("Prioritization agent not defined"); } - // Use group position for child prims + // Use group position for child prims, since we are putting child prims in + // the same queue with the root of the group, the root prim (which goes into + // the queue first) should always be sent first, no need to adjust child prim + // priorities Vector3 entityPos = entity.AbsolutePosition; if (entity is SceneObjectPart) { @@ -125,8 +201,8 @@ namespace OpenSim.Region.Framework.Scenes } // If this is a root agent, then determine front & back - // Bump up the priority queue for any objects behind the avatar - if (! presence.IsChildAgent) + // Bump up the priority queue (drop the priority) for any objects behind the avatar + if (useFrontBack && ! presence.IsChildAgent) { // Root agent, decrease priority for objects behind us Vector3 camPosition = presence.CameraPosition; @@ -141,210 +217,6 @@ namespace OpenSim.Region.Framework.Scenes return pqueue; } -// - public double bGetUpdatePriority(IClientAPI client, ISceneEntity entity) - { - double priority = 0; - - if (entity == null) - return 100000; - - switch (m_scene.UpdatePrioritizationScheme) - { - case UpdatePrioritizationSchemes.Time: - priority = GetPriorityByTime(); - break; - case UpdatePrioritizationSchemes.Distance: - priority = GetPriorityByDistance(client, entity); - break; - case UpdatePrioritizationSchemes.SimpleAngularDistance: - priority = GetPriorityByDistance(client, entity); // TODO: Reimplement SimpleAngularDistance - break; - case UpdatePrioritizationSchemes.FrontBack: - priority = GetPriorityByFrontBack(client, entity); - break; - case UpdatePrioritizationSchemes.BestAvatarResponsiveness: - priority = GetPriorityByBestAvatarResponsiveness(client, entity); - break; - default: - throw new InvalidOperationException("UpdatePrioritizationScheme not defined."); - } - - // Adjust priority so that root prims are sent to the viewer first. This is especially important for - // attachments acting as huds, since current viewers fail to display hud child prims if their updates - // arrive before the root one. - if (entity is SceneObjectPart) - { - SceneObjectPart sop = ((SceneObjectPart)entity); - - if (sop.IsRoot) - { - if (priority >= double.MinValue + m_childPrimAdjustmentFactor) - priority -= m_childPrimAdjustmentFactor; - } - else - { - if (priority <= double.MaxValue - m_childPrimAdjustmentFactor) - priority += m_childPrimAdjustmentFactor; - } - } - - return priority; - } - - private double GetPriorityByTime() - { - return DateTime.UtcNow.ToOADate(); - } - - private double GetPriorityByDistance(IClientAPI client, ISceneEntity entity) - { - ScenePresence presence = m_scene.GetScenePresence(client.AgentId); - if (presence != null) - { - // If this is an update for our own avatar give it the highest priority - if (presence == entity) - return 0.0; - - // Use the camera position for local agents and avatar position for remote agents - Vector3 presencePos = (presence.IsChildAgent) ? - presence.AbsolutePosition : - presence.CameraPosition; - - // Use group position for child prims - Vector3 entityPos; - if (entity is SceneObjectPart) - { - // Can't use Scene.GetGroupByPrim() here, since the entity may have been delete from the scene - // before its scheduled update was triggered - //entityPos = m_scene.GetGroupByPrim(entity.LocalId).AbsolutePosition; - entityPos = ((SceneObjectPart)entity).ParentGroup.AbsolutePosition; - } - else - { - entityPos = entity.AbsolutePosition; - } - - return Vector3.DistanceSquared(presencePos, entityPos); - } - - return double.NaN; - } - - private double GetPriorityByFrontBack(IClientAPI client, ISceneEntity entity) - { - ScenePresence presence = m_scene.GetScenePresence(client.AgentId); - if (presence != null) - { - // If this is an update for our own avatar give it the highest priority - if (presence == entity) - return 0.0; - - // Use group position for child prims - Vector3 entityPos = entity.AbsolutePosition; - if (entity is SceneObjectPart) - { - // Can't use Scene.GetGroupByPrim() here, since the entity may have been delete from the scene - // before its scheduled update was triggered - //entityPos = m_scene.GetGroupByPrim(entity.LocalId).AbsolutePosition; - entityPos = ((SceneObjectPart)entity).ParentGroup.AbsolutePosition; - } - else - { - entityPos = entity.AbsolutePosition; - } - - if (!presence.IsChildAgent) - { - // Root agent. Use distance from camera and a priority decrease for objects behind us - Vector3 camPosition = presence.CameraPosition; - Vector3 camAtAxis = presence.CameraAtAxis; - - // Distance - double priority = Vector3.DistanceSquared(camPosition, entityPos); - - // Plane equation - float d = -Vector3.Dot(camPosition, camAtAxis); - float p = Vector3.Dot(camAtAxis, entityPos) + d; - if (p < 0.0f) priority *= 2.0; - - return priority; - } - else - { - // Child agent. Use the normal distance method - Vector3 presencePos = presence.AbsolutePosition; - - return Vector3.DistanceSquared(presencePos, entityPos); - } - } - - return double.NaN; - } - - private double GetPriorityByBestAvatarResponsiveness(IClientAPI client, ISceneEntity entity) - { - // If this is an update for our own avatar give it the highest priority - if (client.AgentId == entity.UUID) - return 0.0; - if (entity == null) - return double.NaN; - - // Use group position for child prims - Vector3 entityPos = entity.AbsolutePosition; - if (entity is SceneObjectPart) - { - SceneObjectGroup group = (entity as SceneObjectPart).ParentGroup; - if (group != null) - entityPos = group.AbsolutePosition; - else - entityPos = entity.AbsolutePosition; - } - else - entityPos = entity.AbsolutePosition; - - ScenePresence presence = m_scene.GetScenePresence(client.AgentId); - if (presence != null) - { - if (!presence.IsChildAgent) - { - if (entity is ScenePresence) - return 1.0; - - // Root agent. Use distance from camera and a priority decrease for objects behind us - Vector3 camPosition = presence.CameraPosition; - Vector3 camAtAxis = presence.CameraAtAxis; - - // Distance - double priority = Vector3.DistanceSquared(camPosition, entityPos); - - // Plane equation - float d = -Vector3.Dot(camPosition, camAtAxis); - float p = Vector3.Dot(camAtAxis, entityPos) + d; - if (p < 0.0f) priority *= 2.0; - - if (entity is SceneObjectPart) - { - PhysicsActor physActor = ((SceneObjectPart)entity).ParentGroup.RootPart.PhysActor; - if (physActor == null || !physActor.IsPhysical) - priority += 100; - - if (((SceneObjectPart)entity).ParentGroup.RootPart.IsAttachment) - priority = 1.0; - } - return priority; - } - else - { - // Child agent. Use the normal distance method - Vector3 presencePos = presence.AbsolutePosition; - - return Vector3.DistanceSquared(presencePos, entityPos); - } - } - - return double.NaN; - } } } From 83dc2470f2e815f6f9d469104be3a2806438a29a Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 4 Apr 2011 14:18:26 -0700 Subject: [PATCH 012/103] Fix a bug in the computation of the RTO. Basically... the RTO (the time to wait to retransmit packets) always maxed out (no retransmissions for 24 or 48 seconds. Note that this is going to cause faster (and more) retransmissions. Fix for dynamic throttling needs to go with this. --- OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 65a8fe3ca8..9a8bfd357c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -149,7 +149,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Caches packed throttle information private byte[] m_packedThrottles; - private int m_defaultRTO = 3000; + private int m_defaultRTO = 1000; // 1sec is the recommendation in the RFC private int m_maxRTO = 60000; /// @@ -557,7 +557,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int rto = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR)); // Clamp the retransmission timeout to manageable values - rto = Utils.Clamp(RTO, m_defaultRTO, m_maxRTO); + rto = Utils.Clamp(rto, m_defaultRTO, m_maxRTO); RTO = rto; From 19c6d1d569e081418e139d139c15ea66640288ba Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Wed, 6 Apr 2011 09:26:38 -0700 Subject: [PATCH 013/103] Split the priority queue class into a seperate file. LLClientView is big enough. --- .../ClientStack/LindenUDP/LLClientView.cs | 205 --------------- .../ClientStack/LindenUDP/PriorityQueue.cs | 245 ++++++++++++++++++ 2 files changed, 245 insertions(+), 205 deletions(-) create mode 100644 OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index a3f2c15951..a7240991dc 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -11805,209 +11805,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } - #region PriorityQueue - public class PriorityQueue - { - internal delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity); - - // Heap[0] for self updates - // Heap[1..12] for entity updates - - internal const uint m_numberOfQueues = 12; - private MinHeap[] m_heaps = new MinHeap[m_numberOfQueues]; - private Dictionary m_lookupTable; - private object m_syncRoot = new object(); - private uint m_nextQueue = 0; - private UInt64 m_nextRequest = 0; - - internal PriorityQueue() : - this(MinHeap.DEFAULT_CAPACITY) { } - internal PriorityQueue(int capacity) - { - m_lookupTable = new Dictionary(capacity); - - for (int i = 0; i < m_heaps.Length; ++i) - m_heaps[i] = new MinHeap(capacity); - } - - public object SyncRoot { get { return this.m_syncRoot; } } - - internal int Count - { - get - { - int count = 0; - for (int i = 0; i < m_heaps.Length; ++i) - count += m_heaps[i].Count; - return count; - } - } - - public bool Enqueue(uint pqueue, EntityUpdate value) - { - LookupItem lookup; - - uint localid = value.Entity.LocalId; - UInt64 entry = m_nextRequest++; - if (m_lookupTable.TryGetValue(localid, out lookup)) - { - entry = lookup.Heap[lookup.Handle].EntryOrder; - value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; - lookup.Heap.Remove(lookup.Handle); - } - - pqueue = Util.Clamp(pqueue, 0, m_numberOfQueues - 1); - lookup.Heap = m_heaps[pqueue]; - lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle); - m_lookupTable[localid] = lookup; - - return true; - } - - internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) - { - for (int i = 0; i < m_numberOfQueues; ++i) - { - // To get the fair queing, we cycle through each of the - // queues when finding an element to dequeue, this code - // assumes that the distribution of updates in the queues - // is polynomial, probably quadractic (eg distance of PI * R^2) - uint h = (uint)((m_nextQueue + i) % m_numberOfQueues); - if (m_heaps[h].Count > 0) - { - m_nextQueue = (uint)((h + 1) % m_numberOfQueues); - - MinHeapItem item = m_heaps[h].RemoveMin(); - m_lookupTable.Remove(item.Value.Entity.LocalId); - timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime); - value = item.Value; - - return true; - } - } - - timeinqueue = 0; - value = default(EntityUpdate); - return false; - } - - internal void Reprioritize(UpdatePriorityHandler handler) - { - MinHeapItem item; - foreach (LookupItem lookup in new List(this.m_lookupTable.Values)) - { - if (lookup.Heap.TryGetValue(lookup.Handle, out item)) - { - uint pqueue = item.PriorityQueue; - uint localid = item.Value.Entity.LocalId; - - if (handler(ref pqueue, item.Value.Entity)) - { - // unless the priority queue has changed, there is no need to modify - // the entry - pqueue = Util.Clamp(pqueue, 0, m_numberOfQueues - 1); - if (pqueue != item.PriorityQueue) - { - lookup.Heap.Remove(lookup.Handle); - - LookupItem litem = lookup; - litem.Heap = m_heaps[pqueue]; - litem.Heap.Add(new MinHeapItem(pqueue, item), ref litem.Handle); - m_lookupTable[localid] = litem; - } - } - else - { - m_log.WarnFormat("[LLCLIENTVIEW]: UpdatePriorityHandler returned false for {0}",item.Value.Entity.UUID); - lookup.Heap.Remove(lookup.Handle); - this.m_lookupTable.Remove(localid); - } - } - } - } - - public override string ToString() - { - string s = ""; - for (int i = 0; i < m_numberOfQueues; i++) - { - if (s != "") s += ","; - s += m_heaps[i].Count.ToString(); - } - return s; - } - - #region MinHeapItem - private struct MinHeapItem : IComparable - { - private EntityUpdate value; - internal EntityUpdate Value { - get { - return this.value; - } - } - - private uint pqueue; - internal uint PriorityQueue { - get { - return this.pqueue; - } - } - - private Int32 entrytime; - internal Int32 EntryTime { - get { - return this.entrytime; - } - } - - private UInt64 entryorder; - internal UInt64 EntryOrder - { - get { - return this.entryorder; - } - } - - internal MinHeapItem(uint pqueue, MinHeapItem other) - { - this.entrytime = other.entrytime; - this.entryorder = other.entryorder; - this.value = other.value; - this.pqueue = pqueue; - } - - internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) - { - this.entrytime = Util.EnvironmentTickCount(); - this.entryorder = entryorder; - this.value = value; - this.pqueue = pqueue; - } - - public override string ToString() - { - return String.Format("[{0},{1},{2}]",pqueue,entryorder,value.Entity.LocalId); - } - - public int CompareTo(MinHeapItem other) - { - // I'm assuming that the root part of an SOG is added to the update queue - // before the component parts - return Comparer.Default.Compare(this.EntryOrder, other.EntryOrder); - } - } - #endregion - - #region LookupItem - private struct LookupItem - { - internal MinHeap Heap; - internal IHandle Handle; - } - #endregion - } - public struct PacketProcessor { public PacketMethod method; @@ -12028,8 +11825,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - #endregion - public static OSD BuildEvent(string eventName, OSD eventBody) { OSDMap osdEvent = new OSDMap(2); diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs new file mode 100644 index 0000000000..364ce4ba9e --- /dev/null +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -0,0 +1,245 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; + +using OpenSim.Framework; +using OpenSim.Framework.Client; +using log4net; + +namespace OpenSim.Region.ClientStack.LindenUDP +{ + public class PriorityQueue + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + internal delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity); + + // Heap[0] for self updates + // Heap[1..12] for entity updates + + internal const uint m_numberOfQueues = 12; + + private MinHeap[] m_heaps = new MinHeap[m_numberOfQueues]; + private Dictionary m_lookupTable; + private uint m_nextQueue = 0; + private UInt64 m_nextRequest = 0; + + private object m_syncRoot = new object(); + public object SyncRoot { + get { return this.m_syncRoot; } + } + + internal PriorityQueue() : this(MinHeap.DEFAULT_CAPACITY) { } + + internal PriorityQueue(int capacity) + { + m_lookupTable = new Dictionary(capacity); + + for (int i = 0; i < m_heaps.Length; ++i) + m_heaps[i] = new MinHeap(capacity); + } + + internal int Count + { + get + { + int count = 0; + for (int i = 0; i < m_heaps.Length; ++i) + count += m_heaps[i].Count; + return count; + } + } + + public bool Enqueue(uint pqueue, EntityUpdate value) + { + LookupItem lookup; + + uint localid = value.Entity.LocalId; + UInt64 entry = m_nextRequest++; + if (m_lookupTable.TryGetValue(localid, out lookup)) + { + entry = lookup.Heap[lookup.Handle].EntryOrder; + value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; + lookup.Heap.Remove(lookup.Handle); + } + + pqueue = Util.Clamp(pqueue, 0, m_numberOfQueues - 1); + lookup.Heap = m_heaps[pqueue]; + lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle); + m_lookupTable[localid] = lookup; + + return true; + } + + internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) + { + for (int i = 0; i < m_numberOfQueues; ++i) + { + // To get the fair queing, we cycle through each of the + // queues when finding an element to dequeue, this code + // assumes that the distribution of updates in the queues + // is polynomial, probably quadractic (eg distance of PI * R^2) + uint h = (uint)((m_nextQueue + i) % m_numberOfQueues); + if (m_heaps[h].Count > 0) + { + m_nextQueue = (uint)((h + 1) % m_numberOfQueues); + + MinHeapItem item = m_heaps[h].RemoveMin(); + m_lookupTable.Remove(item.Value.Entity.LocalId); + timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime); + value = item.Value; + + return true; + } + } + + timeinqueue = 0; + value = default(EntityUpdate); + return false; + } + + internal void Reprioritize(UpdatePriorityHandler handler) + { + MinHeapItem item; + foreach (LookupItem lookup in new List(this.m_lookupTable.Values)) + { + if (lookup.Heap.TryGetValue(lookup.Handle, out item)) + { + uint pqueue = item.PriorityQueue; + uint localid = item.Value.Entity.LocalId; + + if (handler(ref pqueue, item.Value.Entity)) + { + // unless the priority queue has changed, there is no need to modify + // the entry + pqueue = Util.Clamp(pqueue, 0, m_numberOfQueues - 1); + if (pqueue != item.PriorityQueue) + { + lookup.Heap.Remove(lookup.Handle); + + LookupItem litem = lookup; + litem.Heap = m_heaps[pqueue]; + litem.Heap.Add(new MinHeapItem(pqueue, item), ref litem.Handle); + m_lookupTable[localid] = litem; + } + } + else + { + // m_log.WarnFormat("[PQUEUE]: UpdatePriorityHandler returned false for {0}",item.Value.Entity.UUID); + lookup.Heap.Remove(lookup.Handle); + this.m_lookupTable.Remove(localid); + } + } + } + } + + public override string ToString() + { + string s = ""; + for (int i = 0; i < m_numberOfQueues; i++) + { + if (s != "") s += ","; + s += m_heaps[i].Count.ToString(); + } + return s; + } + +#region MinHeapItem + private struct MinHeapItem : IComparable + { + private EntityUpdate value; + internal EntityUpdate Value { + get { + return this.value; + } + } + + private uint pqueue; + internal uint PriorityQueue { + get { + return this.pqueue; + } + } + + private Int32 entrytime; + internal Int32 EntryTime { + get { + return this.entrytime; + } + } + + private UInt64 entryorder; + internal UInt64 EntryOrder + { + get { + return this.entryorder; + } + } + + internal MinHeapItem(uint pqueue, MinHeapItem other) + { + this.entrytime = other.entrytime; + this.entryorder = other.entryorder; + this.value = other.value; + this.pqueue = pqueue; + } + + internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) + { + this.entrytime = Util.EnvironmentTickCount(); + this.entryorder = entryorder; + this.value = value; + this.pqueue = pqueue; + } + + public override string ToString() + { + return String.Format("[{0},{1},{2}]",pqueue,entryorder,value.Entity.LocalId); + } + + public int CompareTo(MinHeapItem other) + { + // I'm assuming that the root part of an SOG is added to the update queue + // before the component parts + return Comparer.Default.Compare(this.EntryOrder, other.EntryOrder); + } + } +#endregion + +#region LookupItem + private struct LookupItem + { + internal MinHeap Heap; + internal IHandle Handle; + } +#endregion + } +} From ebc249e3bef57aee656936f5e63842d83ee29fff Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Sun, 10 Apr 2011 17:02:40 -0700 Subject: [PATCH 014/103] Changed the "not in scene" check in the prioritizier to just a warning. There appears to be a race condition on slow logins that attempts to prioritize before the scene presence is fully initialized. --- OpenSim/Region/Framework/Scenes/Prioritizer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs index a14bb7001c..4694e2b684 100644 --- a/OpenSim/Region/Framework/Scenes/Prioritizer.cs +++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs @@ -166,8 +166,9 @@ namespace OpenSim.Region.Framework.Scenes ScenePresence presence = m_scene.GetScenePresence(client.AgentId); if (presence == null) { - m_log.WarnFormat("[PRIORITIZER] attempt to prioritize agent no longer in the scene"); - throw new InvalidOperationException("Prioritization agent not defined"); + m_log.WarnFormat("[PRIORITIZER] attempt to use agent {0} not in the scene",client.AgentId); + // throw new InvalidOperationException("Prioritization agent not defined"); + return Int32.MaxValue; } // Use group position for child prims, since we are putting child prims in From f778056c7a81443b11c0c4288979af3a8c6b5b9f Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 11 Apr 2011 08:37:43 -0700 Subject: [PATCH 015/103] Removed some priority queue debugging code --- .../ClientStack/LindenUDP/LLClientView.cs | 80 ------------------- 1 file changed, 80 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index a7240991dc..b5bf26d0d0 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -300,77 +300,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Used to adjust Sun Orbit values so Linden based viewers properly position sun private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; - // First log file or time has expired, start writing to a new log file -// -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// THIS IS DEBUGGING CODE & SHOULD BE REMOVED -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- - public class QueueLogger - { - public Int32 start = 0; - public StreamWriter Log = null; - private Dictionary m_idMap = new Dictionary(); - - public QueueLogger() - { - DateTime now = DateTime.Now; - String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss")); - Log = new StreamWriter(fname); - - start = Util.EnvironmentTickCount(); - } - - public int LookupID(UUID uuid) - { - int localid; - if (! m_idMap.TryGetValue(uuid,out localid)) - { - localid = m_idMap.Count + 1; - m_idMap[uuid] = localid; - } - - return localid; - } - } - - public static QueueLogger QueueLog = null; - - // ----------------------------------------------------------------- - public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - int aid = QueueLog.LookupID(avatar); - QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue); - } - } - - // ----------------------------------------------------------------- - public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString()); - } - } -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected static Dictionary PacketHandlers = new Dictionary(); //Global/static handlers for all clients @@ -3667,10 +3596,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP int updatesThisCall = 0; -// -// DEBUGGING CODE... REMOVE -// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates); -// // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race // condition where a kill can be processed before an out-of-date update for the same object. lock (m_killRecord) @@ -3687,11 +3612,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP avgTimeDilation += update.TimeDilation; avgTimeDilation *= 0.5f; -// -// DEBUGGING CODE... REMOVE -// if (update.Entity is ScenePresence) -// LogAvatarUpdateEvent(this.m_agentId,update.Entity.UUID,timeinqueue); -// if (update.Entity is SceneObjectPart) { From 6e9cdb9ce32807ddd1a39e72c436b8fd788768d2 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 11 Apr 2011 09:06:28 -0700 Subject: [PATCH 016/103] New tokenbucket algorithm. This one provides fair sharing of the queues when client and simulator throttles are set. This algorithm also uses pre-defined burst rate of 150% of the sustained rate for each of the throttles. Removed the "state" queue. The state queue is not a Linden queue and appeared to be used just to get kill packets sent. --- .../ClientStack/LindenUDP/LLClientView.cs | 6 +- .../ClientStack/LindenUDP/LLUDPClient.cs | 96 +++--- .../ClientStack/LindenUDP/LLUDPServer.cs | 2 +- .../ClientStack/LindenUDP/TokenBucket.cs | 314 +++++++++++------- 4 files changed, 266 insertions(+), 152 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 8de31d7069..934a2d5c84 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -1610,7 +1610,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - OutPacket(kill, ThrottleOutPacketType.State); + // OutPacket(kill, ThrottleOutPacketType.State); + OutPacket(kill, ThrottleOutPacketType.Task); } } @@ -2440,7 +2441,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Effect = effectBlocks; - OutPacket(packet, ThrottleOutPacketType.State); + // OutPacket(packet, ThrottleOutPacketType.State); + OutPacket(packet, ThrottleOutPacketType.Task); } public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 9a8bfd357c..5a69851b3c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -135,7 +135,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_nextOnQueueEmpty = 1; /// Throttle bucket for this agent's connection - private readonly TokenBucket m_throttle; + private readonly TokenBucket m_throttleClient; + /// Throttle bucket for this agent's connection + private readonly TokenBucket m_throttleCategory; /// Throttle buckets for each packet category private readonly TokenBucket[] m_throttleCategories; /// Outgoing queues for throttled packets @@ -174,7 +176,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_maxRTO = maxRTO; // Create a token bucket throttle for this client that has the scene token bucket as a parent - m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); + m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit); + // Create a token bucket throttle for the total categary with the client bucket as a throttle + m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit); // Create an array of token buckets for this clients different throttle categories m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; @@ -185,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Initialize the packet outboxes, where packets sit while they are waiting for tokens m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); // Initialize the token buckets that control the throttling for each category - m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); + m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type)); } // Default the retransmission timeout to three seconds @@ -206,6 +210,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_packetOutboxes[i].Clear(); m_nextPackets[i] = null; } + + // pull the throttle out of the scene throttle + m_throttleClient.Parent.UnregisterRequest(m_throttleClient); OnPacketStats = null; OnQueueEmpty = null; } @@ -216,6 +223,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Information about the client connection public ClientInfo GetClientInfo() { +/// + TokenBucket tb; + + tb = m_throttleClient.Parent; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT"); + + tb = m_throttleClient; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT"); + + tb = m_throttleCategory; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY"); + + for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) + { + tb = m_throttleCategories[i]; + m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET"); + } + +/// + // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists // of pending and needed ACKs for every client every time some method wants information about // this connection is a recipe for poor performance @@ -223,13 +250,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP info.pendingAcks = new Dictionary(); info.needAck = new Dictionary(); - info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; - info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; - info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; - info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; - info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; - info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; - info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; + info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; + info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; + info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; + info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; + // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; + info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + info.taskThrottle + info.assetThrottle + info.textureThrottle; @@ -317,8 +345,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); // State is a subcategory of task that we allocate a percentage to - int state = (int)((float)task * STATE_TASK_PERCENTAGE); - task -= state; + int state = 0; + // int state = (int)((float)task * STATE_TASK_PERCENTAGE); + // task -= state; // Make sure none of the throttles are set below our packet MTU, // otherwise a throttle could become permanently clogged @@ -339,40 +368,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Update the token buckets with new throttle values TokenBucket bucket; - bucket = m_throttle; - bucket.MaxBurst = total; + bucket = m_throttleCategory; + bucket.RequestedDripRate = total; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; - bucket.DripRate = resend; - bucket.MaxBurst = resend; + bucket.RequestedDripRate = resend; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; - bucket.DripRate = land; - bucket.MaxBurst = land; + bucket.RequestedDripRate = land; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; - bucket.DripRate = wind; - bucket.MaxBurst = wind; + bucket.RequestedDripRate = wind; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; - bucket.DripRate = cloud; - bucket.MaxBurst = cloud; + bucket.RequestedDripRate = cloud; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; - bucket.DripRate = asset; - bucket.MaxBurst = asset; + bucket.RequestedDripRate = asset; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; - bucket.DripRate = task + state; - bucket.MaxBurst = task + state; + bucket.RequestedDripRate = task; bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; - bucket.DripRate = state; - bucket.MaxBurst = state; + bucket.RequestedDripRate = state; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; - bucket.DripRate = texture; - bucket.MaxBurst = texture; + bucket.RequestedDripRate = texture; // Reset the packed throttles cached data m_packedThrottles = null; @@ -387,14 +408,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP data = new byte[7 * 4]; int i = 0; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + - m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4; m_packedThrottles = data; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 583214ca0b..d08b25f79d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -228,7 +228,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } #endregion BinaryStats - m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); + m_throttle = new TokenBucket(null, sceneThrottleBps); ThrottleRates = new ThrottleRates(configSource); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 0a8331f32a..e4d59ff829 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -26,6 +26,10 @@ */ using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using log4net; namespace OpenSim.Region.ClientStack.LindenUDP { @@ -35,89 +39,126 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public class TokenBucket { - /// Parent bucket to this bucket, or null if this is a root - /// bucket - TokenBucket parent; - /// Size of the bucket in bytes. If zero, the bucket has - /// infinite capacity - int maxBurst; - /// Rate that the bucket fills, in bytes per millisecond. If - /// zero, the bucket always remains full - int tokensPerMS; - /// Number of tokens currently in the bucket - int content; - /// Time of the last drip, in system ticks - int lastDrip; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static Int32 m_counter = 0; + + private Int32 m_identifier; + + /// + /// Number of ticks (ms) per quantum, drip rate and max burst + /// are defined over this interval. + /// + private const Int32 m_ticksPerQuantum = 1000; - #region Properties + /// + /// This is the number of quantums worth of packets that can + /// be accommodated during a burst + /// + private const Double m_quantumsPerBurst = 1.5; + + /// + /// + private const Int32 m_minimumDripRate = 1400; + + /// Time of the last drip, in system ticks + private Int32 m_lastDrip; + + /// + /// The number of bytes that can be sent at this moment. This is the + /// current number of tokens in the bucket + /// + private Int64 m_tokenCount; + + /// + /// Map of children buckets and their requested maximum burst rate + /// + private Dictionary m_children = new Dictionary(); + +#region Properties /// /// The parent bucket of this bucket, or null if this bucket has no /// parent. The parent bucket will limit the aggregate bandwidth of all /// of its children buckets /// + private TokenBucket m_parent; public TokenBucket Parent { - get { return parent; } + get { return m_parent; } + set { m_parent = value; } } /// /// Maximum burst rate in bytes per second. This is the maximum number - /// of tokens that can accumulate in the bucket at any one time + /// of tokens that can accumulate in the bucket at any one time. This + /// also sets the total request for leaf nodes /// - public int MaxBurst + private Int64 m_burstRate; + public Int64 RequestedBurstRate { - get { return maxBurst; } - set { maxBurst = (value >= 0 ? value : 0); } + get { return m_burstRate; } + set { m_burstRate = (value < 0 ? 0 : value); } } + public Int64 BurstRate + { + get { + double rate = RequestedBurstRate * BurstRateModifier(); + if (rate < m_minimumDripRate * m_quantumsPerBurst) + rate = m_minimumDripRate * m_quantumsPerBurst; + + return (Int64) rate; + } + } + /// /// The speed limit of this bucket in bytes per second. This is the - /// number of tokens that are added to the bucket per second + /// number of tokens that are added to the bucket per quantum /// /// Tokens are added to the bucket any time /// is called, at the granularity of /// the system tick interval (typically around 15-22ms) - public int DripRate + private Int64 m_dripRate; + public Int64 RequestedDripRate { - get { return tokensPerMS * 1000; } - set - { - if (value == 0) - tokensPerMS = 0; - else - { - int bpms = (int)((float)value / 1000.0f); + get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } + set { + m_dripRate = (value < 0 ? 0 : value); + m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); + m_totalDripRequest = m_dripRate; + if (m_parent != null) + m_parent.RegisterRequest(this,m_dripRate); + } + } - if (bpms <= 0) - tokensPerMS = 1; // 1 byte/ms is the minimum granularity - else - tokensPerMS = bpms; - } + public Int64 DripRate + { + get { + if (m_parent == null) + return Math.Min(RequestedDripRate,TotalDripRequest); + + double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); + if (rate < m_minimumDripRate) + rate = m_minimumDripRate; + + return (Int64)rate; } } /// - /// The speed limit of this bucket in bytes per millisecond + /// The current total of the requested maximum burst rates of + /// this bucket's children buckets. /// - public int DripPerMS - { - get { return tokensPerMS; } - } + private Int64 m_totalDripRequest; + public Int64 TotalDripRequest + { + get { return m_totalDripRequest; } + set { m_totalDripRequest = value; } + } + +#endregion Properties - /// - /// The number of bytes that can be sent at this moment. This is the - /// current number of tokens in the bucket - /// If this bucket has a parent bucket that does not have - /// enough tokens for a request, will - /// return false regardless of the content of this bucket - /// - public int Content - { - get { return content; } - } - - #endregion Properties +#region Constructor /// /// Default constructor @@ -128,56 +169,115 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// zero if this bucket has no maximum capacity /// Rate that the bucket fills, in bytes per /// second. If zero, the bucket always remains full - public TokenBucket(TokenBucket parent, int maxBurst, int dripRate) + public TokenBucket(TokenBucket parent, Int64 dripRate) { - this.parent = parent; - MaxBurst = maxBurst; - DripRate = dripRate; - lastDrip = Environment.TickCount & Int32.MaxValue; + m_identifier = m_counter++; + + Parent = parent; + RequestedDripRate = dripRate; + // TotalDripRequest = dripRate; // this will be overwritten when a child node registers + // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); + m_lastDrip = Environment.TickCount & Int32.MaxValue; } +#endregion Constructor + + /// + /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning + /// no modification if the requested bandwidth is less than the + /// max burst bandwidth all the way to the root of the throttle + /// hierarchy. However, if any of the parents is over-booked, then + /// the modifier will be less than 1. + /// + private double DripRateModifier() + { + Int64 driprate = DripRate; + return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; + } + + /// + /// + private double BurstRateModifier() + { + // for now... burst rate is always m_quantumsPerBurst (constant) + // larger than drip rate so the ratio of burst requests is the + // same as the drip ratio + return DripRateModifier(); + } + + /// + /// Register drip rate requested by a child of this throttle. Pass the + /// changes up the hierarchy. + /// + public void RegisterRequest(TokenBucket child, Int64 request) + { + m_children[child] = request; + // m_totalDripRequest = m_children.Values.Sum(); + + m_totalDripRequest = 0; + foreach (KeyValuePair cref in m_children) + m_totalDripRequest += cref.Value; + + // Pass the new values up to the parent + if (m_parent != null) + m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); + } + + /// + /// Remove the rate requested by a child of this throttle. Pass the + /// changes up the hierarchy. + /// + public void UnregisterRequest(TokenBucket child) + { + m_children.Remove(child); + // m_totalDripRequest = m_children.Values.Sum(); + + m_totalDripRequest = 0; + foreach (KeyValuePair cref in m_children) + m_totalDripRequest += cref.Value; + + // Pass the new values up to the parent + if (m_parent != null) + m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); + } + /// /// Remove a given number of tokens from the bucket /// /// Number of tokens to remove from the bucket /// True if the requested number of tokens were removed from /// the bucket, otherwise false - public bool RemoveTokens(int amount) + public bool RemoveTokens(Int64 amount) { - bool dummy; - return RemoveTokens(amount, out dummy); + // Deposit tokens for this interval + Drip(); + + // If we have enough tokens then remove them and return + if (m_tokenCount - amount >= 0) + { + if (m_parent == null || m_parent.RemoveTokens(amount)) + { + m_tokenCount -= amount; + return true; + } + } + + return false; } /// - /// Remove a given number of tokens from the bucket + /// Deposit tokens into the bucket from a child bucket that did + /// not use all of its available tokens /// - /// Number of tokens to remove from the bucket - /// True if tokens were added to the bucket - /// during this call, otherwise false - /// True if the requested number of tokens were removed from - /// the bucket, otherwise false - public bool RemoveTokens(int amount, out bool dripSucceeded) + private void Deposit(Int64 count) { - if (maxBurst == 0) - { - dripSucceeded = true; - return true; - } + m_tokenCount += count; - dripSucceeded = Drip(); - - if (content - amount >= 0) - { - if (parent != null && !parent.RemoveTokens(amount)) - return false; - - content -= amount; - return true; - } - else - { - return false; - } + // Deposit the overflow in the parent bucket, this is how we share + // unused bandwidth + Int64 burstrate = BurstRate; + if (m_tokenCount > burstrate) + m_tokenCount = burstrate; } /// @@ -186,37 +286,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// call to Drip /// /// True if tokens were added to the bucket, otherwise false - public bool Drip() + private void Drip() { - if (tokensPerMS == 0) + // This should never happen... means we are a leaf node and were created + // with no drip rate... + if (DripRate == 0) { - content = maxBurst; - return true; + m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0"); + return; } - else - { - int now = Environment.TickCount & Int32.MaxValue; - int deltaMS = now - lastDrip; + + // Determine the interval over which we are adding tokens, never add + // more than a single quantum of tokens + Int32 now = Environment.TickCount & Int32.MaxValue; + Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum); - if (deltaMS <= 0) - { - if (deltaMS < 0) - lastDrip = now; - return false; - } + m_lastDrip = now; - int dripAmount = deltaMS * tokensPerMS; + // This can be 0 in the very unusual case that the timer wrapped + // It can be 0 if we try add tokens at a sub-tick rate + if (deltaMS <= 0) + return; - content = Math.Min(content + dripAmount, maxBurst); - lastDrip = now; - - if (dripAmount < 0 || content < 0) - // sim has been idle for too long, integer has overflown - // previous calculation is meaningless, let's put it at correct max - content = maxBurst; - - return true; - } + Deposit(deltaMS * DripRate / m_ticksPerQuantum); } } } From e9c2beadecf8f531d2bbfcd3f44636cfeff08667 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 11 Apr 2011 20:43:11 +0100 Subject: [PATCH 017/103] Add more instructions to OpenSim.ini.example to try and make it more understandable for new users. --- bin/OpenSim.ini.example | 103 ++++++++++++++++++++++++++-------------- 1 file changed, 68 insertions(+), 35 deletions(-) diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 5bac56e66a..390fe099d6 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -1,4 +1,22 @@ -;; A note on the format of this file +;; This is the main configuration file for OpenSimulator. If it's named OpenSim.ini +;; then it will be loaded by OpenSimulator. If it's named OpenSim.ini.example then +;; you will need to copy it to OpenSim.ini first (if that file does not already exist) +;; +;; If you are copying, then once you have copied OpenSim.ini.example to OpenSim.ini you will +;; need to pick an architecture in the [Architecture] section at the end of this file. +;; +;; The settings in this file are in the form " = ". For example, save_crashes = false +;; in the [Startup] section below. +;; +;; All settings are initially commented out and the default value used, as found in +;; OpenSimDefaults.ini. To change a setting, first uncomment it by deleting the initial semicolon (;) +;; and then change the value. This will override the value in OpenSimDefaults.ini +;; +;; If you want to find out what configuration OpenSimulator has used then type "config get" on the +;; region console command line. +;; +;; +;; NOTES FOR DEVELOPERS REGARDING FORMAT OF TIHS FILE ;; ;; All leading white space is ignored, but preserved. ;; @@ -8,15 +26,14 @@ ;; formatted as: ;; {option} {depends on} {question to ask} {choices} default value ;; Any text comments following the declaration, up to the next blank line. -;; will be copied to the generated file. -;; A * in the choices list will allow an empty entry.\ +;; will be copied to the generated file (NOTE: generation is not yet implemented) +;; A * in the choices list will allow an empty entry. ;; An empty question will set the default if the dependencies are ;; satisfied. ;; -;; ; denotes a commented out option. Uncomment it to actvate it -;; and change it to the desired value -;; Any options added to OpenSim.ini.exmaple must be commented out, -;; and their value must represent the default. +;; ; denotes a commented out option. +;; Any options added to OpenSim.ini.example should be initially commented out. + [Startup] ;# {save_crashes} {} {Save crashes to disk?} {true false} false @@ -35,7 +52,7 @@ ;; Determine where OpenSimulator looks for the files which tell it ;; which regions to server - ;; Defaults to "filesystem" if this setting isn't present + ;; Default is "filesystem" ; region_info_source = "filesystem" ; region_info_source = "web" @@ -131,6 +148,7 @@ ;; ZeroMesher is faster but leaves the physics engine to model the mesh ;; using the basic shapes that it supports. ;; Usually this is only a box. + ;; Default is Meshmerizer ; meshing = Meshmerizer ; meshing = ZeroMesher @@ -138,6 +156,7 @@ ;; OpenDynamicsEngine is by some distance the most developed physics engine ;; basicphysics effectively does not model physics at all, making all ;; objects phantom + ;; Default is OpenDynamicsEngine ; physics = OpenDynamicsEngine ; physics = basicphysics ; physics = POS @@ -154,7 +173,6 @@ ;; permission checks (allowing anybody to copy ;; any item, etc. This may not yet be implemented uniformally. ;; If set to true, then all permissions checks are carried out - ;; Default is false ; serverside_object_permissions = false ;; This allows users with a UserLevel of 200 or more to assume god @@ -188,6 +206,7 @@ ;; server to send mail through. ; emailmodule = DefaultEmailModule + [SMTP] ;; The SMTP server enabled the email module to send email to external ;; destinations. @@ -214,6 +233,7 @@ ;# {SMTP_SERVER_PASSWORD} {[Startup]emailmodule:DefaultEmailModule enabled:true} {SMTP server password} {} ; SMTP_SERVER_PASSWORD = "" + [Network] ;; Configure the remote console user here. This will not actually be used ;; unless you use -console=rest at startup. @@ -247,6 +267,7 @@ ;; " (Mozilla Compatible)" to the text where there are problems with a web server ; user_agent = "OpenSim LSL (Mozilla Compatible)" + [ClientStack.LindenUDP] ;; See OpensSimDefaults.ini for the throttle options. You can copy the ;; relevant sections and override them here. @@ -263,17 +284,18 @@ ;; building's lights to possibly not be rendered. ; DisableFacelights = "false" + [Chat] ;# {whisper_distance} {} {Distance at which a whisper is heard, in meters?} {} 10 - ;; Distance in meters that whispers should travel. Default is 10m + ;; Distance in meters that whispers should travel. ; whisper_distance = 10 ;# {say_distance} {} {Distance at which normal chat is heard, in meters? (SL uses 20 here)} {} 30 - ;; Distance in meters that ordinary chat should travel. Default is 30m + ;; Distance in meters that ordinary chat should travel. ; say_distance = 30 ;# {shout_distance} {Distance at which a shout is heard, in meters?} {} 100 - ;; Distance in meters that shouts should travel. Default is 100m + ;; Distance in meters that shouts should travel. ; shout_distance = 100 @@ -337,13 +359,13 @@ ;# {create_region_enable_voice} {enabled:true} {Enable voice for newly created regions?} {true false} false ;; set this variable to true if you want the create_region XmlRpc ;; call to unconditionally enable voice on all parcels for a newly - ;; created region [default: false] + ;; created region ; create_region_enable_voice = false ;# {create_region_public} {enabled:true} {Make newly created regions public?} {true false} false ;; set this variable to false if you want the create_region XmlRpc ;; call to create all regions as private per default (can be - ;; overridden in the XmlRpc call) [default: true] + ;; overridden in the XmlRpc call) ; create_region_public = false ;# {enabled_methods} {enabled:true} {List of methods to allow, separated by |} {} all @@ -372,15 +394,16 @@ ;; default avatars ; default_appearance = default_appearance.xml + [Wind] ;# {enabled} {} {Enable wind module?} {true false} true - ;; Enables the wind module. Default is true - ;enabled = true + ;; Enables the wind module. + ; enabled = true ;# {wind_update_rate} {enabled:true} {Wind update rate in frames?} {} 150 ;; How often should wind be updated, as a function of world frames. ;; Approximately 50 frames a second - wind_update_rate = 150 + ; wind_update_rate = 150 ;; The Default Wind Plugin to load ; wind_plugin = SimpleRandomWind @@ -396,9 +419,10 @@ ;# {strength} {enabled:true wind_plugin:SimpleRandomWind} {Wind strength?} {} 1.0 ;; This setting is specific to the SimpleRandomWind plugin - ;; Adjusts wind strength. 0.0 = no wind, 1.0 = normal wind. Default is 1.0 + ;; Adjusts wind strength. 0.0 = no wind, 1.0 = normal wind. ; strength = 1.0 + [LightShare] ;# {enable_windlight} {} {Enable LightShare technology?} {true false} false ;; This enables the transmission of Windlight scenes to supporting clients, @@ -406,7 +430,8 @@ ;; It has no ill effect on viewers which do not support server-side ;; windlight settings. ;; Currently we only have support for MySQL databases. - ; enable_windlight = false; + ; enable_windlight = false + [DataSnapshot] ;# {index_sims} {} {Enable data snapshotting (search)?} {true false} false @@ -417,7 +442,6 @@ ;; and you can ignore the rest of these search-related configs. ; index_sims = false - ;# {data_exposure} {index_sims:true} {How much data should be exposed?} {minimum all} minimum ;; The variable data_exposure controls what the regions expose: ;; minimum: exposes only things explicitly marked for search @@ -462,6 +486,7 @@ ;; Money Unit fee to create groups ; PriceGroupCreate = 0 + [XEngine] ;# {Enabled} {} {Enable the XEngine scripting engine?} {true false} true ;; Enable this engine in this OpenSim instance @@ -556,9 +581,9 @@ ;; Default is ./bin/ScriptEngines ; ScriptEnginesPath = "ScriptEngines" + [MRM] ;; Enables the Mini Region Modules Script Engine. - ;; default is false ; Enabled = false ;; Runs MRM in a Security Sandbox @@ -580,6 +605,7 @@ ;; May represent a security risk if you disable this. ; OwnerOnly = true + [FreeSwitchVoice] ;; In order for this to work you need a functioning FreeSWITCH PBX set up. ;; Configuration details at http://opensimulator.org/wiki/Freeswitch_Module @@ -593,6 +619,7 @@ ;; If using a remote module, specify the server URL ; FreeswitchServiceURL = http://my.grid.server:8003/fsapi + [FreeswitchService] ;; !!!!!!!!!!!!!!!!!!!!!!!!!!! ;; !!!!!!STANDALONE ONLY!!!!!! @@ -611,6 +638,7 @@ ; UserName = "freeswitch" ; Password = "password" + [Groups] ;# {Enabled} {} {Enable groups?} {true false} false ;; Enables the groups module @@ -634,7 +662,7 @@ ;# {ServicesConnectorModule} {Module:GroupsModule} {Service connector to use for groups} {XmlRpcGroupsServicesConnector SimianGroupsServicesConnector} XmlRpcGroupsServicesConnector ;; Service connectors to the Groups Service as used in the GroupsModule. Select one depending on ;; whether you're using a Flotsam XmlRpc backend or a SimianGrid backend - ; ServicesConnectorModule = SimianGroupsServicesConnector + ; ServicesConnectorModule = XmlRpcGroupsServicesConnector ;# {GroupsServerURI} {Module:GroupsModule} {Groups Server URI} {} ;; URI for the groups services @@ -654,6 +682,7 @@ ; XmlRpcServiceReadKey = 1234 ; XmlRpcServiceWriteKey = 1234 + [InterestManagement] ;# {UpdatePrioritizationScheme} {} {Update prioritization scheme?} {BestAvatarResponsiveness Time Distance SimpleAngularDistance FrontBack} BestAvatarResponsiveness ;; This section controls how state updates are prioritized for each client @@ -661,24 +690,28 @@ ;; SimpleAngularDistance, FrontBack ; UpdatePrioritizationScheme = BestAvatarResponsiveness + [MediaOnAPrim] ;# {Enabled} {} {Enable Media-on-a-Prim (MOAP)} {true false} true ;; Enable media on a prim facilities ; Enabled = true; + [Architecture] ;# {Include-Architecture} {} {Choose one of the following architectures} {config-include/Standalone.ini config-include/StandaloneHypergrid.ini config-include/Grid.ini config-include/GridHypergrid.ini config-include/SimianGrid.ini config-include/HyperSimianGrid.ini} config-include/Standalone.ini - ;; Choose one of these architecture includes: - ;; Include-Architecture = "config-include/Standalone.ini" - ;; Include-Architecture = "config-include/StandaloneHypergrid.ini" - ;; Include-Architecture = "config-include/Grid.ini" - ;; Include-Architecture = "config-include/GridHypergrid.ini" - ;; Include-Architecture = "config-include/SimianGrid.ini" - ;; Include-Architecture = "config-include/HyperSimianGrid.ini" + ;; Uncomment one of the following includes as required. For instance, to create a standalone OpenSim, + ;; uncomment Include-Architecture = "config-include/Standalone.ini" + ;; + ;; Then you will need to copy and edit the corresponding *Common.example file in config-include/ + ;; that the referenced .ini file goes on to include. + ;; + ;; For instance, if you chose "config-include/Standalone.ini" then you will need to copy + ;; "config-include/StandaloneCommon.ini.example" to "config-include/StandaloneCommon.ini" before + ;; editing it to set the database and backend services that OpenSim will use. + ;; ; Include-Architecture = "config-include/Standalone.ini" - - ;; Then choose - ;; config-include/StandaloneCommon.ini.example (if you're in standlone) OR - ;; config-include/GridCommon.ini.example (if you're connected to a grid) - ;; Copy to your own .ini there (without .example extension) and edit it - ;; to customize your data + ; Include-Architecture = "config-include/StandaloneHypergrid.ini" + ; Include-Architecture = "config-include/Grid.ini" + ; Include-Architecture = "config-include/GridHypergrid.ini" + ; Include-Architecture = "config-include/SimianGrid.ini" + ; Include-Architecture = "config-include/HyperSimianGrid.ini" From fda393b088e625b5811520af6bd502e2471cd8e7 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 11 Apr 2011 21:42:28 +0100 Subject: [PATCH 018/103] Add information comment to top of OpenSimDefaults.ini and make file consistent --- bin/OpenSimDefaults.ini | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index 96ffb7ea6b..d6eee0e101 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -1,3 +1,7 @@ +; This file contains defaults for various settings in OpenSimulator. These can be overriden +; by changing the same setting in OpenSim.ini (once OpenSim.ini.example has been copied to OpenSim.ini). + + [Startup] ; Set this to true if you want to log crashes to disk ; this can be useful when submitting bug reports. @@ -287,6 +291,7 @@ ;SMTP_SERVER_LOGIN=foo ;SMTP_SERVER_PASSWORD=bar + [Network] ConsoleUser = "Test" ConsolePass = "secret" @@ -317,6 +322,7 @@ ; " (Mozilla Compatible)" to the text where there are problems with a web server ;user_agent = "OpenSim LSL (Mozilla Compatible)" + [XMLRPC] ; ## ; ## Scripting XMLRPC mapper @@ -330,6 +336,7 @@ ;XmlRpcRouterModule = "XmlRpcRouterModule" ;XmlRpcPort = 20800 + [ClientStack.LindenUDP] ; Set this to true to process incoming packets asynchronously. Networking is ; already separated from packet handling with a queue, so this will only @@ -422,6 +429,7 @@ ; ;DisableFacelights = "false" + [Chat] ; Controls whether the chat module is enabled. Default is true. enabled = true; @@ -680,6 +688,7 @@ ; path to default appearance XML file that specifies the look of the default avatars ;default_appearance = default_appearance.xml + [RestPlugins] ; Change this to true to enable REST Plugins. This must be true if you wish to use ; REST Region or REST Asset and Inventory Plugins @@ -706,11 +715,10 @@ flush-on-error = true -; Uncomment the following for IRC bridge -; experimental, so if it breaks... keep both parts... yada yada +; IRC bridge is experimental, so if it breaks... keep both parts... yada yada ; also, not good error detection when it fails -;[IRC] - ;enabled = true ; you need to set this otherwise it won't connect +[IRC] + enabled = false; you need to set this to true otherwise it won't connect ;server = name.of.irc.server.on.the.net ;; user password - only use this if the server requires one ;password = mypass @@ -767,14 +775,14 @@ ;exclude_list=User 1,User 2,User 3 -;[CMS] - ;enabled = true +[CMS] + enabled = false ;channel = 345 -; Uncomment the following to control the progression of daytime -; in the Sim. The defaults are what is shown below -;[Sun] +; The following settings control the progression of daytime +; in the Sim. The defaults are the same as the commented out settings +[Sun] ; number of wall clock hours for an opensim day. 24.0 would mean realtime ;day_length = 4 ; Year length in days @@ -821,12 +829,13 @@ ; default is 1000 cloud_update_rate = 1000 -[LightShare] +[LightShare] ; This enables the transmission of Windlight scenes to supporting clients, such as the Meta7 viewer. ; It has no ill effect on viewers which do not support server-side windlight settings. ; Currently we only have support for MySQL databases. - enable_windlight = false; + enable_windlight = false + [Trees] ; Enable this to allow the tree module to manage your sim trees, including growing, reproducing and dying @@ -838,7 +847,6 @@ [VectorRender] - ; the font to use for rendering text (default: Arial) ; font_name = "Arial" @@ -1032,6 +1040,7 @@ ;; Path to script assemblies ; ScriptEnginesPath = "ScriptEngines" + [OpenGridProtocol] ;These are the settings for the Open Grid Protocol.. the Agent Domain, Region Domain, you know.. ;On/true or Off/false @@ -1240,11 +1249,11 @@ ChildReprioritizationDistance = 20.0 -[WebStats] ; View region statistics via a web page ; See http://opensimulator.org/wiki/FAQ#Region_Statistics_on_a_Web_Page ; Use a web browser and type in the "Login URI" + "/SStats/" ; For example- http://127.0.0.1:9000/SStats/ +[WebStats] ; enabled=false From d6948b15c47fd067be6d7cef31caf8e49e9c9afa Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 11 Apr 2011 21:51:17 +0100 Subject: [PATCH 019/103] Make it more obvious when it happens that DLL plugin loading fails. Improve exception output on Windows. --- OpenSim/Region/Framework/ModuleLoader.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/ModuleLoader.cs b/OpenSim/Region/Framework/ModuleLoader.cs index 23be9c252c..14ecd4430a 100644 --- a/OpenSim/Region/Framework/ModuleLoader.cs +++ b/OpenSim/Region/Framework/ModuleLoader.cs @@ -223,7 +223,8 @@ namespace OpenSim.Region.Framework catch (Exception e) { m_log.ErrorFormat( - "[MODULES]: Could not load types for [{0}]. Exception {1}", pluginAssembly.FullName, e); + "[MODULES]: Could not load types for plugin DLL {0}. Exception {1} {2}", + pluginAssembly.FullName, e.Message, e.StackTrace); // justincc: Right now this is fatal to really get the user's attention throw e; From 0bd6bc8fba1ed35344218e2aa25e30a3294f2ed1 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 11 Apr 2011 21:59:26 +0100 Subject: [PATCH 020/103] create "config show" as a region console command synonym for "config get". This is to create greater consistency with all the other show commands. --- OpenSim/Region/Application/OpenSim.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index ec1fb042c1..39004d4028 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -341,10 +341,15 @@ namespace OpenSim m_console.Commands.AddCommand("region", false, "config get", "config get [
] []", - "Show a config option", + "Synonym for config show", + HandleConfig); + + m_console.Commands.AddCommand("region", false, "config show", + "config show [
] []", + "Show config information", "If neither section nor field are specified, then the whole current configuration is printed." + Environment.NewLine + "If a section is given but not a field, then all fields in that section are printed.", - HandleConfig); + HandleConfig); m_console.Commands.AddCommand("region", false, "config save", "config save ", @@ -593,7 +598,9 @@ namespace OpenSim if (cmdparams.Length > 0) { - switch (cmdparams[0].ToLower()) + string firstParam = cmdparams[0].ToLower(); + + switch (firstParam) { case "set": if (cmdparams.Length < 4) @@ -618,6 +625,7 @@ namespace OpenSim break; case "get": + case "show": if (cmdparams.Length == 1) { foreach (IConfig config in m_config.Source.Configs) @@ -654,8 +662,8 @@ namespace OpenSim } else { - Notice("Syntax: config get [
] []"); - Notice("Example: config get ScriptEngine.DotNetEngine NumberOfScriptThreads"); + Notice("Syntax: config {0} [
] []", firstParam); + Notice("Example: config {0} ScriptEngine.DotNetEngine NumberOfScriptThreads", firstParam); } break; From 333a2913cfb8b536939b1d9d4b3ac6ea59dd55ca Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 11 Apr 2011 22:02:12 +0100 Subject: [PATCH 021/103] slightly tweak OpenSim.ini.example text --- bin/OpenSim.ini.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 390fe099d6..55723d1efb 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -12,8 +12,8 @@ ;; OpenSimDefaults.ini. To change a setting, first uncomment it by deleting the initial semicolon (;) ;; and then change the value. This will override the value in OpenSimDefaults.ini ;; -;; If you want to find out what configuration OpenSimulator has used then type "config get" on the -;; region console command line. +;; If you want to find out what configuration OpenSimulator has finished with once all the configuration +;; files are loaded then type "config show" on the region console command line. ;; ;; ;; NOTES FOR DEVELOPERS REGARDING FORMAT OF TIHS FILE From 464fa45ec90767af13bd8edcc7c67de4ec3db6a7 Mon Sep 17 00:00:00 2001 From: "E. Allen Soard" Date: Fri, 8 Apr 2011 18:30:20 -0700 Subject: [PATCH 022/103] Implimented HTTP_VERIFY_CERT for llHttpRequest --- .../HttpRequest/ScriptsHttpRequests.cs | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs index d78931a524..a37c781e87 100644 --- a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs +++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs @@ -29,8 +29,10 @@ using System; using System.Collections.Generic; using System.IO; using System.Net; +using System.Net.Security; using System.Text; using System.Threading; +using System.Security.Cryptography.X509Certificates; using Nini.Config; using OpenMetaverse; using OpenSim.Framework; @@ -84,6 +86,7 @@ using OpenSim.Region.Framework.Scenes; namespace OpenSim.Region.CoreModules.Scripting.HttpRequest { + public class HttpRequestModule : IRegionModule, IHttpRequestModule { private object HttpListLock = new object(); @@ -100,8 +103,23 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest public HttpRequestModule() { + ServicePointManager.ServerCertificateValidationCallback +=ValidateServerCertificate; } + public static bool ValidateServerCertificate( + object sender, + X509Certificate certificate, + X509Chain chain, + SslPolicyErrors sslPolicyErrors) + { + HttpWebRequest Request = (HttpWebRequest)sender; + + if(Request.Headers.Get("NoVerifyCert") != null) + { + return true; + } + return chain.Build(new X509Certificate2(certificate)); + } #region IHttpRequestModule Members public UUID MakeHttpRequest(string url, string parameters, string body) @@ -141,8 +159,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest break; case (int)HttpRequestConstants.HTTP_VERIFY_CERT: - - // TODO implement me + htc.HttpVerifyCert = (int.Parse(parms[i + 1]) != 0); break; } } @@ -282,7 +299,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest public string HttpMethod = "GET"; public string HttpMIMEType = "text/plain;charset=utf-8"; public int HttpTimeout; - // public bool HttpVerifyCert = true; // not implemented + public bool HttpVerifyCert = true; // not implemented private Thread httpThread; // Request info @@ -344,6 +361,17 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest Request.Method = HttpMethod; Request.ContentType = HttpMIMEType; + if(!HttpVerifyCert) + { + // Connection Group Name is probably not used so we hijack it to identify + // a desired security exception +// Request.ConnectionGroupName="NoVerify"; + Request.Headers.Add("NoVerifyCert" , "true"); + } +// else +// { +// Request.ConnectionGroupName="Verify"; +// } if (proxyurl != null && proxyurl.Length > 0) { if (proxyexcepts != null && proxyexcepts.Length > 0) From 3a98fb080a751d21999bb2baa769462426b5d776 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 11 Apr 2011 22:29:08 +0100 Subject: [PATCH 023/103] minor: adjust some spacing and indentation --- .../HttpRequest/ScriptsHttpRequests.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs index a37c781e87..23e15ef9ee 100644 --- a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs +++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs @@ -86,7 +86,6 @@ using OpenSim.Region.Framework.Scenes; namespace OpenSim.Region.CoreModules.Scripting.HttpRequest { - public class HttpRequestModule : IRegionModule, IHttpRequestModule { private object HttpListLock = new object(); @@ -114,10 +113,11 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest { HttpWebRequest Request = (HttpWebRequest)sender; - if(Request.Headers.Get("NoVerifyCert") != null) + if (Request.Headers.Get("NoVerifyCert") != null) { return true; } + return chain.Build(new X509Certificate2(certificate)); } #region IHttpRequestModule Members @@ -206,7 +206,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest * Not sure how important ordering is is here - the next first * one completed in the list is returned, based soley on its list * position, not the order in which the request was started or - * finsihed. I thought about setting up a queue for this, but + * finished. I thought about setting up a queue for this, but * it will need some refactoring and this works 'enough' right now */ @@ -254,8 +254,8 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest m_scene.RegisterModuleInterface(this); - m_proxyurl = config.Configs["Startup"].GetString("HttpProxy"); - m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions"); + m_proxyurl = config.Configs["Startup"].GetString("HttpProxy"); + m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions"); m_pendingRequests = new Dictionary(); } @@ -363,10 +363,10 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest if(!HttpVerifyCert) { - // Connection Group Name is probably not used so we hijack it to identify - // a desired security exception -// Request.ConnectionGroupName="NoVerify"; - Request.Headers.Add("NoVerifyCert" , "true"); + // We could hijack Connection Group Name to identify + // a desired security exception. But at the moment we'll use a dummy header instead. +// Request.ConnectionGroupName = "NoVerify"; + Request.Headers.Add("NoVerifyCert", "true"); } // else // { @@ -464,4 +464,4 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest } } } -} +} \ No newline at end of file From e8ecb2898c55bb97d47e9b1efb7dac98d40609cd Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 11 Apr 2011 22:33:24 +0100 Subject: [PATCH 024/103] minor: remove some mono compiler warnings --- .../Shared/Api/Implementation/LSL_Api.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index c16a985564..aa28fa0ca2 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -10289,12 +10289,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { UUID rq = UUID.Random(); - UUID tid = AsyncCommands. - DataserverPlugin.RegisterRequest(m_localID, - m_itemID, rq.ToString()); + AsyncCommands.DataserverPlugin.RegisterRequest(m_localID, m_itemID, rq.ToString()); - AsyncCommands. - DataserverPlugin.DataserverReply(rq.ToString(), Name2Username(llKey2Name(id))); + AsyncCommands.DataserverPlugin.DataserverReply(rq.ToString(), Name2Username(llKey2Name(id))); return rq.ToString(); } @@ -10308,12 +10305,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { UUID rq = UUID.Random(); - UUID tid = AsyncCommands. - DataserverPlugin.RegisterRequest(m_localID, - m_itemID, rq.ToString()); + AsyncCommands.DataserverPlugin.RegisterRequest(m_localID, m_itemID, rq.ToString()); - AsyncCommands. - DataserverPlugin.DataserverReply(rq.ToString(), llKey2Name(id)); + AsyncCommands.DataserverPlugin.DataserverReply(rq.ToString(), llKey2Name(id)); return rq.ToString(); } From 64dc7e9f141fd0f168ed90c11f932a8bdb077dc7 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 11 Apr 2011 22:35:07 +0100 Subject: [PATCH 025/103] minor: remove now inaccurate comment --- .../CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs index 23e15ef9ee..4c8424d7cc 100644 --- a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs +++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs @@ -299,7 +299,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest public string HttpMethod = "GET"; public string HttpMIMEType = "text/plain;charset=utf-8"; public int HttpTimeout; - public bool HttpVerifyCert = true; // not implemented + public bool HttpVerifyCert = true; private Thread httpThread; // Request info From 49d80f5711e1bc1afb6c650038619ade6d9a9e97 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 11 Apr 2011 23:07:56 +0100 Subject: [PATCH 026/103] Include code to return more information about the NullReferenceException seen in http://opensimulator.org/mantis/view.php?id=5403 prior to doing something about it. --- OpenSim/Data/MySQL/MySQLGenericTableHandler.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs b/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs index 8efe4e9bd6..50b6dbef42 100644 --- a/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs +++ b/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs @@ -39,6 +39,8 @@ namespace OpenSim.Data.MySQL { public class MySQLGenericTableHandler : MySqlFramework where T: class, new() { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + protected Dictionary m_Fields = new Dictionary(); @@ -217,7 +219,6 @@ namespace OpenSim.Data.MySQL { using (MySqlCommand cmd = new MySqlCommand()) { - string query = ""; List names = new List(); List values = new List(); @@ -226,6 +227,16 @@ namespace OpenSim.Data.MySQL { names.Add(fi.Name); values.Add("?" + fi.Name); + + // Temporarily return more information about what field is unexpectedly null for + // http://opensimulator.org/mantis/view.php?id=5403. This might be due to a bug in the + // InventoryTransferModule or we may be required to substitute a DBNull here. + if (fi.GetValue(row) == null) + throw new NullReferenceException( + string.Format( + "[MYSQL GENERIC TABLE HANDLER]: Trying to store field {0} for {1} which is unexpectedly null", + fi.Name, row)); + cmd.Parameters.AddWithValue(fi.Name, fi.GetValue(row).ToString()); } @@ -268,4 +279,4 @@ namespace OpenSim.Data.MySQL } } } -} +} \ No newline at end of file From 621d5b58e1a9bc24aab384a5e9bd6eb881928505 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 11 Apr 2011 23:56:04 +0100 Subject: [PATCH 027/103] minor: add a bit more method doc to IInventoryService.GetItem() --- OpenSim/Services/Interfaces/IInventoryService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Services/Interfaces/IInventoryService.cs b/OpenSim/Services/Interfaces/IInventoryService.cs index d19faeddf7..a8bfe479a7 100644 --- a/OpenSim/Services/Interfaces/IInventoryService.cs +++ b/OpenSim/Services/Interfaces/IInventoryService.cs @@ -169,7 +169,7 @@ namespace OpenSim.Services.Interfaces /// Get an item, given by its UUID ///
/// - /// + /// null if no item was found, otherwise the found item InventoryItemBase GetItem(InventoryItemBase item); /// From 98d7de22dc44ca1e5971301c02a6a1fe49620889 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 12 Apr 2011 18:31:41 +0100 Subject: [PATCH 028/103] Fix (add) ability to rez objects by dragging them out of another prim's inventory. This should happen if the client supplies a task ID with the RezObject call. The rez goes through the same code as llRezObject(), so the same perms are applied. Rotation isn't yet preserved, this should be fixed shortly. --- .../InventoryAccess/InventoryAccessModule.cs | 4 ++ .../Framework/Scenes/Scene.Inventory.cs | 59 +++++++++++++++++-- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 9fbfc3477f..cdee53c4e2 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -552,8 +552,10 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess /// /// Rez an object into the scene from the user's inventory /// + /// /// FIXME: It would be really nice if inventory access modules didn't also actually do the work of rezzing /// things to the scene. The caller should be doing that, I think. + /// /// /// /// @@ -570,6 +572,8 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment) { +// m_log.DebugFormat("[INVENTORY ACCESS MODULE]: RezObject for {0}, item {1}", remoteClient.Name, itemID); + // Work out position details byte bRayEndIsIntersection = (byte)0; diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 73dd531536..43708500fe 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -1955,11 +1955,60 @@ namespace OpenSim.Region.Framework.Scenes UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, bool RezSelected, bool RemoveItem, UUID fromTaskID) { - IInventoryAccessModule invAccess = RequestModuleInterface(); - if (invAccess != null) - invAccess.RezObject( - remoteClient, itemID, RayEnd, RayStart, RayTargetID, BypassRayCast, RayEndIsIntersection, - RezSelected, RemoveItem, fromTaskID, false); +// m_log.DebugFormat( +// "[PRIM INVENTORY]: RezObject from {0} for item {1} from task id {2}", +// remoteClient.Name, itemID, fromTaskID); + + if (fromTaskID == UUID.Zero) + { + IInventoryAccessModule invAccess = RequestModuleInterface(); + if (invAccess != null) + invAccess.RezObject( + remoteClient, itemID, RayEnd, RayStart, RayTargetID, BypassRayCast, RayEndIsIntersection, + RezSelected, RemoveItem, fromTaskID, false); + } + else + { + SceneObjectPart part = GetSceneObjectPart(fromTaskID); + if (part == null) + { + m_log.ErrorFormat( + "[TASK INVENTORY]: {0} tried to rez item id {1} from object id {2} but there is no such scene object", + remoteClient.Name, itemID, fromTaskID); + + return; + } + + TaskInventoryItem item = part.Inventory.GetInventoryItem(itemID); + if (item == null) + { + m_log.ErrorFormat( + "[TASK INVENTORY]: {0} tried to rez item id {1} from object id {2} but there is no such item", + remoteClient.Name, itemID, fromTaskID); + + return; + } + + // Work out position details + byte bRayEndIsIntersection = (byte)0; + + if (RayEndIsIntersection) + { + bRayEndIsIntersection = (byte)1; + } + else + { + bRayEndIsIntersection = (byte)0; + } + + Vector3 scale = new Vector3(0.5f, 0.5f, 0.5f); + Vector3 pos + = GetNewRezLocation( + RayStart, RayEnd, RayTargetID, Quaternion.Identity, + BypassRayCast, bRayEndIsIntersection, true, scale, false); + + RezObject(part, item, pos, Quaternion.Identity, Vector3.Zero, 0); + } } /// From 095e602c4ac6da315f3f6711e24f034b6c490a02 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 12:36:36 -0700 Subject: [PATCH 029/103] First pass at moving object property requests into a queue similar to the entity update queue. The number of property packets can become significant when selecting/deselecting large numbers of objects. This is experimental code. --- .../Client/MXP/ClientStack/MXPClientView.cs | 4 +- .../VWoHTTP/ClientStack/VWHClientView.cs | 4 +- OpenSim/Framework/IClientAPI.cs | 37 +- .../ClientStack/LindenUDP/LLClientView.cs | 367 +++++++++++------- .../ClientStack/LindenUDP/PriorityQueue.cs | 12 +- .../Examples/SimpleModule/MyNpcCharacter.cs | 12 +- .../Framework/Scenes/SceneObjectGroup.cs | 10 +- .../Framework/Scenes/SceneObjectPart.cs | 10 +- .../Server/IRCClientView.cs | 5 +- .../OptionalModules/World/NPC/NPCAvatar.cs | 12 +- OpenSim/Tests/Common/Mock/TestClient.cs | 11 +- 11 files changed, 269 insertions(+), 215 deletions(-) diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index d1a0440524..a604a2ee63 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -1337,12 +1337,12 @@ namespace OpenSim.Client.MXP.ClientStack // Need to translate to MXP somehow } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { //throw new System.NotImplementedException(); } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { //throw new System.NotImplementedException(); } diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index fc27f0198b..d8cd0acbd9 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -884,12 +884,12 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { throw new System.NotImplementedException(); } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { throw new System.NotImplementedException(); } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 5bf0b7b0a9..c56a756314 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -570,16 +570,30 @@ namespace OpenSim.Framework public float dwell; } - public class EntityUpdate + public class IEntityUpdate { public ISceneEntity Entity; - public PrimUpdateFlags Flags; - public float TimeDilation; + public uint Flags; - public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + public IEntityUpdate(ISceneEntity entity, uint flags) { Entity = entity; Flags = flags; + } + } + + + public class EntityUpdate : IEntityUpdate + { + // public ISceneEntity Entity; + // public PrimUpdateFlags Flags; + public float TimeDilation; + + public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + : base(entity,(uint)flags) + { + //Entity = entity; + // Flags = flags; TimeDilation = timedilation; } } @@ -1211,20 +1225,9 @@ namespace OpenSim.Framework /// void SendSimStats(SimStats stats); - void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, - uint Category, - UUID LastOwnerID, string ObjectName, string Description); + void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags); - void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, - UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, - string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, - uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice); + void SendObjectPropertiesReply(ISceneEntity Entity); void SendAgentOffline(UUID[] agentIDs); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 934a2d5c84..b96343e672 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -386,6 +386,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_cachedTextureSerial; private PriorityQueue m_entityUpdates; + private PriorityQueue m_entityProps; private Prioritizer m_prioritizer; private bool m_disableFacelights = false; @@ -433,9 +434,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected IAssetService m_assetService; private const bool m_checkPackets = true; - private Timer m_propertiesPacketTimer; - private List m_propertiesBlocks = new List(); - #endregion Class Members #region Properties @@ -511,6 +509,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene = scene; m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); + m_entityProps = new PriorityQueue(m_scene.Entities.Count); m_fullUpdateDataBlocksBuilder = new List(); m_killRecord = new HashSet(); // m_attachmentsSent = new HashSet(); @@ -534,9 +533,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_udpClient.OnQueueEmpty += HandleQueueEmpty; m_udpClient.OnPacketStats += PopulateStats; - m_propertiesPacketTimer = new Timer(100); - m_propertiesPacketTimer.Elapsed += ProcessObjectPropertiesPacket; - m_prioritizer = new Prioritizer(m_scene); RegisterLocalPacketHandlers(); @@ -3636,9 +3632,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); } - private Int32 m_LastQueueFill = 0; - private uint m_maxUpdates = 0; - private void ProcessEntityUpdates(int maxUpdates) { OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); @@ -3646,46 +3639,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.Lazy> terseUpdateBlocks = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy>(); + // Check to see if this is a flush if (maxUpdates <= 0) { - m_maxUpdates = Int32.MaxValue; + maxUpdates = Int32.MaxValue; } - else - { - if (m_maxUpdates == 0 || m_LastQueueFill == 0) - { - m_maxUpdates = (uint)maxUpdates; - } - else - { - if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) - m_maxUpdates += 5; - else - m_maxUpdates = m_maxUpdates >> 1; - } - m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); - } - m_LastQueueFill = Util.EnvironmentTickCount(); - + int updatesThisCall = 0; -// -// DEBUGGING CODE... REMOVE -// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates); -// // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race // condition where a kill can be processed before an out-of-date update for the same object. lock (m_killRecord) { float avgTimeDilation = 1.0f; - EntityUpdate update; + IEntityUpdate iupdate; Int32 timeinqueue; // this is just debugging code & can be dropped later - while (updatesThisCall < m_maxUpdates) + while (updatesThisCall < maxUpdates) { lock (m_entityUpdates.SyncRoot) - if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) + if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue)) break; + + EntityUpdate update = (EntityUpdate)iupdate; + avgTimeDilation += update.TimeDilation; avgTimeDilation *= 0.5f; @@ -3725,7 +3702,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region UpdateFlags to packet type conversion - PrimUpdateFlags updateFlags = update.Flags; + PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags; bool canUseCompressed = true; bool canUseImproved = true; @@ -3804,6 +3781,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Block Construction } + #region Packet Sending @@ -3904,12 +3882,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Primitive Packet/Data Sending Methods + // These are used to implement an adaptive backoff in the number + // of updates converted to packets. Since we don't want packets + // to sit in the queue with old data, only convert enough updates + // to packets that can be sent in 200ms. + private Int32 m_LastQueueFill = 0; + private Int32 m_maxUpdates = 0; + void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) { if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) { + if (m_maxUpdates == 0 || m_LastQueueFill == 0) + { + m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; + } + else + { + if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) + m_maxUpdates += 5; + else + m_maxUpdates = m_maxUpdates >> 1; + } + m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); + m_LastQueueFill = Util.EnvironmentTickCount(); + if (m_entityUpdates.Count > 0) - ProcessEntityUpdates(m_udpServer.PrimUpdatesPerCallback); + ProcessEntityUpdates(m_maxUpdates); + + if (m_entityProps.Count > 0) + ProcessEntityPropertyRequests(m_maxUpdates); } if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) @@ -4023,47 +4025,147 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) +/// ----------------------------------------------------------------- +/// +/// ----------------------------------------------------------------- + + private class ObjectPropertyUpdate : IEntityUpdate { - ObjectPropertiesFamilyPacket objPropFamilyPack = (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); - // TODO: don't create new blocks if recycling an old packet - - ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); - objPropDB.RequestFlags = RequestFlags; - objPropDB.ObjectID = ObjectUUID; - if (OwnerID == GroupID) - objPropDB.OwnerID = UUID.Zero; - else - objPropDB.OwnerID = OwnerID; - objPropDB.GroupID = GroupID; - objPropDB.BaseMask = BaseMask; - objPropDB.OwnerMask = OwnerMask; - objPropDB.GroupMask = GroupMask; - objPropDB.EveryoneMask = EveryoneMask; - objPropDB.NextOwnerMask = NextOwnerMask; - - // TODO: More properties are needed in SceneObjectPart! - objPropDB.OwnershipCost = OwnershipCost; - objPropDB.SaleType = SaleType; - objPropDB.SalePrice = SalePrice; - objPropDB.Category = Category; - objPropDB.LastOwnerID = LastOwnerID; - objPropDB.Name = Util.StringToBytes256(ObjectName); - objPropDB.Description = Util.StringToBytes256(Description); - objPropFamilyPack.ObjectData = objPropDB; - objPropFamilyPack.Header.Zerocoded = true; - OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task); + internal bool SendFamilyProps; + + public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam) + : base(entity,flags) + { + SendFamilyProps = sendfam; + } + } + + public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) + { + uint priority = m_prioritizer.GetUpdatePriority(this, entity); + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true)); } - public void SendObjectPropertiesReply( - UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) + { + uint priority = m_prioritizer.GetUpdatePriority(this, entity); + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false)); + } + + private void ProcessEntityPropertyRequests(int maxUpdates) + { + OpenSim.Framework.Lazy> objectFamilyBlocks = + new OpenSim.Framework.Lazy>(); + + OpenSim.Framework.Lazy> objectPropertiesBlocks = + new OpenSim.Framework.Lazy>(); + + IEntityUpdate iupdate; + Int32 timeinqueue; // this is just debugging code & can be dropped later + + int updatesThisCall = 0; + while (updatesThisCall < m_maxUpdates) + { + lock (m_entityProps.SyncRoot) + if (!m_entityProps.TryDequeue(out iupdate, out timeinqueue)) + break; + + ObjectPropertyUpdate update = (ObjectPropertyUpdate)iupdate; + if (update.SendFamilyProps) + { + if (update.Entity is SceneObjectPart) + { + SceneObjectPart sop = (SceneObjectPart)update.Entity; + ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); + objectFamilyBlocks.Value.Add(objPropDB); + } + } + else + { + if (update.Entity is SceneObjectPart) + { + SceneObjectPart sop = (SceneObjectPart)update.Entity; + ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); + objectPropertiesBlocks.Value.Add(objPropDB); + } + } + } + + + if (objectPropertiesBlocks.IsValueCreated) + { + List blocks = objectPropertiesBlocks.Value; + + ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); + packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; + for (int i = 0; i < blocks.Count; i++) + packet.ObjectData[i] = blocks[i]; + + packet.Header.Zerocoded = true; + OutPacket(packet, ThrottleOutPacketType.Task, true); + } + + + if (objectFamilyBlocks.IsValueCreated) + { + List blocks = objectFamilyBlocks.Value; + + // ObjectPropertiesFamilyPacket objPropFamilyPack = + // (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); + // + // objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count]; + // for (int i = 0; i < blocks.Count; i++) + // objPropFamilyPack.ObjectData[i] = blocks[i]; + // + // OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true); + + // one packet per object block... uggh... + for (int i = 0; i < blocks.Count; i++) + { + ObjectPropertiesFamilyPacket packet = + (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); + + packet.ObjectData = blocks[i]; + packet.Header.Zerocoded = true; + OutPacket(packet, ThrottleOutPacketType.Task); + } + + } + + } + + private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) + { + ObjectPropertiesFamilyPacket.ObjectDataBlock block = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); + + block.RequestFlags = requestFlags; + block.ObjectID = sop.UUID; + if (sop.OwnerID == sop.GroupID) + block.OwnerID = UUID.Zero; + else + block.OwnerID = sop.OwnerID; + block.GroupID = sop.GroupID; + block.BaseMask = sop.BaseMask; + block.OwnerMask = sop.OwnerMask; + block.GroupMask = sop.GroupMask; + block.EveryoneMask = sop.EveryoneMask; + block.NextOwnerMask = sop.NextOwnerMask; + + // TODO: More properties are needed in SceneObjectPart! + block.OwnershipCost = sop.OwnershipCost; + block.SaleType = sop.ObjectSaleType; + block.SalePrice = sop.SalePrice; + block.Category = sop.Category; + block.LastOwnerID = sop.CreatorID; // copied from old SOG call... is this right? + block.Name = Util.StringToBytes256(sop.Name); + block.Description = Util.StringToBytes256(sop.Description); + + return block; + } + + private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop) { //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); // TODO: don't create new blocks if recycling an old packet @@ -4071,85 +4173,43 @@ namespace OpenSim.Region.ClientStack.LindenUDP ObjectPropertiesPacket.ObjectDataBlock block = new ObjectPropertiesPacket.ObjectDataBlock(); - block.ItemID = ItemID; - block.CreationDate = CreationDate; - block.CreatorID = CreatorUUID; - block.FolderID = FolderUUID; - block.FromTaskID = FromTaskUUID; - block.GroupID = GroupUUID; - block.InventorySerial = InventorySerial; + block.ObjectID = sop.UUID; + block.Name = Util.StringToBytes256(sop.Name); + block.Description = Util.StringToBytes256(sop.Description); - block.LastOwnerID = LastOwnerUUID; - // proper.ObjectData[0].LastOwnerID = UUID.Zero; - - block.ObjectID = ObjectUUID; - if (OwnerUUID == GroupUUID) + block.CreationDate = (ulong)sop.CreationDate * 1000000; // viewer wants date in microseconds + block.CreatorID = sop.CreatorID; + block.GroupID = sop.GroupID; + block.LastOwnerID = sop.LastOwnerID; + if (sop.OwnerID == sop.GroupID) block.OwnerID = UUID.Zero; else - block.OwnerID = OwnerUUID; - block.TouchName = Util.StringToBytes256(TouchTitle); - block.TextureID = TextureID; - block.SitName = Util.StringToBytes256(SitTitle); - block.Name = Util.StringToBytes256(ItemName); - block.Description = Util.StringToBytes256(ItemDescription); - block.OwnerMask = OwnerMask; - block.NextOwnerMask = NextOwnerMask; - block.GroupMask = GroupMask; - block.EveryoneMask = EveryoneMask; - block.BaseMask = BaseMask; - // proper.ObjectData[0].AggregatePerms = 53; - // proper.ObjectData[0].AggregatePermTextures = 0; - // proper.ObjectData[0].AggregatePermTexturesOwner = 0; - block.SaleType = saleType; - block.SalePrice = salePrice; + block.OwnerID = sop.OwnerID; - lock (m_propertiesPacketTimer) - { - m_propertiesBlocks.Add(block); + block.ItemID = sop.FromUserInventoryItemID; + block.FolderID = UUID.Zero; // sop.FromFolderID ?? + block.FromTaskID = UUID.Zero; // ??? + block.InventorySerial = (short)sop.InventorySerial; + + SceneObjectPart root = sop.ParentGroup.RootPart; - int length = 0; - foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) - { - length += b.Length; - } - if (length > 1100) // FIXME: use real MTU - { - ProcessObjectPropertiesPacket(null, null); - m_propertiesPacketTimer.Stop(); - return; - } + block.TouchName = Util.StringToBytes256(root.TouchName); + block.TextureID = new byte[0]; // TextureID ??? + block.SitName = Util.StringToBytes256(root.SitName); + block.OwnerMask = root.OwnerMask; + block.NextOwnerMask = root.NextOwnerMask; + block.GroupMask = root.GroupMask; + block.EveryoneMask = root.EveryoneMask; + block.BaseMask = root.BaseMask; + block.SaleType = root.ObjectSaleType; + block.SalePrice = root.SalePrice; - m_propertiesPacketTimer.Stop(); - m_propertiesPacketTimer.Start(); - } - - //proper.Header.Zerocoded = true; - //OutPacket(proper, ThrottleOutPacketType.Task); + return block; } - private void ProcessObjectPropertiesPacket(Object sender, ElapsedEventArgs e) - { - ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); - - lock (m_propertiesPacketTimer) - { - m_propertiesPacketTimer.Stop(); - - proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count]; - - int index = 0; - - foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) - { - proper.ObjectData[index++] = b; - } - - m_propertiesBlocks.Clear(); - } - - proper.Header.Zerocoded = true; - OutPacket(proper, ThrottleOutPacketType.Task); - } +/// ----------------------------------------------------------------- +/// +/// ----------------------------------------------------------------- #region Estate Data Sending Methods @@ -4482,6 +4542,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendForceClientSelectObjects(List ObjectIDs) { + m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count); + bool firstCall = true; const int MAX_OBJECTS_PER_PACKET = 251; ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect); @@ -11374,6 +11436,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } + if (throttlePacketType == ThrottleOutPacketType.Task) + { + System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); // get call stack + System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) + + string stack = ""; + for (int count = 1; count < stackFrames.Length; count++) + { + stack += (stack == "" ? "" : ",") + stackFrames[count].GetMethod().Name; + if (count > 5) break; + } + + // m_log.WarnFormat("[BADGUY] {0}", stack); + } + m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 364ce4ba9e..6521a00745 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -78,7 +78,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public bool Enqueue(uint pqueue, EntityUpdate value) + public bool Enqueue(uint pqueue, IEntityUpdate value) { LookupItem lookup; @@ -99,7 +99,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return true; } - internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) + internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue) { for (int i = 0; i < m_numberOfQueues; ++i) { @@ -122,7 +122,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } timeinqueue = 0; - value = default(EntityUpdate); + value = default(IEntityUpdate); return false; } @@ -175,8 +175,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region MinHeapItem private struct MinHeapItem : IComparable { - private EntityUpdate value; - internal EntityUpdate Value { + private IEntityUpdate value; + internal IEntityUpdate Value { get { return this.value; } @@ -212,7 +212,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP this.pqueue = pqueue; } - internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) + internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value) { this.entrytime = Util.EnvironmentTickCount(); this.entryorder = entryorder; diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index d939329814..89e9e204bb 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -702,18 +702,12 @@ namespace OpenSim.Region.Examples.SimpleModule { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { + } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index ca7d9d9d57..30563d4232 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -1765,10 +1765,12 @@ namespace OpenSim.Region.Framework.Scenes /// public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags) { - remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, - RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, - RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, - RootPart.CreatorID, RootPart.Name, RootPart.Description); + remoteClient.SendObjectPropertiesFamilyData(RootPart, RequestFlags); + +// remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, +// RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, +// RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, +// RootPart.CreatorID, RootPart.Name, RootPart.Description); } public void SetPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 4d5eedfc95..3d2eacd720 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2055,15 +2055,7 @@ namespace OpenSim.Region.Framework.Scenes public void GetProperties(IClientAPI client) { - //Viewer wants date in microseconds so multiply it by 1,000,000. - client.SendObjectPropertiesReply( - m_fromUserInventoryItemID, (ulong)_creationDate*(ulong)1e6, _creatorID, UUID.Zero, UUID.Zero, - _groupID, (short)InventorySerial, _lastOwnerID, UUID, _ownerID, - ParentGroup.RootPart.TouchName, new byte[0], ParentGroup.RootPart.SitName, Name, Description, - ParentGroup.RootPart._ownerMask, ParentGroup.RootPart._nextOwnerMask, ParentGroup.RootPart._groupMask, ParentGroup.RootPart._everyoneMask, - ParentGroup.RootPart._baseMask, - ParentGroup.RootPart.ObjectSaleType, - ParentGroup.RootPart.SalePrice); + client.SendObjectPropertiesReply(this); } public UUID GetRootPartUUID() diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 821cd4bd33..4b6e52e757 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -1332,14 +1332,13 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { - } public void SendAgentOffline(UUID[] agentIDs) diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 96760a2721..2504e3019c 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -786,18 +786,12 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { + } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index d1dc17fc9b..dca562674b 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -816,18 +816,11 @@ namespace OpenSim.Tests.Common.Mock { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } From 3ba5eeb6c3b7c6381ca1e0ed87bce44049f96e37 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 12 Apr 2011 22:15:40 +0100 Subject: [PATCH 030/103] Allow a null rotation to be passed in to RezObject so that we can control whether to use the serialized rotation or not. Not used yet. --- .../Framework/Scenes/Scene.Inventory.cs | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 43708500fe..87b4cb8741 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -2016,14 +2016,14 @@ namespace OpenSim.Region.Framework.Scenes /// /// /// - /// - /// - /// + /// The position of the rezzed object. + /// The rotation of the rezzed object. If null, then the rotation stored with the object + /// will be used if it exists. + /// The velocity of the rezzed object. /// /// The SceneObjectGroup rezzed or null if rez was unsuccessful public virtual SceneObjectGroup RezObject( - SceneObjectPart sourcePart, TaskInventoryItem item, - Vector3 pos, Quaternion rot, Vector3 vel, int param) + SceneObjectPart sourcePart, TaskInventoryItem item, Vector3 pos, Quaternion? rot, Vector3 vel, int param) { if (null == item) return null; @@ -2041,8 +2041,14 @@ namespace OpenSim.Region.Framework.Scenes if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0) sourcePart.Inventory.RemoveInventoryItem(item.ItemID); } - - AddNewSceneObject(group, true, pos, rot, vel); + + AddNewSceneObject(group, true); + + group.AbsolutePosition = pos; + group.Velocity = vel; + + if (rot != null) + group.UpdateGroupRotationR((Quaternion)rot); // We can only call this after adding the scene object, since the scene object references the scene // to find out if scripts should be activated at all. From 8e0d2cc43b63046b0dd6b9a3a7dafd70a70362d0 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 12 Apr 2011 22:21:46 +0100 Subject: [PATCH 031/103] If an object is rezzed directly from a prim inventory then give it the rotation it was stored with. --- OpenSim/Region/Framework/Scenes/Scene.Inventory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 87b4cb8741..254879b937 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -2007,7 +2007,7 @@ namespace OpenSim.Region.Framework.Scenes RayStart, RayEnd, RayTargetID, Quaternion.Identity, BypassRayCast, bRayEndIsIntersection, true, scale, false); - RezObject(part, item, pos, Quaternion.Identity, Vector3.Zero, 0); + RezObject(part, item, pos, null, Vector3.Zero, 0); } } From b0889ed92a3c7d152f9b05aec1ce00dfc010e34e Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 12 Apr 2011 22:30:43 +0100 Subject: [PATCH 032/103] refactor: simplify bRayEndIsIntersection boolean set from RayEndIsIntersection byte --- .../InventoryAccess/InventoryAccessModule.cs | 15 +-------------- .../Region/Framework/Scenes/Scene.Inventory.cs | 13 +------------ 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index cdee53c4e2..73b0a35b7c 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -574,21 +574,8 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess { // m_log.DebugFormat("[INVENTORY ACCESS MODULE]: RezObject for {0}, item {1}", remoteClient.Name, itemID); - // Work out position details - byte bRayEndIsIntersection = (byte)0; - - if (RayEndIsIntersection) - { - bRayEndIsIntersection = (byte)1; - } - else - { - bRayEndIsIntersection = (byte)0; - } - + byte bRayEndIsIntersection = (byte)(RayEndIsIntersection ? 1 : 0); Vector3 scale = new Vector3(0.5f, 0.5f, 0.5f); - - Vector3 pos = m_Scene.GetNewRezLocation( RayStart, RayEnd, RayTargetID, Quaternion.Identity, BypassRayCast, bRayEndIsIntersection, true, scale, false); diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 254879b937..0f85925c78 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -1989,18 +1989,7 @@ namespace OpenSim.Region.Framework.Scenes return; } - // Work out position details - byte bRayEndIsIntersection = (byte)0; - - if (RayEndIsIntersection) - { - bRayEndIsIntersection = (byte)1; - } - else - { - bRayEndIsIntersection = (byte)0; - } - + byte bRayEndIsIntersection = (byte)(RayEndIsIntersection ? 1 : 0); Vector3 scale = new Vector3(0.5f, 0.5f, 0.5f); Vector3 pos = GetNewRezLocation( From da4a9653a27cbb1ecc779774a3d8715afb079086 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 12 Apr 2011 22:33:06 +0100 Subject: [PATCH 033/103] minor: remove some mono compiler warnings --- OpenSim/Data/Null/NullEstateData.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenSim/Data/Null/NullEstateData.cs b/OpenSim/Data/Null/NullEstateData.cs index 0cebff55aa..8db8064cbe 100755 --- a/OpenSim/Data/Null/NullEstateData.cs +++ b/OpenSim/Data/Null/NullEstateData.cs @@ -38,9 +38,9 @@ namespace OpenSim.Data.Null { public class NullEstateStore : IEstateDataStore { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private string m_connectionString; +// private string m_connectionString; protected virtual Assembly Assembly { @@ -58,7 +58,7 @@ namespace OpenSim.Data.Null public void Initialise(string connectionString) { - m_connectionString = connectionString; +// m_connectionString = connectionString; } private string[] FieldList From 7fa085b3fa8eacbfec4b9f8969bcbcc210ee50f0 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 14:40:57 -0700 Subject: [PATCH 034/103] fixed a couple bugs with the property queues --- .../ClientStack/LindenUDP/LLClientView.cs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index b96343e672..4db5b222d2 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4091,9 +4091,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectPropertiesBlocks.Value.Add(objPropDB); } } + + updatesThisCall++; } + Int32 ppcnt = 0; + Int32 pbcnt = 0; + if (objectPropertiesBlocks.IsValueCreated) { List blocks = objectPropertiesBlocks.Value; @@ -4105,9 +4110,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Header.Zerocoded = true; OutPacket(packet, ThrottleOutPacketType.Task, true); + + pbcnt += blocks.Count; + ppcnt++; } + Int32 fpcnt = 0; + Int32 fbcnt = 0; + if (objectFamilyBlocks.IsValueCreated) { List blocks = objectFamilyBlocks.Value; @@ -4130,10 +4141,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.ObjectData = blocks[i]; packet.Header.Zerocoded = true; OutPacket(packet, ThrottleOutPacketType.Task); + + fpcnt++; + fbcnt++; } } + m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); + m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); } private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) @@ -11401,6 +11417,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, throttlePacketType, true); } +/// + Dictionary pktsrc = new Dictionary(); + uint pktcnt = 0; +/// + /// /// This is the starting point for sending a simulator packet out to the client /// @@ -11448,7 +11469,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (count > 5) break; } - // m_log.WarnFormat("[BADGUY] {0}", stack); + lock(pktsrc) + { + if (! pktsrc.ContainsKey(stack)) + pktsrc.Add(stack,0); + pktsrc[stack]++; + + if (++pktcnt > 500) + { + pktcnt = 0; + m_log.WarnFormat("[PACKETCOUNTS] START"); + foreach (KeyValuePair pkt in pktsrc) + m_log.WarnFormat("[PACKETCOUNTS] {0,8}, {1}", pkt.Value, pkt.Key); + pktsrc.Clear(); + m_log.WarnFormat("[PACKETCOUNTS] END"); + } + } } m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); From bed463666cdc023bab8db2b725fb85736d2067df Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 12 Apr 2011 23:16:43 +0100 Subject: [PATCH 035/103] minor typo correction. thanks Ai Austin. --- bin/OpenSim.ini.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 55723d1efb..823daa0cc4 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -16,7 +16,7 @@ ;; files are loaded then type "config show" on the region console command line. ;; ;; -;; NOTES FOR DEVELOPERS REGARDING FORMAT OF TIHS FILE +;; NOTES FOR DEVELOPERS REGARDING THE FORMAT OF THIS FILE ;; ;; All leading white space is ignored, but preserved. ;; From 424995e1384178462799469e0200615308035460 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 12 Apr 2011 23:22:21 +0100 Subject: [PATCH 036/103] Move example HttpProxy setting to OpenSim.ini.example and tidy --- bin/OpenSim.ini.example | 8 ++++++++ bin/OpenSimDefaults.ini | 9 --------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 823daa0cc4..c05c3de2c4 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -201,6 +201,14 @@ ;; If not generating maptiles, use this static texture asset ID ; MaptileStaticUUID = "00000000-0000-0000-0000-000000000000" + ;; Http proxy setting for llHTTPRequest and dynamic texture loading, if required + ; HttpProxy = "http://proxy.com:8080" + + ;; If you're using HttpProxy, then you can set HttpProxyExceptions to a list of regular expressions for URLs that you don't want to go through the proxy + ;; For example, servers inside your firewall. + ;; Separate patterns with a ';' + ; HttpProxyExceptions = ".mydomain.com;localhost" + ;# {emailmodule} {} {Provide llEmail and llGetNextEmail functionality? (requires SMTP server)} {true false} false ;; The email module requires some configuration. It needs an SMTP ;; server to send mail through. diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index d6eee0e101..f857b8da63 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -14,15 +14,6 @@ ; Place to create a PID file ; PIDFile = "/tmp/my.pid" - ; Http proxy support for llHTTPRequest and dynamic texture loading - ; Set HttpProxy to the URL for your proxy server if you would like - ; to proxy llHTTPRequests through a firewall - ; HttpProxy = "http://proxy.com" - ; Set HttpProxyExceptions to a list of regular expressions for - ; URLs that you don't want going through the proxy such as servers - ; inside your firewall, separate patterns with a ';' - ; HttpProxyExceptions = ".mydomain.com;localhost" - startup_console_commands_file = "startup_commands.txt" shutdown_console_commands_file = "shutdown_commands.txt" From 80ba3de90240d190bad1bd15ff89dfdb390c5e24 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 15:40:57 -0700 Subject: [PATCH 037/103] Fixed the update of items in the priority queue to enable both types of property updates to be specified. Not sure if one form of property update should supercede another. But for now the old OpenSim behavior is preserved by sending both. --- OpenSim/Framework/IClientAPI.cs | 5 +++++ .../ClientStack/LindenUDP/LLClientView.cs | 21 +++++++++++++------ .../ClientStack/LindenUDP/PriorityQueue.cs | 2 +- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index c56a756314..f573c32d07 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -575,6 +575,11 @@ namespace OpenSim.Framework public ISceneEntity Entity; public uint Flags; + public virtual void Update(IEntityUpdate update) + { + this.Flags |= update.Flags; + } + public IEntityUpdate(ISceneEntity entity, uint flags) { Entity = entity; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 4db5b222d2..92a76acba6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4032,26 +4032,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP private class ObjectPropertyUpdate : IEntityUpdate { internal bool SendFamilyProps; + internal bool SendObjectProps; - public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam) + public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam, bool sendobj) : base(entity,flags) { SendFamilyProps = sendfam; + SendObjectProps = sendobj; + } + public void Update(ObjectPropertyUpdate update) + { + SendFamilyProps = SendFamilyProps || update.SendFamilyProps; + SendObjectProps = SendObjectProps || update.SendObjectProps; + Flags |= update.Flags; } } public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) { - uint priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = 0; // time based ordering only lock (m_entityProps.SyncRoot) - m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true)); + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); } public void SendObjectPropertiesReply(ISceneEntity entity) { - uint priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = 0; // time based ordering only lock (m_entityProps.SyncRoot) - m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false)); + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false,true)); } private void ProcessEntityPropertyRequests(int maxUpdates) @@ -4082,7 +4090,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectFamilyBlocks.Value.Add(objPropDB); } } - else + + if (update.SendObjectProps) { if (update.Entity is SceneObjectPart) { diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 6521a00745..b62ec07ed9 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -87,7 +87,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_lookupTable.TryGetValue(localid, out lookup)) { entry = lookup.Heap[lookup.Handle].EntryOrder; - value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; + value.Update(lookup.Heap[lookup.Handle].Value); lookup.Heap.Remove(lookup.Handle); } From 317617cfda5356e9d375d00c93cd0d23a489107e Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 15:55:21 -0700 Subject: [PATCH 038/103] remove packet monitoring debugging code --- .../ClientStack/LindenUDP/LLClientView.cs | 118 +----------------- 1 file changed, 2 insertions(+), 116 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 92a76acba6..cf04f0d809 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -300,77 +300,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Used to adjust Sun Orbit values so Linden based viewers properly position sun private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; - // First log file or time has expired, start writing to a new log file -// -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// THIS IS DEBUGGING CODE & SHOULD BE REMOVED -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- - public class QueueLogger - { - public Int32 start = 0; - public StreamWriter Log = null; - private Dictionary m_idMap = new Dictionary(); - - public QueueLogger() - { - DateTime now = DateTime.Now; - String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss")); - Log = new StreamWriter(fname); - - start = Util.EnvironmentTickCount(); - } - - public int LookupID(UUID uuid) - { - int localid; - if (! m_idMap.TryGetValue(uuid,out localid)) - { - localid = m_idMap.Count + 1; - m_idMap[uuid] = localid; - } - - return localid; - } - } - - public static QueueLogger QueueLog = null; - - // ----------------------------------------------------------------- - public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - int aid = QueueLog.LookupID(avatar); - QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue); - } - } - - // ----------------------------------------------------------------- - public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString()); - } - } -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected static Dictionary PacketHandlers = new Dictionary(); //Global/static handlers for all clients @@ -4025,10 +3954,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } -/// ----------------------------------------------------------------- -/// -/// ----------------------------------------------------------------- - private class ObjectPropertyUpdate : IEntityUpdate { internal bool SendFamilyProps; @@ -4157,8 +4082,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } - m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); - m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); + // m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); + // m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); } private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) @@ -4232,10 +4157,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return block; } -/// ----------------------------------------------------------------- -/// -/// ----------------------------------------------------------------- - #region Estate Data Sending Methods private static bool convertParamStringToBool(byte[] field) @@ -11426,11 +11347,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, throttlePacketType, true); } -/// - Dictionary pktsrc = new Dictionary(); - uint pktcnt = 0; -/// - /// /// This is the starting point for sending a simulator packet out to the client /// @@ -11465,36 +11381,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (logPacket) m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } - - if (throttlePacketType == ThrottleOutPacketType.Task) - { - System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); // get call stack - System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) - - string stack = ""; - for (int count = 1; count < stackFrames.Length; count++) - { - stack += (stack == "" ? "" : ",") + stackFrames[count].GetMethod().Name; - if (count > 5) break; - } - - lock(pktsrc) - { - if (! pktsrc.ContainsKey(stack)) - pktsrc.Add(stack,0); - pktsrc[stack]++; - - if (++pktcnt > 500) - { - pktcnt = 0; - m_log.WarnFormat("[PACKETCOUNTS] START"); - foreach (KeyValuePair pkt in pktsrc) - m_log.WarnFormat("[PACKETCOUNTS] {0,8}, {1}", pkt.Value, pkt.Key); - pktsrc.Clear(); - m_log.WarnFormat("[PACKETCOUNTS] END"); - } - } - } m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); } From 2f436875899f432a88f432ab86a6858b3a4cc373 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 11 Apr 2011 09:06:28 -0700 Subject: [PATCH 039/103] New tokenbucket algorithm. This one provides fair sharing of the queues when client and simulator throttles are set. This algorithm also uses pre-defined burst rate of 150% of the sustained rate for each of the throttles. Removed the "state" queue. The state queue is not a Linden queue and appeared to be used just to get kill packets sent. --- .../ClientStack/LindenUDP/LLClientView.cs | 6 +- .../ClientStack/LindenUDP/LLUDPClient.cs | 96 +++--- .../ClientStack/LindenUDP/LLUDPServer.cs | 2 +- .../ClientStack/LindenUDP/TokenBucket.cs | 314 +++++++++++------- 4 files changed, 266 insertions(+), 152 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 8de31d7069..934a2d5c84 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -1610,7 +1610,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - OutPacket(kill, ThrottleOutPacketType.State); + // OutPacket(kill, ThrottleOutPacketType.State); + OutPacket(kill, ThrottleOutPacketType.Task); } } @@ -2440,7 +2441,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Effect = effectBlocks; - OutPacket(packet, ThrottleOutPacketType.State); + // OutPacket(packet, ThrottleOutPacketType.State); + OutPacket(packet, ThrottleOutPacketType.Task); } public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 9a8bfd357c..5a69851b3c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -135,7 +135,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_nextOnQueueEmpty = 1; /// Throttle bucket for this agent's connection - private readonly TokenBucket m_throttle; + private readonly TokenBucket m_throttleClient; + /// Throttle bucket for this agent's connection + private readonly TokenBucket m_throttleCategory; /// Throttle buckets for each packet category private readonly TokenBucket[] m_throttleCategories; /// Outgoing queues for throttled packets @@ -174,7 +176,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_maxRTO = maxRTO; // Create a token bucket throttle for this client that has the scene token bucket as a parent - m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); + m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit); + // Create a token bucket throttle for the total categary with the client bucket as a throttle + m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit); // Create an array of token buckets for this clients different throttle categories m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; @@ -185,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Initialize the packet outboxes, where packets sit while they are waiting for tokens m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); // Initialize the token buckets that control the throttling for each category - m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); + m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type)); } // Default the retransmission timeout to three seconds @@ -206,6 +210,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_packetOutboxes[i].Clear(); m_nextPackets[i] = null; } + + // pull the throttle out of the scene throttle + m_throttleClient.Parent.UnregisterRequest(m_throttleClient); OnPacketStats = null; OnQueueEmpty = null; } @@ -216,6 +223,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Information about the client connection public ClientInfo GetClientInfo() { +/// + TokenBucket tb; + + tb = m_throttleClient.Parent; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT"); + + tb = m_throttleClient; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT"); + + tb = m_throttleCategory; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY"); + + for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) + { + tb = m_throttleCategories[i]; + m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET"); + } + +/// + // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists // of pending and needed ACKs for every client every time some method wants information about // this connection is a recipe for poor performance @@ -223,13 +250,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP info.pendingAcks = new Dictionary(); info.needAck = new Dictionary(); - info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; - info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; - info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; - info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; - info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; - info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; - info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; + info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; + info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; + info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; + info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; + // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; + info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + info.taskThrottle + info.assetThrottle + info.textureThrottle; @@ -317,8 +345,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); // State is a subcategory of task that we allocate a percentage to - int state = (int)((float)task * STATE_TASK_PERCENTAGE); - task -= state; + int state = 0; + // int state = (int)((float)task * STATE_TASK_PERCENTAGE); + // task -= state; // Make sure none of the throttles are set below our packet MTU, // otherwise a throttle could become permanently clogged @@ -339,40 +368,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Update the token buckets with new throttle values TokenBucket bucket; - bucket = m_throttle; - bucket.MaxBurst = total; + bucket = m_throttleCategory; + bucket.RequestedDripRate = total; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; - bucket.DripRate = resend; - bucket.MaxBurst = resend; + bucket.RequestedDripRate = resend; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; - bucket.DripRate = land; - bucket.MaxBurst = land; + bucket.RequestedDripRate = land; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; - bucket.DripRate = wind; - bucket.MaxBurst = wind; + bucket.RequestedDripRate = wind; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; - bucket.DripRate = cloud; - bucket.MaxBurst = cloud; + bucket.RequestedDripRate = cloud; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; - bucket.DripRate = asset; - bucket.MaxBurst = asset; + bucket.RequestedDripRate = asset; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; - bucket.DripRate = task + state; - bucket.MaxBurst = task + state; + bucket.RequestedDripRate = task; bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; - bucket.DripRate = state; - bucket.MaxBurst = state; + bucket.RequestedDripRate = state; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; - bucket.DripRate = texture; - bucket.MaxBurst = texture; + bucket.RequestedDripRate = texture; // Reset the packed throttles cached data m_packedThrottles = null; @@ -387,14 +408,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP data = new byte[7 * 4]; int i = 0; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + - m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4; m_packedThrottles = data; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 583214ca0b..d08b25f79d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -228,7 +228,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } #endregion BinaryStats - m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); + m_throttle = new TokenBucket(null, sceneThrottleBps); ThrottleRates = new ThrottleRates(configSource); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 0a8331f32a..e4d59ff829 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -26,6 +26,10 @@ */ using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using log4net; namespace OpenSim.Region.ClientStack.LindenUDP { @@ -35,89 +39,126 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public class TokenBucket { - /// Parent bucket to this bucket, or null if this is a root - /// bucket - TokenBucket parent; - /// Size of the bucket in bytes. If zero, the bucket has - /// infinite capacity - int maxBurst; - /// Rate that the bucket fills, in bytes per millisecond. If - /// zero, the bucket always remains full - int tokensPerMS; - /// Number of tokens currently in the bucket - int content; - /// Time of the last drip, in system ticks - int lastDrip; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static Int32 m_counter = 0; + + private Int32 m_identifier; + + /// + /// Number of ticks (ms) per quantum, drip rate and max burst + /// are defined over this interval. + /// + private const Int32 m_ticksPerQuantum = 1000; - #region Properties + /// + /// This is the number of quantums worth of packets that can + /// be accommodated during a burst + /// + private const Double m_quantumsPerBurst = 1.5; + + /// + /// + private const Int32 m_minimumDripRate = 1400; + + /// Time of the last drip, in system ticks + private Int32 m_lastDrip; + + /// + /// The number of bytes that can be sent at this moment. This is the + /// current number of tokens in the bucket + /// + private Int64 m_tokenCount; + + /// + /// Map of children buckets and their requested maximum burst rate + /// + private Dictionary m_children = new Dictionary(); + +#region Properties /// /// The parent bucket of this bucket, or null if this bucket has no /// parent. The parent bucket will limit the aggregate bandwidth of all /// of its children buckets /// + private TokenBucket m_parent; public TokenBucket Parent { - get { return parent; } + get { return m_parent; } + set { m_parent = value; } } /// /// Maximum burst rate in bytes per second. This is the maximum number - /// of tokens that can accumulate in the bucket at any one time + /// of tokens that can accumulate in the bucket at any one time. This + /// also sets the total request for leaf nodes /// - public int MaxBurst + private Int64 m_burstRate; + public Int64 RequestedBurstRate { - get { return maxBurst; } - set { maxBurst = (value >= 0 ? value : 0); } + get { return m_burstRate; } + set { m_burstRate = (value < 0 ? 0 : value); } } + public Int64 BurstRate + { + get { + double rate = RequestedBurstRate * BurstRateModifier(); + if (rate < m_minimumDripRate * m_quantumsPerBurst) + rate = m_minimumDripRate * m_quantumsPerBurst; + + return (Int64) rate; + } + } + /// /// The speed limit of this bucket in bytes per second. This is the - /// number of tokens that are added to the bucket per second + /// number of tokens that are added to the bucket per quantum /// /// Tokens are added to the bucket any time /// is called, at the granularity of /// the system tick interval (typically around 15-22ms) - public int DripRate + private Int64 m_dripRate; + public Int64 RequestedDripRate { - get { return tokensPerMS * 1000; } - set - { - if (value == 0) - tokensPerMS = 0; - else - { - int bpms = (int)((float)value / 1000.0f); + get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } + set { + m_dripRate = (value < 0 ? 0 : value); + m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); + m_totalDripRequest = m_dripRate; + if (m_parent != null) + m_parent.RegisterRequest(this,m_dripRate); + } + } - if (bpms <= 0) - tokensPerMS = 1; // 1 byte/ms is the minimum granularity - else - tokensPerMS = bpms; - } + public Int64 DripRate + { + get { + if (m_parent == null) + return Math.Min(RequestedDripRate,TotalDripRequest); + + double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); + if (rate < m_minimumDripRate) + rate = m_minimumDripRate; + + return (Int64)rate; } } /// - /// The speed limit of this bucket in bytes per millisecond + /// The current total of the requested maximum burst rates of + /// this bucket's children buckets. /// - public int DripPerMS - { - get { return tokensPerMS; } - } + private Int64 m_totalDripRequest; + public Int64 TotalDripRequest + { + get { return m_totalDripRequest; } + set { m_totalDripRequest = value; } + } + +#endregion Properties - /// - /// The number of bytes that can be sent at this moment. This is the - /// current number of tokens in the bucket - /// If this bucket has a parent bucket that does not have - /// enough tokens for a request, will - /// return false regardless of the content of this bucket - /// - public int Content - { - get { return content; } - } - - #endregion Properties +#region Constructor /// /// Default constructor @@ -128,56 +169,115 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// zero if this bucket has no maximum capacity /// Rate that the bucket fills, in bytes per /// second. If zero, the bucket always remains full - public TokenBucket(TokenBucket parent, int maxBurst, int dripRate) + public TokenBucket(TokenBucket parent, Int64 dripRate) { - this.parent = parent; - MaxBurst = maxBurst; - DripRate = dripRate; - lastDrip = Environment.TickCount & Int32.MaxValue; + m_identifier = m_counter++; + + Parent = parent; + RequestedDripRate = dripRate; + // TotalDripRequest = dripRate; // this will be overwritten when a child node registers + // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); + m_lastDrip = Environment.TickCount & Int32.MaxValue; } +#endregion Constructor + + /// + /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning + /// no modification if the requested bandwidth is less than the + /// max burst bandwidth all the way to the root of the throttle + /// hierarchy. However, if any of the parents is over-booked, then + /// the modifier will be less than 1. + /// + private double DripRateModifier() + { + Int64 driprate = DripRate; + return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; + } + + /// + /// + private double BurstRateModifier() + { + // for now... burst rate is always m_quantumsPerBurst (constant) + // larger than drip rate so the ratio of burst requests is the + // same as the drip ratio + return DripRateModifier(); + } + + /// + /// Register drip rate requested by a child of this throttle. Pass the + /// changes up the hierarchy. + /// + public void RegisterRequest(TokenBucket child, Int64 request) + { + m_children[child] = request; + // m_totalDripRequest = m_children.Values.Sum(); + + m_totalDripRequest = 0; + foreach (KeyValuePair cref in m_children) + m_totalDripRequest += cref.Value; + + // Pass the new values up to the parent + if (m_parent != null) + m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); + } + + /// + /// Remove the rate requested by a child of this throttle. Pass the + /// changes up the hierarchy. + /// + public void UnregisterRequest(TokenBucket child) + { + m_children.Remove(child); + // m_totalDripRequest = m_children.Values.Sum(); + + m_totalDripRequest = 0; + foreach (KeyValuePair cref in m_children) + m_totalDripRequest += cref.Value; + + // Pass the new values up to the parent + if (m_parent != null) + m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); + } + /// /// Remove a given number of tokens from the bucket /// /// Number of tokens to remove from the bucket /// True if the requested number of tokens were removed from /// the bucket, otherwise false - public bool RemoveTokens(int amount) + public bool RemoveTokens(Int64 amount) { - bool dummy; - return RemoveTokens(amount, out dummy); + // Deposit tokens for this interval + Drip(); + + // If we have enough tokens then remove them and return + if (m_tokenCount - amount >= 0) + { + if (m_parent == null || m_parent.RemoveTokens(amount)) + { + m_tokenCount -= amount; + return true; + } + } + + return false; } /// - /// Remove a given number of tokens from the bucket + /// Deposit tokens into the bucket from a child bucket that did + /// not use all of its available tokens /// - /// Number of tokens to remove from the bucket - /// True if tokens were added to the bucket - /// during this call, otherwise false - /// True if the requested number of tokens were removed from - /// the bucket, otherwise false - public bool RemoveTokens(int amount, out bool dripSucceeded) + private void Deposit(Int64 count) { - if (maxBurst == 0) - { - dripSucceeded = true; - return true; - } + m_tokenCount += count; - dripSucceeded = Drip(); - - if (content - amount >= 0) - { - if (parent != null && !parent.RemoveTokens(amount)) - return false; - - content -= amount; - return true; - } - else - { - return false; - } + // Deposit the overflow in the parent bucket, this is how we share + // unused bandwidth + Int64 burstrate = BurstRate; + if (m_tokenCount > burstrate) + m_tokenCount = burstrate; } /// @@ -186,37 +286,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// call to Drip /// /// True if tokens were added to the bucket, otherwise false - public bool Drip() + private void Drip() { - if (tokensPerMS == 0) + // This should never happen... means we are a leaf node and were created + // with no drip rate... + if (DripRate == 0) { - content = maxBurst; - return true; + m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0"); + return; } - else - { - int now = Environment.TickCount & Int32.MaxValue; - int deltaMS = now - lastDrip; + + // Determine the interval over which we are adding tokens, never add + // more than a single quantum of tokens + Int32 now = Environment.TickCount & Int32.MaxValue; + Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum); - if (deltaMS <= 0) - { - if (deltaMS < 0) - lastDrip = now; - return false; - } + m_lastDrip = now; - int dripAmount = deltaMS * tokensPerMS; + // This can be 0 in the very unusual case that the timer wrapped + // It can be 0 if we try add tokens at a sub-tick rate + if (deltaMS <= 0) + return; - content = Math.Min(content + dripAmount, maxBurst); - lastDrip = now; - - if (dripAmount < 0 || content < 0) - // sim has been idle for too long, integer has overflown - // previous calculation is meaningless, let's put it at correct max - content = maxBurst; - - return true; - } + Deposit(deltaMS * DripRate / m_ticksPerQuantum); } } } From 69d014e1dcb0e05a4ec927a5501156627856bccb Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 12:36:36 -0700 Subject: [PATCH 040/103] First pass at moving object property requests into a queue similar to the entity update queue. The number of property packets can become significant when selecting/deselecting large numbers of objects. This is experimental code. --- .../Client/MXP/ClientStack/MXPClientView.cs | 4 +- .../VWoHTTP/ClientStack/VWHClientView.cs | 4 +- OpenSim/Framework/IClientAPI.cs | 37 +- .../ClientStack/LindenUDP/LLClientView.cs | 367 +++++++++++------- .../ClientStack/LindenUDP/PriorityQueue.cs | 12 +- .../Examples/SimpleModule/MyNpcCharacter.cs | 12 +- .../Framework/Scenes/SceneObjectGroup.cs | 10 +- .../Framework/Scenes/SceneObjectPart.cs | 10 +- .../Server/IRCClientView.cs | 5 +- .../OptionalModules/World/NPC/NPCAvatar.cs | 12 +- OpenSim/Tests/Common/Mock/TestClient.cs | 11 +- 11 files changed, 269 insertions(+), 215 deletions(-) diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index d1a0440524..a604a2ee63 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -1337,12 +1337,12 @@ namespace OpenSim.Client.MXP.ClientStack // Need to translate to MXP somehow } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { //throw new System.NotImplementedException(); } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { //throw new System.NotImplementedException(); } diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index fc27f0198b..d8cd0acbd9 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -884,12 +884,12 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { throw new System.NotImplementedException(); } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { throw new System.NotImplementedException(); } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 5bf0b7b0a9..c56a756314 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -570,16 +570,30 @@ namespace OpenSim.Framework public float dwell; } - public class EntityUpdate + public class IEntityUpdate { public ISceneEntity Entity; - public PrimUpdateFlags Flags; - public float TimeDilation; + public uint Flags; - public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + public IEntityUpdate(ISceneEntity entity, uint flags) { Entity = entity; Flags = flags; + } + } + + + public class EntityUpdate : IEntityUpdate + { + // public ISceneEntity Entity; + // public PrimUpdateFlags Flags; + public float TimeDilation; + + public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + : base(entity,(uint)flags) + { + //Entity = entity; + // Flags = flags; TimeDilation = timedilation; } } @@ -1211,20 +1225,9 @@ namespace OpenSim.Framework /// void SendSimStats(SimStats stats); - void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, - uint Category, - UUID LastOwnerID, string ObjectName, string Description); + void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags); - void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, - UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, - string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, - uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice); + void SendObjectPropertiesReply(ISceneEntity Entity); void SendAgentOffline(UUID[] agentIDs); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 934a2d5c84..b96343e672 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -386,6 +386,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_cachedTextureSerial; private PriorityQueue m_entityUpdates; + private PriorityQueue m_entityProps; private Prioritizer m_prioritizer; private bool m_disableFacelights = false; @@ -433,9 +434,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected IAssetService m_assetService; private const bool m_checkPackets = true; - private Timer m_propertiesPacketTimer; - private List m_propertiesBlocks = new List(); - #endregion Class Members #region Properties @@ -511,6 +509,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene = scene; m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); + m_entityProps = new PriorityQueue(m_scene.Entities.Count); m_fullUpdateDataBlocksBuilder = new List(); m_killRecord = new HashSet(); // m_attachmentsSent = new HashSet(); @@ -534,9 +533,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_udpClient.OnQueueEmpty += HandleQueueEmpty; m_udpClient.OnPacketStats += PopulateStats; - m_propertiesPacketTimer = new Timer(100); - m_propertiesPacketTimer.Elapsed += ProcessObjectPropertiesPacket; - m_prioritizer = new Prioritizer(m_scene); RegisterLocalPacketHandlers(); @@ -3636,9 +3632,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); } - private Int32 m_LastQueueFill = 0; - private uint m_maxUpdates = 0; - private void ProcessEntityUpdates(int maxUpdates) { OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); @@ -3646,46 +3639,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.Lazy> terseUpdateBlocks = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy>(); + // Check to see if this is a flush if (maxUpdates <= 0) { - m_maxUpdates = Int32.MaxValue; + maxUpdates = Int32.MaxValue; } - else - { - if (m_maxUpdates == 0 || m_LastQueueFill == 0) - { - m_maxUpdates = (uint)maxUpdates; - } - else - { - if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) - m_maxUpdates += 5; - else - m_maxUpdates = m_maxUpdates >> 1; - } - m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); - } - m_LastQueueFill = Util.EnvironmentTickCount(); - + int updatesThisCall = 0; -// -// DEBUGGING CODE... REMOVE -// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates); -// // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race // condition where a kill can be processed before an out-of-date update for the same object. lock (m_killRecord) { float avgTimeDilation = 1.0f; - EntityUpdate update; + IEntityUpdate iupdate; Int32 timeinqueue; // this is just debugging code & can be dropped later - while (updatesThisCall < m_maxUpdates) + while (updatesThisCall < maxUpdates) { lock (m_entityUpdates.SyncRoot) - if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) + if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue)) break; + + EntityUpdate update = (EntityUpdate)iupdate; + avgTimeDilation += update.TimeDilation; avgTimeDilation *= 0.5f; @@ -3725,7 +3702,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region UpdateFlags to packet type conversion - PrimUpdateFlags updateFlags = update.Flags; + PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags; bool canUseCompressed = true; bool canUseImproved = true; @@ -3804,6 +3781,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Block Construction } + #region Packet Sending @@ -3904,12 +3882,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Primitive Packet/Data Sending Methods + // These are used to implement an adaptive backoff in the number + // of updates converted to packets. Since we don't want packets + // to sit in the queue with old data, only convert enough updates + // to packets that can be sent in 200ms. + private Int32 m_LastQueueFill = 0; + private Int32 m_maxUpdates = 0; + void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) { if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) { + if (m_maxUpdates == 0 || m_LastQueueFill == 0) + { + m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; + } + else + { + if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) + m_maxUpdates += 5; + else + m_maxUpdates = m_maxUpdates >> 1; + } + m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); + m_LastQueueFill = Util.EnvironmentTickCount(); + if (m_entityUpdates.Count > 0) - ProcessEntityUpdates(m_udpServer.PrimUpdatesPerCallback); + ProcessEntityUpdates(m_maxUpdates); + + if (m_entityProps.Count > 0) + ProcessEntityPropertyRequests(m_maxUpdates); } if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) @@ -4023,47 +4025,147 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) +/// ----------------------------------------------------------------- +/// +/// ----------------------------------------------------------------- + + private class ObjectPropertyUpdate : IEntityUpdate { - ObjectPropertiesFamilyPacket objPropFamilyPack = (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); - // TODO: don't create new blocks if recycling an old packet - - ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); - objPropDB.RequestFlags = RequestFlags; - objPropDB.ObjectID = ObjectUUID; - if (OwnerID == GroupID) - objPropDB.OwnerID = UUID.Zero; - else - objPropDB.OwnerID = OwnerID; - objPropDB.GroupID = GroupID; - objPropDB.BaseMask = BaseMask; - objPropDB.OwnerMask = OwnerMask; - objPropDB.GroupMask = GroupMask; - objPropDB.EveryoneMask = EveryoneMask; - objPropDB.NextOwnerMask = NextOwnerMask; - - // TODO: More properties are needed in SceneObjectPart! - objPropDB.OwnershipCost = OwnershipCost; - objPropDB.SaleType = SaleType; - objPropDB.SalePrice = SalePrice; - objPropDB.Category = Category; - objPropDB.LastOwnerID = LastOwnerID; - objPropDB.Name = Util.StringToBytes256(ObjectName); - objPropDB.Description = Util.StringToBytes256(Description); - objPropFamilyPack.ObjectData = objPropDB; - objPropFamilyPack.Header.Zerocoded = true; - OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task); + internal bool SendFamilyProps; + + public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam) + : base(entity,flags) + { + SendFamilyProps = sendfam; + } + } + + public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) + { + uint priority = m_prioritizer.GetUpdatePriority(this, entity); + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true)); } - public void SendObjectPropertiesReply( - UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) + { + uint priority = m_prioritizer.GetUpdatePriority(this, entity); + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false)); + } + + private void ProcessEntityPropertyRequests(int maxUpdates) + { + OpenSim.Framework.Lazy> objectFamilyBlocks = + new OpenSim.Framework.Lazy>(); + + OpenSim.Framework.Lazy> objectPropertiesBlocks = + new OpenSim.Framework.Lazy>(); + + IEntityUpdate iupdate; + Int32 timeinqueue; // this is just debugging code & can be dropped later + + int updatesThisCall = 0; + while (updatesThisCall < m_maxUpdates) + { + lock (m_entityProps.SyncRoot) + if (!m_entityProps.TryDequeue(out iupdate, out timeinqueue)) + break; + + ObjectPropertyUpdate update = (ObjectPropertyUpdate)iupdate; + if (update.SendFamilyProps) + { + if (update.Entity is SceneObjectPart) + { + SceneObjectPart sop = (SceneObjectPart)update.Entity; + ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); + objectFamilyBlocks.Value.Add(objPropDB); + } + } + else + { + if (update.Entity is SceneObjectPart) + { + SceneObjectPart sop = (SceneObjectPart)update.Entity; + ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); + objectPropertiesBlocks.Value.Add(objPropDB); + } + } + } + + + if (objectPropertiesBlocks.IsValueCreated) + { + List blocks = objectPropertiesBlocks.Value; + + ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); + packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; + for (int i = 0; i < blocks.Count; i++) + packet.ObjectData[i] = blocks[i]; + + packet.Header.Zerocoded = true; + OutPacket(packet, ThrottleOutPacketType.Task, true); + } + + + if (objectFamilyBlocks.IsValueCreated) + { + List blocks = objectFamilyBlocks.Value; + + // ObjectPropertiesFamilyPacket objPropFamilyPack = + // (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); + // + // objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count]; + // for (int i = 0; i < blocks.Count; i++) + // objPropFamilyPack.ObjectData[i] = blocks[i]; + // + // OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true); + + // one packet per object block... uggh... + for (int i = 0; i < blocks.Count; i++) + { + ObjectPropertiesFamilyPacket packet = + (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); + + packet.ObjectData = blocks[i]; + packet.Header.Zerocoded = true; + OutPacket(packet, ThrottleOutPacketType.Task); + } + + } + + } + + private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) + { + ObjectPropertiesFamilyPacket.ObjectDataBlock block = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); + + block.RequestFlags = requestFlags; + block.ObjectID = sop.UUID; + if (sop.OwnerID == sop.GroupID) + block.OwnerID = UUID.Zero; + else + block.OwnerID = sop.OwnerID; + block.GroupID = sop.GroupID; + block.BaseMask = sop.BaseMask; + block.OwnerMask = sop.OwnerMask; + block.GroupMask = sop.GroupMask; + block.EveryoneMask = sop.EveryoneMask; + block.NextOwnerMask = sop.NextOwnerMask; + + // TODO: More properties are needed in SceneObjectPart! + block.OwnershipCost = sop.OwnershipCost; + block.SaleType = sop.ObjectSaleType; + block.SalePrice = sop.SalePrice; + block.Category = sop.Category; + block.LastOwnerID = sop.CreatorID; // copied from old SOG call... is this right? + block.Name = Util.StringToBytes256(sop.Name); + block.Description = Util.StringToBytes256(sop.Description); + + return block; + } + + private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop) { //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); // TODO: don't create new blocks if recycling an old packet @@ -4071,85 +4173,43 @@ namespace OpenSim.Region.ClientStack.LindenUDP ObjectPropertiesPacket.ObjectDataBlock block = new ObjectPropertiesPacket.ObjectDataBlock(); - block.ItemID = ItemID; - block.CreationDate = CreationDate; - block.CreatorID = CreatorUUID; - block.FolderID = FolderUUID; - block.FromTaskID = FromTaskUUID; - block.GroupID = GroupUUID; - block.InventorySerial = InventorySerial; + block.ObjectID = sop.UUID; + block.Name = Util.StringToBytes256(sop.Name); + block.Description = Util.StringToBytes256(sop.Description); - block.LastOwnerID = LastOwnerUUID; - // proper.ObjectData[0].LastOwnerID = UUID.Zero; - - block.ObjectID = ObjectUUID; - if (OwnerUUID == GroupUUID) + block.CreationDate = (ulong)sop.CreationDate * 1000000; // viewer wants date in microseconds + block.CreatorID = sop.CreatorID; + block.GroupID = sop.GroupID; + block.LastOwnerID = sop.LastOwnerID; + if (sop.OwnerID == sop.GroupID) block.OwnerID = UUID.Zero; else - block.OwnerID = OwnerUUID; - block.TouchName = Util.StringToBytes256(TouchTitle); - block.TextureID = TextureID; - block.SitName = Util.StringToBytes256(SitTitle); - block.Name = Util.StringToBytes256(ItemName); - block.Description = Util.StringToBytes256(ItemDescription); - block.OwnerMask = OwnerMask; - block.NextOwnerMask = NextOwnerMask; - block.GroupMask = GroupMask; - block.EveryoneMask = EveryoneMask; - block.BaseMask = BaseMask; - // proper.ObjectData[0].AggregatePerms = 53; - // proper.ObjectData[0].AggregatePermTextures = 0; - // proper.ObjectData[0].AggregatePermTexturesOwner = 0; - block.SaleType = saleType; - block.SalePrice = salePrice; + block.OwnerID = sop.OwnerID; - lock (m_propertiesPacketTimer) - { - m_propertiesBlocks.Add(block); + block.ItemID = sop.FromUserInventoryItemID; + block.FolderID = UUID.Zero; // sop.FromFolderID ?? + block.FromTaskID = UUID.Zero; // ??? + block.InventorySerial = (short)sop.InventorySerial; + + SceneObjectPart root = sop.ParentGroup.RootPart; - int length = 0; - foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) - { - length += b.Length; - } - if (length > 1100) // FIXME: use real MTU - { - ProcessObjectPropertiesPacket(null, null); - m_propertiesPacketTimer.Stop(); - return; - } + block.TouchName = Util.StringToBytes256(root.TouchName); + block.TextureID = new byte[0]; // TextureID ??? + block.SitName = Util.StringToBytes256(root.SitName); + block.OwnerMask = root.OwnerMask; + block.NextOwnerMask = root.NextOwnerMask; + block.GroupMask = root.GroupMask; + block.EveryoneMask = root.EveryoneMask; + block.BaseMask = root.BaseMask; + block.SaleType = root.ObjectSaleType; + block.SalePrice = root.SalePrice; - m_propertiesPacketTimer.Stop(); - m_propertiesPacketTimer.Start(); - } - - //proper.Header.Zerocoded = true; - //OutPacket(proper, ThrottleOutPacketType.Task); + return block; } - private void ProcessObjectPropertiesPacket(Object sender, ElapsedEventArgs e) - { - ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); - - lock (m_propertiesPacketTimer) - { - m_propertiesPacketTimer.Stop(); - - proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count]; - - int index = 0; - - foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) - { - proper.ObjectData[index++] = b; - } - - m_propertiesBlocks.Clear(); - } - - proper.Header.Zerocoded = true; - OutPacket(proper, ThrottleOutPacketType.Task); - } +/// ----------------------------------------------------------------- +/// +/// ----------------------------------------------------------------- #region Estate Data Sending Methods @@ -4482,6 +4542,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendForceClientSelectObjects(List ObjectIDs) { + m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count); + bool firstCall = true; const int MAX_OBJECTS_PER_PACKET = 251; ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect); @@ -11374,6 +11436,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } + if (throttlePacketType == ThrottleOutPacketType.Task) + { + System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); // get call stack + System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) + + string stack = ""; + for (int count = 1; count < stackFrames.Length; count++) + { + stack += (stack == "" ? "" : ",") + stackFrames[count].GetMethod().Name; + if (count > 5) break; + } + + // m_log.WarnFormat("[BADGUY] {0}", stack); + } + m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 364ce4ba9e..6521a00745 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -78,7 +78,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public bool Enqueue(uint pqueue, EntityUpdate value) + public bool Enqueue(uint pqueue, IEntityUpdate value) { LookupItem lookup; @@ -99,7 +99,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return true; } - internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) + internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue) { for (int i = 0; i < m_numberOfQueues; ++i) { @@ -122,7 +122,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } timeinqueue = 0; - value = default(EntityUpdate); + value = default(IEntityUpdate); return false; } @@ -175,8 +175,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region MinHeapItem private struct MinHeapItem : IComparable { - private EntityUpdate value; - internal EntityUpdate Value { + private IEntityUpdate value; + internal IEntityUpdate Value { get { return this.value; } @@ -212,7 +212,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP this.pqueue = pqueue; } - internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) + internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value) { this.entrytime = Util.EnvironmentTickCount(); this.entryorder = entryorder; diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index d939329814..89e9e204bb 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -702,18 +702,12 @@ namespace OpenSim.Region.Examples.SimpleModule { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { + } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index ca7d9d9d57..30563d4232 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -1765,10 +1765,12 @@ namespace OpenSim.Region.Framework.Scenes /// public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags) { - remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, - RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, - RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, - RootPart.CreatorID, RootPart.Name, RootPart.Description); + remoteClient.SendObjectPropertiesFamilyData(RootPart, RequestFlags); + +// remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, +// RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, +// RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, +// RootPart.CreatorID, RootPart.Name, RootPart.Description); } public void SetPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 4d5eedfc95..3d2eacd720 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2055,15 +2055,7 @@ namespace OpenSim.Region.Framework.Scenes public void GetProperties(IClientAPI client) { - //Viewer wants date in microseconds so multiply it by 1,000,000. - client.SendObjectPropertiesReply( - m_fromUserInventoryItemID, (ulong)_creationDate*(ulong)1e6, _creatorID, UUID.Zero, UUID.Zero, - _groupID, (short)InventorySerial, _lastOwnerID, UUID, _ownerID, - ParentGroup.RootPart.TouchName, new byte[0], ParentGroup.RootPart.SitName, Name, Description, - ParentGroup.RootPart._ownerMask, ParentGroup.RootPart._nextOwnerMask, ParentGroup.RootPart._groupMask, ParentGroup.RootPart._everyoneMask, - ParentGroup.RootPart._baseMask, - ParentGroup.RootPart.ObjectSaleType, - ParentGroup.RootPart.SalePrice); + client.SendObjectPropertiesReply(this); } public UUID GetRootPartUUID() diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 821cd4bd33..4b6e52e757 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -1332,14 +1332,13 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { - } public void SendAgentOffline(UUID[] agentIDs) diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 96760a2721..2504e3019c 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -786,18 +786,12 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { + } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index d1dc17fc9b..dca562674b 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -816,18 +816,11 @@ namespace OpenSim.Tests.Common.Mock { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } From 1a0f107012ae96f5030b564cc51b6f36b949a467 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 14:40:57 -0700 Subject: [PATCH 041/103] fixed a couple bugs with the property queues --- .../ClientStack/LindenUDP/LLClientView.cs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index b96343e672..4db5b222d2 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4091,9 +4091,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectPropertiesBlocks.Value.Add(objPropDB); } } + + updatesThisCall++; } + Int32 ppcnt = 0; + Int32 pbcnt = 0; + if (objectPropertiesBlocks.IsValueCreated) { List blocks = objectPropertiesBlocks.Value; @@ -4105,9 +4110,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Header.Zerocoded = true; OutPacket(packet, ThrottleOutPacketType.Task, true); + + pbcnt += blocks.Count; + ppcnt++; } + Int32 fpcnt = 0; + Int32 fbcnt = 0; + if (objectFamilyBlocks.IsValueCreated) { List blocks = objectFamilyBlocks.Value; @@ -4130,10 +4141,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.ObjectData = blocks[i]; packet.Header.Zerocoded = true; OutPacket(packet, ThrottleOutPacketType.Task); + + fpcnt++; + fbcnt++; } } + m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); + m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); } private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) @@ -11401,6 +11417,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, throttlePacketType, true); } +/// + Dictionary pktsrc = new Dictionary(); + uint pktcnt = 0; +/// + /// /// This is the starting point for sending a simulator packet out to the client /// @@ -11448,7 +11469,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (count > 5) break; } - // m_log.WarnFormat("[BADGUY] {0}", stack); + lock(pktsrc) + { + if (! pktsrc.ContainsKey(stack)) + pktsrc.Add(stack,0); + pktsrc[stack]++; + + if (++pktcnt > 500) + { + pktcnt = 0; + m_log.WarnFormat("[PACKETCOUNTS] START"); + foreach (KeyValuePair pkt in pktsrc) + m_log.WarnFormat("[PACKETCOUNTS] {0,8}, {1}", pkt.Value, pkt.Key); + pktsrc.Clear(); + m_log.WarnFormat("[PACKETCOUNTS] END"); + } + } } m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); From 78c04d61caed7dc62eb299a09148934d95a1c882 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 15:40:57 -0700 Subject: [PATCH 042/103] Fixed the update of items in the priority queue to enable both types of property updates to be specified. Not sure if one form of property update should supercede another. But for now the old OpenSim behavior is preserved by sending both. --- OpenSim/Framework/IClientAPI.cs | 5 +++++ .../ClientStack/LindenUDP/LLClientView.cs | 21 +++++++++++++------ .../ClientStack/LindenUDP/PriorityQueue.cs | 2 +- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index c56a756314..f573c32d07 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -575,6 +575,11 @@ namespace OpenSim.Framework public ISceneEntity Entity; public uint Flags; + public virtual void Update(IEntityUpdate update) + { + this.Flags |= update.Flags; + } + public IEntityUpdate(ISceneEntity entity, uint flags) { Entity = entity; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 4db5b222d2..92a76acba6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4032,26 +4032,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP private class ObjectPropertyUpdate : IEntityUpdate { internal bool SendFamilyProps; + internal bool SendObjectProps; - public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam) + public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam, bool sendobj) : base(entity,flags) { SendFamilyProps = sendfam; + SendObjectProps = sendobj; + } + public void Update(ObjectPropertyUpdate update) + { + SendFamilyProps = SendFamilyProps || update.SendFamilyProps; + SendObjectProps = SendObjectProps || update.SendObjectProps; + Flags |= update.Flags; } } public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) { - uint priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = 0; // time based ordering only lock (m_entityProps.SyncRoot) - m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true)); + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); } public void SendObjectPropertiesReply(ISceneEntity entity) { - uint priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = 0; // time based ordering only lock (m_entityProps.SyncRoot) - m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false)); + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false,true)); } private void ProcessEntityPropertyRequests(int maxUpdates) @@ -4082,7 +4090,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectFamilyBlocks.Value.Add(objPropDB); } } - else + + if (update.SendObjectProps) { if (update.Entity is SceneObjectPart) { diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 6521a00745..b62ec07ed9 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -87,7 +87,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_lookupTable.TryGetValue(localid, out lookup)) { entry = lookup.Heap[lookup.Handle].EntryOrder; - value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; + value.Update(lookup.Heap[lookup.Handle].Value); lookup.Heap.Remove(lookup.Handle); } From b84ad81740ea1c732711b7f555d44c56b3858178 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 15:55:21 -0700 Subject: [PATCH 043/103] remove packet monitoring debugging code --- .../ClientStack/LindenUDP/LLClientView.cs | 118 +----------------- 1 file changed, 2 insertions(+), 116 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 92a76acba6..cf04f0d809 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -300,77 +300,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Used to adjust Sun Orbit values so Linden based viewers properly position sun private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; - // First log file or time has expired, start writing to a new log file -// -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// THIS IS DEBUGGING CODE & SHOULD BE REMOVED -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- - public class QueueLogger - { - public Int32 start = 0; - public StreamWriter Log = null; - private Dictionary m_idMap = new Dictionary(); - - public QueueLogger() - { - DateTime now = DateTime.Now; - String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss")); - Log = new StreamWriter(fname); - - start = Util.EnvironmentTickCount(); - } - - public int LookupID(UUID uuid) - { - int localid; - if (! m_idMap.TryGetValue(uuid,out localid)) - { - localid = m_idMap.Count + 1; - m_idMap[uuid] = localid; - } - - return localid; - } - } - - public static QueueLogger QueueLog = null; - - // ----------------------------------------------------------------- - public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - int aid = QueueLog.LookupID(avatar); - QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue); - } - } - - // ----------------------------------------------------------------- - public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString()); - } - } -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected static Dictionary PacketHandlers = new Dictionary(); //Global/static handlers for all clients @@ -4025,10 +3954,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } -/// ----------------------------------------------------------------- -/// -/// ----------------------------------------------------------------- - private class ObjectPropertyUpdate : IEntityUpdate { internal bool SendFamilyProps; @@ -4157,8 +4082,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } - m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); - m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); + // m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); + // m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); } private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) @@ -4232,10 +4157,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return block; } -/// ----------------------------------------------------------------- -/// -/// ----------------------------------------------------------------- - #region Estate Data Sending Methods private static bool convertParamStringToBool(byte[] field) @@ -11426,11 +11347,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, throttlePacketType, true); } -/// - Dictionary pktsrc = new Dictionary(); - uint pktcnt = 0; -/// - /// /// This is the starting point for sending a simulator packet out to the client /// @@ -11465,36 +11381,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (logPacket) m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } - - if (throttlePacketType == ThrottleOutPacketType.Task) - { - System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); // get call stack - System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) - - string stack = ""; - for (int count = 1; count < stackFrames.Length; count++) - { - stack += (stack == "" ? "" : ",") + stackFrames[count].GetMethod().Name; - if (count > 5) break; - } - - lock(pktsrc) - { - if (! pktsrc.ContainsKey(stack)) - pktsrc.Add(stack,0); - pktsrc[stack]++; - - if (++pktcnt > 500) - { - pktcnt = 0; - m_log.WarnFormat("[PACKETCOUNTS] START"); - foreach (KeyValuePair pkt in pktsrc) - m_log.WarnFormat("[PACKETCOUNTS] {0,8}, {1}", pkt.Value, pkt.Key); - pktsrc.Clear(); - m_log.WarnFormat("[PACKETCOUNTS] END"); - } - } - } m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); } From 5c870fce548eeca24ae0491a2261ce4fb330a57b Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 12 Apr 2011 19:45:33 -0700 Subject: [PATCH 044/103] Bug fix in object serialization -- sculpt data was wrong. --- .../Framework/Scenes/Serialization/SceneObjectSerializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs index 57ae4fd24f..b412e25579 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs @@ -1318,7 +1318,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization writer.WriteStartElement("SculptData"); byte[] sd; if (shp.SculptData != null) - sd = shp.ExtraParams; + sd = shp.SculptData; else sd = Utils.EmptyBytes; writer.WriteBase64(sd, 0, sd.Length); From 16c911dcbb883e60d7a49176db8b2cea650cc929 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 12 Apr 2011 19:46:27 -0700 Subject: [PATCH 045/103] Implemented terrain save-tile command. Does the opposite of load-tile. For now, only saves to .png. --- .../FileLoaders/GenericSystemDrawing.cs | 46 ++++++++++++++++ .../World/Terrain/FileLoaders/JPEG.cs | 8 +++ .../World/Terrain/FileLoaders/LLRAW.cs | 8 +++ .../World/Terrain/FileLoaders/RAW32.cs | 7 +++ .../World/Terrain/FileLoaders/Terragen.cs | 8 +++ .../World/Terrain/ITerrainLoader.cs | 1 + .../World/Terrain/TerrainModule.cs | 54 +++++++++++++++++++ 7 files changed, 132 insertions(+) diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs index d6fa0937e5..21a9999ba0 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs @@ -124,6 +124,52 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders colours.Save(stream, ImageFormat.Png); } + public virtual void SaveFile(ITerrainChannel m_channel, string filename, + int offsetX, int offsetY, + int fileWidth, int fileHeight, + int regionSizeX, int regionSizeY) + + { + // We need to do this because: + // "Saving the image to the same file it was constructed from is not allowed and throws an exception." + string tempName = offsetX + "_ " + offsetY + "_" + filename; + + Bitmap entireBitmap = null; + Bitmap thisBitmap = null; + if (File.Exists(filename)) + { + File.Copy(filename, tempName); + entireBitmap = new Bitmap(tempName); + if (entireBitmap.Width != fileWidth * regionSizeX || entireBitmap.Height != fileHeight * regionSizeY) + { + // old file, let's overwrite it + entireBitmap = new Bitmap(fileWidth * regionSizeX, fileHeight * regionSizeY); + } + } + else + { + entireBitmap = new Bitmap(fileWidth * regionSizeX, fileHeight * regionSizeY); + } + + thisBitmap = CreateGrayscaleBitmapFromMap(m_channel); + Console.WriteLine("offsetX=" + offsetX + " offsetY=" + offsetY); + for (int x = 0; x < regionSizeX; x++) + for (int y = 0; y < regionSizeY; y++) + entireBitmap.SetPixel(x + offsetX * regionSizeX, y + (fileHeight - 1 - offsetY) * regionSizeY, thisBitmap.GetPixel(x, y)); + + Save(entireBitmap, filename); + thisBitmap.Dispose(); + entireBitmap.Dispose(); + + if (File.Exists(tempName)) + File.Delete(tempName); + } + + protected virtual void Save(Bitmap bmp, string filename) + { + bmp.Save(filename, ImageFormat.Png); + } + #endregion public override string ToString() diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/JPEG.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/JPEG.cs index 8667607d0b..1a0d8ecf60 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/JPEG.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/JPEG.cs @@ -76,6 +76,14 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders colours.Save(stream, ImageFormat.Jpeg); } + public virtual void SaveFile(ITerrainChannel m_channel, string filename, + int offsetX, int offsetY, + int fileWidth, int fileHeight, + int regionSizeX, int regionSizeY) + { + throw new System.Exception("Not Implemented"); + } + #endregion public override string ToString() diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs index a70ef13ed0..fad7641e42 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs @@ -240,6 +240,14 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders get { return ".raw"; } } + public virtual void SaveFile(ITerrainChannel m_channel, string filename, + int offsetX, int offsetY, + int fileWidth, int fileHeight, + int regionSizeX, int regionSizeY) + { + throw new System.Exception("Not Implemented"); + } + #endregion public override string ToString() diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs index 3c76665d5e..ba073cae5a 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs @@ -160,6 +160,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders bs.Close(); } + public virtual void SaveFile(ITerrainChannel m_channel, string filename, + int offsetX, int offsetY, + int fileWidth, int fileHeight, + int regionSizeX, int regionSizeY) + { + throw new System.Exception("Not Implemented"); + } #endregion public override string ToString() diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs index 2919897153..2f37d9d7e9 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs @@ -308,6 +308,14 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders get { return ".ter"; } } + public virtual void SaveFile(ITerrainChannel m_channel, string filename, + int offsetX, int offsetY, + int fileWidth, int fileHeight, + int regionSizeX, int regionSizeY) + { + throw new System.Exception("Not Implemented"); + } + #endregion public override string ToString() diff --git a/OpenSim/Region/CoreModules/World/Terrain/ITerrainLoader.cs b/OpenSim/Region/CoreModules/World/Terrain/ITerrainLoader.cs index 7403281621..7237f90a38 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/ITerrainLoader.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/ITerrainLoader.cs @@ -38,5 +38,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain ITerrainChannel LoadStream(Stream stream); void SaveFile(string filename, ITerrainChannel map); void SaveStream(Stream stream, ITerrainChannel map); + void SaveFile(ITerrainChannel map, string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int regionSizeX, int regionSizeY); } } \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs index 8a79d78b22..9c7b2fab9c 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs @@ -540,6 +540,39 @@ namespace OpenSim.Region.CoreModules.World.Terrain } } + /// + /// Saves the terrain to a larger terrain file. + /// + /// The terrain file to save + /// The width of the file in units + /// The height of the file in units + /// Where to begin our slice + /// Where to begin our slice + public void SaveToFile(string filename, int fileWidth, int fileHeight, int fileStartX, int fileStartY) + { + int offsetX = (int)m_scene.RegionInfo.RegionLocX - fileStartX; + int offsetY = (int)m_scene.RegionInfo.RegionLocY - fileStartY; + + if (offsetX >= 0 && offsetX < fileWidth && offsetY >= 0 && offsetY < fileHeight) + { + // this region is included in the tile request + foreach (KeyValuePair loader in m_loaders) + { + if (filename.EndsWith(loader.Key)) + { + lock (m_scene) + { + loader.Value.SaveFile(m_channel, filename, offsetX, offsetY, + fileWidth, fileHeight, + (int)Constants.RegionSize, + (int)Constants.RegionSize); + } + return; + } + } + } + } + /// /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections /// @@ -860,6 +893,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain SaveToFile((string) args[0]); } + private void InterfaceSaveTileFile(Object[] args) + { + SaveToFile((string)args[0], + (int)args[1], + (int)args[2], + (int)args[3], + (int)args[4]); + } + private void InterfaceBakeTerrain(Object[] args) { UpdateRevertMap(); @@ -1115,6 +1157,17 @@ namespace OpenSim.Region.CoreModules.World.Terrain loadFromTileCommand.AddArgument("minimum Y tile", "The Y region coordinate of the first section on the file", "Integer"); + Command saveToTileCommand = + new Command("save-tile", CommandIntentions.COMMAND_HAZARDOUS, InterfaceSaveTileFile, "Saves the current heightmap to the larger file."); + saveToTileCommand.AddArgument("filename", + "The file you wish to save to, the file extension determines the loader to be used. Supported extensions include: " + + supportedFileExtensions, "String"); + saveToTileCommand.AddArgument("file width", "The width of the file in tiles", "Integer"); + saveToTileCommand.AddArgument("file height", "The height of the file in tiles", "Integer"); + saveToTileCommand.AddArgument("minimum X tile", "The X region coordinate of the first section on the file", + "Integer"); + saveToTileCommand.AddArgument("minimum Y tile", "The Y region coordinate of the first section on the file", + "Integer"); // Terrain adjustments Command fillRegionCommand = new Command("fill", CommandIntentions.COMMAND_HAZARDOUS, InterfaceFillTerrain, "Fills the current heightmap with a specified value."); @@ -1166,6 +1219,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_commander.RegisterCommand("load", loadFromFileCommand); m_commander.RegisterCommand("load-tile", loadFromTileCommand); m_commander.RegisterCommand("save", saveToFileCommand); + m_commander.RegisterCommand("save-tile", saveToTileCommand); m_commander.RegisterCommand("fill", fillRegionCommand); m_commander.RegisterCommand("elevate", elevateCommand); m_commander.RegisterCommand("lower", lowerCommand); From 7592a033df4f81ba985e1d6f57ad0ef488dc5745 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 12 Apr 2011 20:24:28 -0700 Subject: [PATCH 046/103] Moved 3 request handlers from OpenSim.Framework.Servers.HttpServer up to OpenSim.Framework -- just pasted them in WebUtil. This is so that code that uses the Service connectors don't need to include the HttpServer dll -- that was odd. --- .../AsynchronousRestObjectRequester.cs | 192 ---------- .../SynchronousRestFormsRequester.cs | 131 ------- .../SynchronousRestObjectRequester.cs | 122 ------- OpenSim/Framework/WebUtil.cs | 335 ++++++++++++++++++ .../Avatar/Friends/FriendsModule.cs | 2 +- .../InstantMessage/OfflineMessageModule.cs | 1 - .../Asset/AssetServiceInConnectorModule.cs | 1 - .../AuthenticationServiceInConnectorModule.cs | 1 - .../Grid/GridInfoServiceInConnectorModule.cs | 1 - .../HypergridServiceInConnectorModule.cs | 1 - .../InventoryServiceInConnectorModule.cs | 3 +- .../Land/LandServiceInConnectorModule.cs | 1 - .../Login/LLLoginServiceInConnectorModule.cs | 1 - .../NeighbourServiceInConnectorModule.cs | 1 - .../SimulationServiceInConnectorModule.cs | 1 - .../Connectors/Asset/AssetServiceConnector.cs | 1 - .../AuthenticationServiceConnector.cs | 1 - .../AuthorizationServiceConnector.cs | 1 - .../Avatar/AvatarServiceConnector.cs | 1 - .../Freeswitch/RemoteFreeswitchConnector.cs | 1 - .../Friends/FriendsServiceConnector.cs | 1 - .../Connectors/Friends/FriendsSimConnector.cs | 2 +- .../Connectors/Grid/GridServiceConnector.cs | 1 - .../GridUser/GridUserServiceConnector.cs | 1 - .../Inventory/XInventoryConnector.cs | 1 - .../Connectors/Land/LandServiceConnector.cs | 1 - .../Neighbour/NeighbourServiceConnector.cs | 1 - .../Presence/PresenceServiceConnector.cs | 1 - .../UserAccountServiceConnector.cs | 1 - prebuild.xml | 1 - 30 files changed, 338 insertions(+), 472 deletions(-) delete mode 100644 OpenSim/Framework/Servers/HttpServer/AsynchronousRestObjectRequester.cs delete mode 100644 OpenSim/Framework/Servers/HttpServer/SynchronousRestFormsRequester.cs delete mode 100644 OpenSim/Framework/Servers/HttpServer/SynchronousRestObjectRequester.cs diff --git a/OpenSim/Framework/Servers/HttpServer/AsynchronousRestObjectRequester.cs b/OpenSim/Framework/Servers/HttpServer/AsynchronousRestObjectRequester.cs deleted file mode 100644 index 03c12dd560..0000000000 --- a/OpenSim/Framework/Servers/HttpServer/AsynchronousRestObjectRequester.cs +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.IO; -using System.Net; -using System.Reflection; -using System.Text; -using System.Xml; -using System.Xml.Serialization; -using log4net; - -namespace OpenSim.Framework.Servers.HttpServer -{ - public class AsynchronousRestObjectRequester - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - /// - /// Perform an asynchronous REST request. - /// - /// GET or POST - /// - /// - /// - /// - /// - /// Thrown if we encounter a - /// network issue while posting the request. You'll want to make - /// sure you deal with this as they're not uncommon - // - public static void MakeRequest(string verb, - string requestUrl, TRequest obj, Action action) - { -// m_log.DebugFormat("[ASYNC REQUEST]: Starting {0} {1}", verb, requestUrl); - - Type type = typeof (TRequest); - - WebRequest request = WebRequest.Create(requestUrl); - WebResponse response = null; - TResponse deserial = default(TResponse); - XmlSerializer deserializer = new XmlSerializer(typeof (TResponse)); - - request.Method = verb; - - if (verb == "POST") - { - request.ContentType = "text/xml"; - - MemoryStream buffer = new MemoryStream(); - - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Encoding = Encoding.UTF8; - - using (XmlWriter writer = XmlWriter.Create(buffer, settings)) - { - XmlSerializer serializer = new XmlSerializer(type); - serializer.Serialize(writer, obj); - writer.Flush(); - } - - int length = (int) buffer.Length; - request.ContentLength = length; - - request.BeginGetRequestStream(delegate(IAsyncResult res) - { - Stream requestStream = request.EndGetRequestStream(res); - - requestStream.Write(buffer.ToArray(), 0, length); - requestStream.Close(); - - request.BeginGetResponse(delegate(IAsyncResult ar) - { - response = request.EndGetResponse(ar); - Stream respStream = null; - try - { - respStream = response.GetResponseStream(); - deserial = (TResponse)deserializer.Deserialize( - respStream); - } - catch (System.InvalidOperationException) - { - } - finally - { - // Let's not close this - //buffer.Close(); - respStream.Close(); - response.Close(); - } - - action(deserial); - - }, null); - }, null); - - - return; - } - - request.BeginGetResponse(delegate(IAsyncResult res2) - { - try - { - // If the server returns a 404, this appears to trigger a System.Net.WebException even though that isn't - // documented in MSDN - response = request.EndGetResponse(res2); - - Stream respStream = null; - try - { - respStream = response.GetResponseStream(); - deserial = (TResponse)deserializer.Deserialize(respStream); - } - catch (System.InvalidOperationException) - { - } - finally - { - respStream.Close(); - response.Close(); - } - } - catch (WebException e) - { - if (e.Status == WebExceptionStatus.ProtocolError) - { - if (e.Response is HttpWebResponse) - { - HttpWebResponse httpResponse = (HttpWebResponse)e.Response; - - if (httpResponse.StatusCode != HttpStatusCode.NotFound) - { - // We don't appear to be handling any other status codes, so log these feailures to that - // people don't spend unnecessary hours hunting phantom bugs. - m_log.DebugFormat( - "[ASYNC REQUEST]: Request {0} {1} failed with unexpected status code {2}", - verb, requestUrl, httpResponse.StatusCode); - } - } - } - else - { - m_log.ErrorFormat("[ASYNC REQUEST]: Request {0} {1} failed with status {2} and message {3}", verb, requestUrl, e.Status, e.Message); - } - } - catch (Exception e) - { - m_log.ErrorFormat("[ASYNC REQUEST]: Request {0} {1} failed with exception {2}", verb, requestUrl, e); - } - - // m_log.DebugFormat("[ASYNC REQUEST]: Received {0}", deserial.ToString()); - - try - { - action(deserial); - } - catch (Exception e) - { - m_log.ErrorFormat( - "[ASYNC REQUEST]: Request {0} {1} callback failed with exception {2}", verb, requestUrl, e); - } - - }, null); - } - } -} diff --git a/OpenSim/Framework/Servers/HttpServer/SynchronousRestFormsRequester.cs b/OpenSim/Framework/Servers/HttpServer/SynchronousRestFormsRequester.cs deleted file mode 100644 index 41ece86047..0000000000 --- a/OpenSim/Framework/Servers/HttpServer/SynchronousRestFormsRequester.cs +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.IO; -using System.Net; -using System.Reflection; -using System.Text; -using System.Xml; -using System.Xml.Serialization; - -using log4net; - -namespace OpenSim.Framework.Servers.HttpServer -{ - public class SynchronousRestFormsRequester - { - private static readonly ILog m_log = - LogManager.GetLogger( - MethodBase.GetCurrentMethod().DeclaringType); - - /// - /// Perform a synchronous REST request. - /// - /// - /// - /// - /// - /// - /// Thrown if we encounter a network issue while posting - /// the request. You'll want to make sure you deal with this as they're not uncommon - public static string MakeRequest(string verb, string requestUrl, string obj) - { - WebRequest request = WebRequest.Create(requestUrl); - request.Method = verb; - string respstring = String.Empty; - - using (MemoryStream buffer = new MemoryStream()) - { - if ((verb == "POST") || (verb == "PUT")) - { - request.ContentType = "text/www-form-urlencoded"; - - int length = 0; - using (StreamWriter writer = new StreamWriter(buffer)) - { - writer.Write(obj); - writer.Flush(); - } - - length = (int)obj.Length; - request.ContentLength = length; - - Stream requestStream = null; - try - { - requestStream = request.GetRequestStream(); - requestStream.Write(buffer.ToArray(), 0, length); - } - catch (Exception e) - { - m_log.DebugFormat("[FORMS]: exception occured on sending request to {0}: " + e.ToString(), requestUrl); - } - finally - { - if (requestStream != null) - requestStream.Close(); - } - } - - try - { - using (WebResponse resp = request.GetResponse()) - { - if (resp.ContentLength != 0) - { - Stream respStream = null; - try - { - respStream = resp.GetResponseStream(); - using (StreamReader reader = new StreamReader(respStream)) - { - respstring = reader.ReadToEnd(); - } - } - catch (Exception e) - { - m_log.DebugFormat("[FORMS]: exception occured on receiving reply " + e.ToString()); - } - finally - { - if (respStream != null) - respStream.Close(); - } - } - } - } - catch (System.InvalidOperationException) - { - // This is what happens when there is invalid XML - m_log.DebugFormat("[FORMS]: InvalidOperationException on receiving request"); - } - } - return respstring; - } - } -} diff --git a/OpenSim/Framework/Servers/HttpServer/SynchronousRestObjectRequester.cs b/OpenSim/Framework/Servers/HttpServer/SynchronousRestObjectRequester.cs deleted file mode 100644 index eab463cbd8..0000000000 --- a/OpenSim/Framework/Servers/HttpServer/SynchronousRestObjectRequester.cs +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.IO; -using System.Net; -using System.Text; -using System.Xml; -using System.Xml.Serialization; - -namespace OpenSim.Framework.Servers.HttpServer -{ - public class SynchronousRestObjectPoster - { - [Obsolete] - public static TResponse BeginPostObject(string verb, string requestUrl, TRequest obj) - { - return SynchronousRestObjectRequester.MakeRequest(verb, requestUrl, obj); - } - } - - public class SynchronousRestObjectRequester - { - /// - /// Perform a synchronous REST request. - /// - /// - /// - /// - /// - /// - /// Thrown if we encounter a network issue while posting - /// the request. You'll want to make sure you deal with this as they're not uncommon - public static TResponse MakeRequest(string verb, string requestUrl, TRequest obj) - { - Type type = typeof (TRequest); - TResponse deserial = default(TResponse); - - WebRequest request = WebRequest.Create(requestUrl); - request.Method = verb; - - if ((verb == "POST") || (verb == "PUT")) - { - request.ContentType = "text/xml"; - - MemoryStream buffer = new MemoryStream(); - - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Encoding = Encoding.UTF8; - - using (XmlWriter writer = XmlWriter.Create(buffer, settings)) - { - XmlSerializer serializer = new XmlSerializer(type); - serializer.Serialize(writer, obj); - writer.Flush(); - } - - int length = (int) buffer.Length; - request.ContentLength = length; - - Stream requestStream = null; - try - { - requestStream = request.GetRequestStream(); - requestStream.Write(buffer.ToArray(), 0, length); - } - catch (Exception) - { - return deserial; - } - finally - { - if (requestStream != null) - requestStream.Close(); - } - } - - try - { - using (WebResponse resp = request.GetResponse()) - { - if (resp.ContentLength > 0) - { - Stream respStream = resp.GetResponseStream(); - XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); - deserial = (TResponse)deserializer.Deserialize(respStream); - respStream.Close(); - } - } - } - catch (System.InvalidOperationException) - { - // This is what happens when there is invalid XML - } - return deserial; - } - } -} diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs index 1feeeb31e5..64cd014f5c 100644 --- a/OpenSim/Framework/WebUtil.cs +++ b/OpenSim/Framework/WebUtil.cs @@ -36,6 +36,9 @@ using System.Net.Security; using System.Reflection; using System.Text; using System.Web; +using System.Xml; +using System.Xml.Serialization; + using log4net; using OpenSim.Framework.Servers.HttpServer; using OpenMetaverse.StructuredData; @@ -625,4 +628,336 @@ namespace OpenSim.Framework } + + public static class AsynchronousRestObjectRequester + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Perform an asynchronous REST request. + /// + /// GET or POST + /// + /// + /// + /// + /// + /// Thrown if we encounter a + /// network issue while posting the request. You'll want to make + /// sure you deal with this as they're not uncommon + // + public static void MakeRequest(string verb, + string requestUrl, TRequest obj, Action action) + { + // m_log.DebugFormat("[ASYNC REQUEST]: Starting {0} {1}", verb, requestUrl); + + Type type = typeof(TRequest); + + WebRequest request = WebRequest.Create(requestUrl); + WebResponse response = null; + TResponse deserial = default(TResponse); + XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); + + request.Method = verb; + + if (verb == "POST") + { + request.ContentType = "text/xml"; + + MemoryStream buffer = new MemoryStream(); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, obj); + writer.Flush(); + } + + int length = (int)buffer.Length; + request.ContentLength = length; + + request.BeginGetRequestStream(delegate(IAsyncResult res) + { + Stream requestStream = request.EndGetRequestStream(res); + + requestStream.Write(buffer.ToArray(), 0, length); + requestStream.Close(); + + request.BeginGetResponse(delegate(IAsyncResult ar) + { + response = request.EndGetResponse(ar); + Stream respStream = null; + try + { + respStream = response.GetResponseStream(); + deserial = (TResponse)deserializer.Deserialize( + respStream); + } + catch (System.InvalidOperationException) + { + } + finally + { + // Let's not close this + //buffer.Close(); + respStream.Close(); + response.Close(); + } + + action(deserial); + + }, null); + }, null); + + + return; + } + + request.BeginGetResponse(delegate(IAsyncResult res2) + { + try + { + // If the server returns a 404, this appears to trigger a System.Net.WebException even though that isn't + // documented in MSDN + response = request.EndGetResponse(res2); + + Stream respStream = null; + try + { + respStream = response.GetResponseStream(); + deserial = (TResponse)deserializer.Deserialize(respStream); + } + catch (System.InvalidOperationException) + { + } + finally + { + respStream.Close(); + response.Close(); + } + } + catch (WebException e) + { + if (e.Status == WebExceptionStatus.ProtocolError) + { + if (e.Response is HttpWebResponse) + { + HttpWebResponse httpResponse = (HttpWebResponse)e.Response; + + if (httpResponse.StatusCode != HttpStatusCode.NotFound) + { + // We don't appear to be handling any other status codes, so log these feailures to that + // people don't spend unnecessary hours hunting phantom bugs. + m_log.DebugFormat( + "[ASYNC REQUEST]: Request {0} {1} failed with unexpected status code {2}", + verb, requestUrl, httpResponse.StatusCode); + } + } + } + else + { + m_log.ErrorFormat("[ASYNC REQUEST]: Request {0} {1} failed with status {2} and message {3}", verb, requestUrl, e.Status, e.Message); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[ASYNC REQUEST]: Request {0} {1} failed with exception {2}", verb, requestUrl, e); + } + + // m_log.DebugFormat("[ASYNC REQUEST]: Received {0}", deserial.ToString()); + + try + { + action(deserial); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[ASYNC REQUEST]: Request {0} {1} callback failed with exception {2}", verb, requestUrl, e); + } + + }, null); + } + } + + public static class SynchronousRestFormsRequester + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Perform a synchronous REST request. + /// + /// + /// + /// + /// + /// + /// Thrown if we encounter a network issue while posting + /// the request. You'll want to make sure you deal with this as they're not uncommon + public static string MakeRequest(string verb, string requestUrl, string obj) + { + WebRequest request = WebRequest.Create(requestUrl); + request.Method = verb; + string respstring = String.Empty; + + using (MemoryStream buffer = new MemoryStream()) + { + if ((verb == "POST") || (verb == "PUT")) + { + request.ContentType = "text/www-form-urlencoded"; + + int length = 0; + using (StreamWriter writer = new StreamWriter(buffer)) + { + writer.Write(obj); + writer.Flush(); + } + + length = (int)obj.Length; + request.ContentLength = length; + + Stream requestStream = null; + try + { + requestStream = request.GetRequestStream(); + requestStream.Write(buffer.ToArray(), 0, length); + } + catch (Exception e) + { + m_log.DebugFormat("[FORMS]: exception occured on sending request to {0}: " + e.ToString(), requestUrl); + } + finally + { + if (requestStream != null) + requestStream.Close(); + } + } + + try + { + using (WebResponse resp = request.GetResponse()) + { + if (resp.ContentLength != 0) + { + Stream respStream = null; + try + { + respStream = resp.GetResponseStream(); + using (StreamReader reader = new StreamReader(respStream)) + { + respstring = reader.ReadToEnd(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[FORMS]: exception occured on receiving reply " + e.ToString()); + } + finally + { + if (respStream != null) + respStream.Close(); + } + } + } + } + catch (System.InvalidOperationException) + { + // This is what happens when there is invalid XML + m_log.DebugFormat("[FORMS]: InvalidOperationException on receiving request"); + } + } + return respstring; + } + } + + public class SynchronousRestObjectPoster + { + [Obsolete] + public static TResponse BeginPostObject(string verb, string requestUrl, TRequest obj) + { + return SynchronousRestObjectRequester.MakeRequest(verb, requestUrl, obj); + } + } + + public class SynchronousRestObjectRequester + { + /// + /// Perform a synchronous REST request. + /// + /// + /// + /// + /// + /// + /// Thrown if we encounter a network issue while posting + /// the request. You'll want to make sure you deal with this as they're not uncommon + public static TResponse MakeRequest(string verb, string requestUrl, TRequest obj) + { + Type type = typeof(TRequest); + TResponse deserial = default(TResponse); + + WebRequest request = WebRequest.Create(requestUrl); + request.Method = verb; + + if ((verb == "POST") || (verb == "PUT")) + { + request.ContentType = "text/xml"; + + MemoryStream buffer = new MemoryStream(); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, obj); + writer.Flush(); + } + + int length = (int)buffer.Length; + request.ContentLength = length; + + Stream requestStream = null; + try + { + requestStream = request.GetRequestStream(); + requestStream.Write(buffer.ToArray(), 0, length); + } + catch (Exception) + { + return deserial; + } + finally + { + if (requestStream != null) + requestStream.Close(); + } + } + + try + { + using (WebResponse resp = request.GetResponse()) + { + if (resp.ContentLength > 0) + { + Stream respStream = resp.GetResponseStream(); + XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); + deserial = (TResponse)deserializer.Deserialize(respStream); + respStream.Close(); + } + } + } + catch (System.InvalidOperationException) + { + // This is what happens when there is invalid XML + } + return deserial; + } + } } diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs index 4d74b2a0a0..5baf0785f4 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs @@ -34,13 +34,13 @@ using Nini.Config; using Nwc.XmlRpc; using OpenMetaverse; using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; using OpenSim.Framework.Communications; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Services.Interfaces; using OpenSim.Services.Connectors.Friends; using OpenSim.Server.Base; -using OpenSim.Framework.Servers.HttpServer; using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; using GridRegion = OpenSim.Services.Interfaces.GridRegion; diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs index fdfcd104c0..919ea338f4 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs @@ -33,7 +33,6 @@ using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Communications; using OpenSim.Framework.Servers; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Framework.Client; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Asset/AssetServiceInConnectorModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Asset/AssetServiceInConnectorModule.cs index e25700d1f7..422f394d7a 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Asset/AssetServiceInConnectorModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Asset/AssetServiceInConnectorModule.cs @@ -31,7 +31,6 @@ using System.Collections.Generic; using log4net; using Nini.Config; using OpenSim.Framework; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Interfaces; using OpenSim.Server.Base; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Authentication/AuthenticationServiceInConnectorModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Authentication/AuthenticationServiceInConnectorModule.cs index 02acddc37e..2b5beba374 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Authentication/AuthenticationServiceInConnectorModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Authentication/AuthenticationServiceInConnectorModule.cs @@ -31,7 +31,6 @@ using System.Collections.Generic; using log4net; using Nini.Config; using OpenSim.Framework; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Interfaces; using OpenSim.Server.Base; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Grid/GridInfoServiceInConnectorModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Grid/GridInfoServiceInConnectorModule.cs index 6d975afb14..f29c074206 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Grid/GridInfoServiceInConnectorModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Grid/GridInfoServiceInConnectorModule.cs @@ -31,7 +31,6 @@ using System.Collections.Generic; using log4net; using Nini.Config; using OpenSim.Framework; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Interfaces; using OpenSim.Server.Base; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs index 2f96bcb1ab..d2343c994e 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs @@ -31,7 +31,6 @@ using System.Collections.Generic; using log4net; using Nini.Config; using OpenSim.Framework; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Interfaces; using OpenSim.Server.Base; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Inventory/InventoryServiceInConnectorModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Inventory/InventoryServiceInConnectorModule.cs index 209cf0d3c2..53a8ace14c 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Inventory/InventoryServiceInConnectorModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Inventory/InventoryServiceInConnectorModule.cs @@ -31,9 +31,8 @@ using System.Collections.Generic; using log4net; using Nini.Config; using OpenSim.Framework; -using OpenSim.Framework.Servers.HttpServer; -using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; using OpenSim.Server.Base; using OpenSim.Server.Handlers.Base; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Land/LandServiceInConnectorModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Land/LandServiceInConnectorModule.cs index fcc69e949f..fc642032b5 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Land/LandServiceInConnectorModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Land/LandServiceInConnectorModule.cs @@ -31,7 +31,6 @@ using System.Collections.Generic; using log4net; using Nini.Config; using OpenSim.Framework; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Interfaces; using OpenSim.Server.Base; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Login/LLLoginServiceInConnectorModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Login/LLLoginServiceInConnectorModule.cs index 2a9366c44e..f759470b28 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Login/LLLoginServiceInConnectorModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Login/LLLoginServiceInConnectorModule.cs @@ -31,7 +31,6 @@ using System.Collections.Generic; using log4net; using Nini.Config; using OpenSim.Framework; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Interfaces; using OpenSim.Server.Base; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Neighbour/NeighbourServiceInConnectorModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Neighbour/NeighbourServiceInConnectorModule.cs index 35518d59f8..5c3263260e 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Neighbour/NeighbourServiceInConnectorModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Neighbour/NeighbourServiceInConnectorModule.cs @@ -31,7 +31,6 @@ using System.Collections.Generic; using log4net; using Nini.Config; using OpenSim.Framework; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Interfaces; using OpenSim.Server.Base; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Simulation/SimulationServiceInConnectorModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Simulation/SimulationServiceInConnectorModule.cs index 5ee1c97e5f..86b4926ac5 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Simulation/SimulationServiceInConnectorModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Simulation/SimulationServiceInConnectorModule.cs @@ -31,7 +31,6 @@ using System.Collections.Generic; using log4net; using Nini.Config; using OpenSim.Framework; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Interfaces; using OpenSim.Server.Base; diff --git a/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs b/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs index 65b3537041..f1da4fa777 100644 --- a/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs +++ b/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs @@ -34,7 +34,6 @@ using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Console; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using OpenMetaverse; diff --git a/OpenSim/Services/Connectors/Authentication/AuthenticationServiceConnector.cs b/OpenSim/Services/Connectors/Authentication/AuthenticationServiceConnector.cs index 6f77a2d8b3..c04e7a4971 100644 --- a/OpenSim/Services/Connectors/Authentication/AuthenticationServiceConnector.cs +++ b/OpenSim/Services/Connectors/Authentication/AuthenticationServiceConnector.cs @@ -33,7 +33,6 @@ using System.Reflection; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using OpenSim.Server.Base; using OpenMetaverse; diff --git a/OpenSim/Services/Connectors/Authorization/AuthorizationServiceConnector.cs b/OpenSim/Services/Connectors/Authorization/AuthorizationServiceConnector.cs index 4eb4bd2470..35b7109496 100644 --- a/OpenSim/Services/Connectors/Authorization/AuthorizationServiceConnector.cs +++ b/OpenSim/Services/Connectors/Authorization/AuthorizationServiceConnector.cs @@ -33,7 +33,6 @@ using System.Reflection; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using OpenMetaverse; diff --git a/OpenSim/Services/Connectors/Avatar/AvatarServiceConnector.cs b/OpenSim/Services/Connectors/Avatar/AvatarServiceConnector.cs index 1cd6bf8e1a..1a93ae71c6 100644 --- a/OpenSim/Services/Connectors/Avatar/AvatarServiceConnector.cs +++ b/OpenSim/Services/Connectors/Avatar/AvatarServiceConnector.cs @@ -33,7 +33,6 @@ using System.Reflection; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using GridRegion = OpenSim.Services.Interfaces.GridRegion; using IAvatarService = OpenSim.Services.Interfaces.IAvatarService; diff --git a/OpenSim/Services/Connectors/Freeswitch/RemoteFreeswitchConnector.cs b/OpenSim/Services/Connectors/Freeswitch/RemoteFreeswitchConnector.cs index c9bba0b730..d68829996d 100644 --- a/OpenSim/Services/Connectors/Freeswitch/RemoteFreeswitchConnector.cs +++ b/OpenSim/Services/Connectors/Freeswitch/RemoteFreeswitchConnector.cs @@ -33,7 +33,6 @@ using System.Reflection; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using OpenSim.Server.Base; using OpenMetaverse; diff --git a/OpenSim/Services/Connectors/Friends/FriendsServiceConnector.cs b/OpenSim/Services/Connectors/Friends/FriendsServiceConnector.cs index 36b5083860..861c4759a3 100644 --- a/OpenSim/Services/Connectors/Friends/FriendsServiceConnector.cs +++ b/OpenSim/Services/Connectors/Friends/FriendsServiceConnector.cs @@ -33,7 +33,6 @@ using System.Reflection; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; using OpenSim.Server.Base; diff --git a/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs index 0a7b277e4c..4ffa68c381 100644 --- a/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs +++ b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs @@ -32,7 +32,7 @@ using System.Reflection; using OpenSim.Services.Interfaces; using GridRegion = OpenSim.Services.Interfaces.GridRegion; using OpenSim.Server.Base; -using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework; using OpenMetaverse; using log4net; diff --git a/OpenSim/Services/Connectors/Grid/GridServiceConnector.cs b/OpenSim/Services/Connectors/Grid/GridServiceConnector.cs index 5092d741e6..e57f28b44e 100644 --- a/OpenSim/Services/Connectors/Grid/GridServiceConnector.cs +++ b/OpenSim/Services/Connectors/Grid/GridServiceConnector.cs @@ -33,7 +33,6 @@ using System.Reflection; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using GridRegion = OpenSim.Services.Interfaces.GridRegion; using OpenSim.Server.Base; diff --git a/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs b/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs index b3ea865996..738cc06949 100644 --- a/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs +++ b/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs @@ -33,7 +33,6 @@ using System.Reflection; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using GridRegion = OpenSim.Services.Interfaces.GridRegion; using OpenSim.Server.Base; diff --git a/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs b/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs index b3bfcc27df..cd9f2bfcff 100644 --- a/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs +++ b/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs @@ -34,7 +34,6 @@ using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Console; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using OpenSim.Server.Base; using OpenMetaverse; diff --git a/OpenSim/Services/Connectors/Land/LandServiceConnector.cs b/OpenSim/Services/Connectors/Land/LandServiceConnector.cs index 252f7a1d16..30a73a471c 100644 --- a/OpenSim/Services/Connectors/Land/LandServiceConnector.cs +++ b/OpenSim/Services/Connectors/Land/LandServiceConnector.cs @@ -34,7 +34,6 @@ using System.Reflection; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using OpenMetaverse; using Nwc.XmlRpc; diff --git a/OpenSim/Services/Connectors/Neighbour/NeighbourServiceConnector.cs b/OpenSim/Services/Connectors/Neighbour/NeighbourServiceConnector.cs index 9e444c49bc..2cae02d42a 100644 --- a/OpenSim/Services/Connectors/Neighbour/NeighbourServiceConnector.cs +++ b/OpenSim/Services/Connectors/Neighbour/NeighbourServiceConnector.cs @@ -36,7 +36,6 @@ using System.Text; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using OpenMetaverse; using OpenMetaverse.StructuredData; diff --git a/OpenSim/Services/Connectors/Presence/PresenceServiceConnector.cs b/OpenSim/Services/Connectors/Presence/PresenceServiceConnector.cs index 41ebeaf05e..7238afc613 100644 --- a/OpenSim/Services/Connectors/Presence/PresenceServiceConnector.cs +++ b/OpenSim/Services/Connectors/Presence/PresenceServiceConnector.cs @@ -33,7 +33,6 @@ using System.Reflection; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; using GridRegion = OpenSim.Services.Interfaces.GridRegion; using OpenSim.Server.Base; diff --git a/OpenSim/Services/Connectors/UserAccounts/UserAccountServiceConnector.cs b/OpenSim/Services/Connectors/UserAccounts/UserAccountServiceConnector.cs index 2a5df83193..f6835b9f2f 100644 --- a/OpenSim/Services/Connectors/UserAccounts/UserAccountServiceConnector.cs +++ b/OpenSim/Services/Connectors/UserAccounts/UserAccountServiceConnector.cs @@ -33,7 +33,6 @@ using System.Reflection; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Communications; -using OpenSim.Framework.Servers.HttpServer; using OpenSim.Server.Base; using OpenSim.Services.Interfaces; using OpenMetaverse; diff --git a/prebuild.xml b/prebuild.xml index 870ebf300c..81716641c6 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -931,7 +931,6 @@ - From 90b810c27ef4fe3da1796842940af13db61fb91d Mon Sep 17 00:00:00 2001 From: dahlia Date: Tue, 12 Apr 2011 22:47:30 -0700 Subject: [PATCH 047/103] force mesh proxy for simple box prims with path cut --- OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETScene.cs | 3 +++ OpenSim/Region/Physics/OdePlugin/OdePlugin.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETScene.cs b/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETScene.cs index 85e34c1e3d..6df213d851 100644 --- a/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETScene.cs +++ b/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETScene.cs @@ -648,6 +648,9 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin if (pbs.ProfileHollow != 0) iPropertiesNotSupportedDefault++; + if ((pbs.PathBegin != 0) || pbs.PathEnd != 0) + iPropertiesNotSupportedDefault++; + if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0)) iPropertiesNotSupportedDefault++; diff --git a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs index eb97f41e3c..a0101af2bb 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs @@ -2528,6 +2528,9 @@ namespace OpenSim.Region.Physics.OdePlugin if (pbs.ProfileHollow != 0) iPropertiesNotSupportedDefault++; + if ((pbs.PathBegin != 0) || pbs.PathEnd != 0) + iPropertiesNotSupportedDefault++; + if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0)) iPropertiesNotSupportedDefault++; From 6613daa82a3bf0d45b0be1b2941646042f9008f4 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 13 Apr 2011 17:46:36 +0100 Subject: [PATCH 048/103] Add a regression test for rezzing a single object into a scene from user inventory --- .../ClientStack/LindenUDP/LLClientView.cs | 9 +- .../Tests/InventoryAccessModuleTests.cs | 133 ++++++++++++++++++ .../World/Estate/EstateManagementModule.cs | 4 + prebuild.xml | 1 + 4 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 8de31d7069..76d7f7964b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4290,6 +4290,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendEstateCovenantInformation(UUID covenant) { +// m_log.DebugFormat("[LLCLIENTVIEW]: Sending estate covenant asset id of {0} to {1}", covenant, Name); + EstateCovenantReplyPacket einfopack = new EstateCovenantReplyPacket(); EstateCovenantReplyPacket.DataBlock edata = new EstateCovenantReplyPacket.DataBlock(); edata.CovenantID = covenant; @@ -4300,8 +4302,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(einfopack, ThrottleOutPacketType.Task); } - public void SendDetailedEstateData(UUID invoice, string estateName, uint estateID, uint parentEstate, uint estateFlags, uint sunPosition, UUID covenant, string abuseEmail, UUID estateOwner) + public void SendDetailedEstateData( + UUID invoice, string estateName, uint estateID, uint parentEstate, uint estateFlags, uint sunPosition, + UUID covenant, string abuseEmail, UUID estateOwner) { +// m_log.DebugFormat( +// "[LLCLIENTVIEW]: Sending detailed estate data to {0} with covenant asset id {1}", Name, covenant); + EstateOwnerMessagePacket packet = new EstateOwnerMessagePacket(); packet.MethodData.Invoice = invoice; packet.AgentData.TransactionID = UUID.Random(); diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs new file mode 100644 index 0000000000..ecf70ac347 --- /dev/null +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs @@ -0,0 +1,133 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Threading; +using Nini.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Framework.Serialization; +using OpenSim.Framework.Serialization.External; +using OpenSim.Framework.Communications; +using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; +using OpenSim.Region.CoreModules.Framework.InventoryAccess; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Scenes.Serialization; +using OpenSim.Services.Interfaces; +using OpenSim.Tests.Common; +using OpenSim.Tests.Common.Mock; +using OpenSim.Tests.Common.Setup; + +namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests +{ + [TestFixture] + public class InventoryAccessModuleTests + { + protected TestScene m_scene; + protected BasicInventoryAccessModule m_iam; + + [SetUp] + public void SetUp() + { + m_iam = new BasicInventoryAccessModule(); + + IConfigSource config = new IniConfigSource(); + config.AddConfig("Modules"); + config.Configs["Modules"].Set("InventoryAccessModule", "BasicInventoryAccessModule"); + + m_scene = SceneSetupHelpers.SetupScene("Inventory"); + SceneSetupHelpers.SetupSceneModules(m_scene, config, m_iam); + } + + [Test] + public void TestRezObject() + { + TestHelper.InMethod(); + log4net.Config.XmlConfigurator.Configure(); + + // Create user + string userFirstName = "Jock"; + string userLastName = "Stirrup"; + string userPassword = "troll"; + UUID userId = UUID.Parse("00000000-0000-0000-0000-000000000020"); + UserProfileTestUtils.CreateUserWithInventory(m_scene, userFirstName, userLastName, userId, userPassword); + + // Create asset + SceneObjectGroup object1; + SceneObjectPart part1; + { + string partName = "My Little Dog Object"; + UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); + PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); + Vector3 groupPosition = new Vector3(10, 20, 30); + Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); + Vector3 offsetPosition = new Vector3(5, 10, 15); + + part1 + = new SceneObjectPart( + ownerId, shape, groupPosition, rotationOffset, offsetPosition); + part1.Name = partName; + + object1 = new SceneObjectGroup(part1); + } + + UUID asset1Id = UUID.Parse("00000000-0000-0000-0000-000000000060"); + AssetBase asset1 = AssetHelpers.CreateAsset(asset1Id, object1); + m_scene.AssetService.Store(asset1); + + // Create item + UUID item1Id = UUID.Parse("00000000-0000-0000-0000-000000000080"); + string item1Name = "My Little Dog"; + InventoryItemBase item1 = new InventoryItemBase(); + item1.Name = item1Name; + item1.AssetID = asset1.FullID; + item1.ID = item1Id; + InventoryFolderBase objsFolder + = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, userId, "Objects")[0]; + item1.Folder = objsFolder.ID; + m_scene.AddInventoryItem(item1); + + AgentCircuitData acd = new AgentCircuitData(); + acd.AgentID = userId; + TestClient tc = new TestClient(acd, m_scene); + + SceneObjectGroup so + = m_iam.RezObject( + tc, item1Id, Vector3.Zero, Vector3.Zero, UUID.Zero, 1, false, false, false, UUID.Zero, false); + + Assert.That(so, Is.Not.Null); + + SceneObjectPart retrievedPart = m_scene.GetSceneObjectPart(so.UUID); + Assert.That(retrievedPart, Is.Not.Null); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index b6d64acb80..ab90e90b78 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -259,6 +259,10 @@ namespace OpenSim.Region.CoreModules.World.Estate private void handleChangeEstateCovenantRequest(IClientAPI remoteClient, UUID estateCovenantID) { +// m_log.DebugFormat( +// "[ESTATE MANAGEMENT MODULE]: Handling request from {0} to change estate covenant to {1}", +// remoteClient.Name, estateCovenantID); + Scene.RegionInfo.RegionSettings.Covenant = estateCovenantID; Scene.RegionInfo.RegionSettings.Save(); TriggerRegionInfoChange(); diff --git a/prebuild.xml b/prebuild.xml index 81716641c6..49063daa57 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2888,6 +2888,7 @@ + From 08fd1d9bbd0878e6ea0ce4abc0a664a70865ea01 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 13 Apr 2011 18:11:04 +0100 Subject: [PATCH 049/103] factor out some test code into the SetUp() --- .../Tests/InventoryAccessModuleTests.cs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs index ecf70ac347..5eca753d03 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs @@ -54,6 +54,8 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests { protected TestScene m_scene; protected BasicInventoryAccessModule m_iam; + protected UUID m_userId = UUID.Parse("00000000-0000-0000-0000-000000000020"); + protected TestClient m_tc; [SetUp] public void SetUp() @@ -66,20 +68,23 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests m_scene = SceneSetupHelpers.SetupScene("Inventory"); SceneSetupHelpers.SetupSceneModules(m_scene, config, m_iam); + + // Create user + string userFirstName = "Jock"; + string userLastName = "Stirrup"; + string userPassword = "troll"; + UserProfileTestUtils.CreateUserWithInventory(m_scene, userFirstName, userLastName, m_userId, userPassword); + + AgentCircuitData acd = new AgentCircuitData(); + acd.AgentID = m_userId; + m_tc = new TestClient(acd, m_scene); } [Test] public void TestRezObject() { TestHelper.InMethod(); - log4net.Config.XmlConfigurator.Configure(); - - // Create user - string userFirstName = "Jock"; - string userLastName = "Stirrup"; - string userPassword = "troll"; - UUID userId = UUID.Parse("00000000-0000-0000-0000-000000000020"); - UserProfileTestUtils.CreateUserWithInventory(m_scene, userFirstName, userLastName, userId, userPassword); +// log4net.Config.XmlConfigurator.Configure(); // Create asset SceneObjectGroup object1; @@ -112,17 +117,13 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests item1.AssetID = asset1.FullID; item1.ID = item1Id; InventoryFolderBase objsFolder - = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, userId, "Objects")[0]; + = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, m_userId, "Objects")[0]; item1.Folder = objsFolder.ID; m_scene.AddInventoryItem(item1); - AgentCircuitData acd = new AgentCircuitData(); - acd.AgentID = userId; - TestClient tc = new TestClient(acd, m_scene); - SceneObjectGroup so = m_iam.RezObject( - tc, item1Id, Vector3.Zero, Vector3.Zero, UUID.Zero, 1, false, false, false, UUID.Zero, false); + m_tc, item1Id, Vector3.Zero, Vector3.Zero, UUID.Zero, 1, false, false, false, UUID.Zero, false); Assert.That(so, Is.Not.Null); From dec9c2283b548818409c91a93307334d1c0d9774 Mon Sep 17 00:00:00 2001 From: BlueWall Date: Wed, 13 Apr 2011 16:10:18 -0400 Subject: [PATCH 050/103] Add ColladaMesh switch --- .../CoreModules/Avatar/Assets/GetMeshModule.cs | 13 +++++++++++-- .../NewFileAgentInventoryVariablePriceModule.cs | 11 ++++++++++- bin/OpenSimDefaults.ini | 6 +++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Assets/GetMeshModule.cs b/OpenSim/Region/CoreModules/Avatar/Assets/GetMeshModule.cs index 878242a447..d2278bc3de 100644 --- a/OpenSim/Region/CoreModules/Avatar/Assets/GetMeshModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Assets/GetMeshModule.cs @@ -54,6 +54,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets private Scene m_scene; private IAssetService m_assetService; + private bool m_enabled = true; #region IRegionModuleBase Members @@ -65,7 +66,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets public void Initialise(IConfigSource source) { - + IConfig startupConfig = source.Configs["Startup"]; + if (startupConfig == null) + return; + + if (!startupConfig.GetBoolean("ColladaMesh",true)) + m_enabled = false; } public void AddRegion(Scene pScene) @@ -101,16 +107,19 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets public void RegisterCaps(UUID agentID, Caps caps) { + if(!m_enabled) + return; + UUID capID = UUID.Random(); // m_log.Info("[GETMESH]: /CAPS/" + capID); + caps.RegisterHandler("GetMesh", new RestHTTPHandler("GET", "/CAPS/" + capID, delegate(Hashtable m_dhttpMethod) { return ProcessGetMesh(m_dhttpMethod, agentID, caps); })); - } #endregion diff --git a/OpenSim/Region/CoreModules/Avatar/Assets/NewFileAgentInventoryVariablePriceModule.cs b/OpenSim/Region/CoreModules/Avatar/Assets/NewFileAgentInventoryVariablePriceModule.cs index 4a42c938d5..fb07cc9ca0 100644 --- a/OpenSim/Region/CoreModules/Avatar/Assets/NewFileAgentInventoryVariablePriceModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Assets/NewFileAgentInventoryVariablePriceModule.cs @@ -56,6 +56,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets private Scene m_scene; // private IAssetService m_assetService; private bool m_dumpAssetsToFile = false; + private bool m_enabled = true; #region IRegionModuleBase Members @@ -67,7 +68,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets public void Initialise(IConfigSource source) { - + IConfig startupConfig = source.Configs["Startup"]; + if (startupConfig == null) + return; + + if (!startupConfig.GetBoolean("ColladaMesh",true)) + m_enabled = false; } public void AddRegion(Scene pScene) @@ -103,6 +109,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets public void RegisterCaps(UUID agentID, Caps caps) { + if(!m_enabled) + return; + UUID capID = UUID.Random(); // m_log.Debug("[NEW FILE AGENT INVENTORY VARIABLE PRICE]: /CAPS/" + capID); diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index f857b8da63..107e859827 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -154,7 +154,11 @@ ; mesh, and use it for collisions. This is currently experimental code and enabling ; it may cause unexpected physics problems. ;UseMeshiesPhysicsMesh = false - + + ; enable / disable Collada mesh support + ; default is true + ; ColladaMesh = true + ; Choose one of the physics engines below ; OpenDynamicsEngine is by some distance the most developed physics engine ; basicphysics effectively does not model physics at all, making all objects phantom From 58efd761d13bd4f2617fcb3f94bbe265880589e3 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 13 Apr 2011 21:17:43 +0100 Subject: [PATCH 051/103] Add coalesced scene objects class and serializer. This is currently only used by the TestRezCoalescedObject() regression test. This structure matches the existing one for SceneObjects and will allow code to be reused by the uuid gatherer, other tests, etc. Test is not yet fully implemented due to a bug in rezzing coalesced objects where they all get the same name as the item. Only one object should get the same name as the item, which appears to be the one selected last when the the objects were coalesced in the first place. This bug will be addressed shortly. --- .../InventoryAccess/InventoryAccessModule.cs | 19 ++- .../Tests/InventoryAccessModuleTests.cs | 79 +++++++++++ .../Framework/Scenes/CoalescedSceneObjects.cs | 128 ++++++++++++++++++ OpenSim/Region/Framework/Scenes/Scene.cs | 15 +- OpenSim/Region/Framework/Scenes/SceneGraph.cs | 2 + .../CoalescedSceneObjectsSerializer.cs | 117 ++++++++++++++++ .../Serialization/SceneObjectSerializer.cs | 23 +++- OpenSim/Tests/Common/Setup/AssetHelpers.cs | 15 ++ 8 files changed, 389 insertions(+), 9 deletions(-) create mode 100644 OpenSim/Region/Framework/Scenes/CoalescedSceneObjects.cs create mode 100644 OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 73b0a35b7c..cfcfc79c11 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -274,7 +274,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess float minX, minY, minZ; float maxX, maxY, maxZ; - Vector3[] offsets = m_Scene.GetCombinedBoundingBox(objlist, + Vector3[] offsets = Scene.GetCombinedBoundingBox(objlist, out minX, out maxX, out minY, out maxY, out minZ, out maxZ); @@ -289,7 +289,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess XmlDocument doc = new XmlDocument(); SceneObjectGroup g = objlist[i]; doc.LoadXml(xmlStrings[g.UUID]); - XmlElement e = (XmlElement)doc.SelectSingleNode("/SceneObjectGroup"); + XmlElement e = (XmlElement)doc.SelectSingleNode("/SceneObjectGroup"); e.SetAttribute("offsetx", offsets[i].X.ToString()); e.SetAttribute("offsety", offsets[i].Y.ToString()); e.SetAttribute("offsetz", offsets[i].Z.ToString()); @@ -659,9 +659,18 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess itemId, n.OuterXml); objlist.Add(g); XmlElement el = (XmlElement)n; - float x = Convert.ToSingle(el.GetAttribute("offsetx")); - float y = Convert.ToSingle(el.GetAttribute("offsety")); - float z = Convert.ToSingle(el.GetAttribute("offsetz")); + + string rawX = el.GetAttribute("offsetx"); + string rawY = el.GetAttribute("offsety"); + string rawZ = el.GetAttribute("offsetz"); +// +// m_log.DebugFormat( +// "[INVENTORY ACCESS MODULE]: Converting coalesced object {0} offset <{1}, {2}, {3}>", +// g.Name, rawX, rawY, rawZ); + + float x = Convert.ToSingle(rawX); + float y = Convert.ToSingle(rawY); + float z = Convert.ToSingle(rawZ); veclist.Add(new Vector3(x, y, z)); } } diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs index 5eca753d03..f8fa424738 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs @@ -80,6 +80,85 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests m_tc = new TestClient(acd, m_scene); } + [Test] + public void TestRezCoalescedObject() + { + TestHelper.InMethod(); + log4net.Config.XmlConfigurator.Configure(); + + // Create asset + SceneObjectGroup object1; + { + string partName = "Object1"; + UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); + PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); + Vector3 groupPosition = new Vector3(10, 20, 30); + Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); + Vector3 offsetPosition = new Vector3(5, 10, 15); + + SceneObjectPart part1 + = new SceneObjectPart( + ownerId, shape, groupPosition, rotationOffset, offsetPosition); + part1.Name = partName; + + object1 = new SceneObjectGroup(part1); + } + + SceneObjectGroup object2; + { + string partName = "Object2"; + UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); + PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); + Vector3 groupPosition = new Vector3(10, 20, 30); + Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); + Vector3 offsetPosition = new Vector3(5, 10, 15); + + SceneObjectPart part1 + = new SceneObjectPart( + ownerId, shape, groupPosition, rotationOffset, offsetPosition); + part1.Name = partName; + + object2 = new SceneObjectGroup(part1); + } + + CoalescedSceneObjects coa = new CoalescedSceneObjects(m_userId, object1, object2); + + UUID asset1Id = UUID.Parse("00000000-0000-0000-0000-000000000060"); + AssetBase asset1 = AssetHelpers.CreateAsset(asset1Id, coa); + m_scene.AssetService.Store(asset1); + + // Create item + UUID item1Id = UUID.Parse("00000000-0000-0000-0000-000000000080"); + string item1Name = "My Little Dog"; + InventoryItemBase item1 = new InventoryItemBase(); + item1.Name = item1Name; + item1.AssetID = asset1.FullID; + item1.ID = item1Id; + InventoryFolderBase objsFolder + = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, m_userId, "Objects")[0]; + item1.Folder = objsFolder.ID; + m_scene.AddInventoryItem(item1); + + SceneObjectGroup so + = m_iam.RezObject( + m_tc, item1Id, Vector3.Zero, Vector3.Zero, UUID.Zero, 1, false, false, false, UUID.Zero, false); + + Assert.That(so, Is.Not.Null); + + Assert.That(m_scene.SceneGraph.GetTotalObjectsCount(), Is.EqualTo(2)); + + SceneObjectPart retrievedObj1Part = m_scene.GetSceneObjectPart(object1.Name); + Assert.That(retrievedObj1Part, Is.Null); + + retrievedObj1Part = m_scene.GetSceneObjectPart(item1.Name); + Assert.That(retrievedObj1Part, Is.Not.Null); + Assert.That(retrievedObj1Part.Name, Is.EqualTo(item1.Name)); + + // FIXME: Can't test yet due to a bug where objects in coalescence all get the item name when rerezzed. +// SceneObjectPart retrievedObj2Part = m_scene.GetSceneObjectPart(object2.Name); +// Assert.That(retrievedObj2Part, Is.Not.Null); + } + [Test] public void TestRezObject() { diff --git a/OpenSim/Region/Framework/Scenes/CoalescedSceneObjects.cs b/OpenSim/Region/Framework/Scenes/CoalescedSceneObjects.cs new file mode 100644 index 0000000000..51eac5f3f6 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/CoalescedSceneObjects.cs @@ -0,0 +1,128 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Scenes +{ + /// + /// Represents a coalescene of scene objects. A coalescence occurs when objects that are not in the same linkset + /// are grouped together. + /// + public class CoalescedSceneObjects + { + /// + /// The creator of this coalesence, though not necessarily the objects within it. + /// + public UUID CreatorId { get; set; } + + /// + /// The number of objects in this coalesence + /// + public int Count + { + get + { + lock (m_memberObjects) + return m_memberObjects.Count; + } + } + + /// + /// Does this coalesence have any member objects? + /// + public bool HasObjects { get { return Count > 0; } } + + /// + /// Get the objects currently in this coalescence + /// + public List Objects + { + get + { + lock (m_memberObjects) + return new List(m_memberObjects); + } + } + + /// + /// Get the scene that contains the objects in this coalescence. If there are no objects then null is returned. + /// + public Scene Scene + { + get + { + if (!HasObjects) + return null; + else + return Objects[0].Scene; + } + } + + /// + /// At this point, we need to preserve the order of objects added to the coalescence, since the first + /// one will end up matching the item name when rerezzed. + /// + protected List m_memberObjects = new List(); + + public CoalescedSceneObjects(UUID creatorId) + { + CreatorId = creatorId; + } + + public CoalescedSceneObjects(UUID creatorId, params SceneObjectGroup[] objs) : this(creatorId) + { + foreach (SceneObjectGroup obj in objs) + Add(obj); + } + + /// + /// Add an object to the coalescence. + /// + /// + /// The offset of the object within the group + public void Add(SceneObjectGroup obj) + { + lock (m_memberObjects) + m_memberObjects.Add(obj); + } + + /// + /// Removes a scene object from the coalescene + /// + /// + /// true if the object was there to be removed, false if not. + public bool Remove(SceneObjectGroup obj) + { + lock (m_memberObjects) + return m_memberObjects.Remove(obj); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index f0acc38820..e6dd48940f 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -4839,7 +4839,20 @@ namespace OpenSim.Region.Framework.Scenes } } - public Vector3[] GetCombinedBoundingBox(List objects, out float minX, out float maxX, out float minY, out float maxY, out float minZ, out float maxZ) + /// + /// Get the volume of space that will encompass all the given objects. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static Vector3[] GetCombinedBoundingBox( + List objects, + out float minX, out float maxX, out float minY, out float maxY, out float minZ, out float maxZ) { minX = 256; maxX = -256; diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 97af0a0b47..72f040252f 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -997,6 +997,8 @@ namespace OpenSim.Region.Framework.Scenes { foreach (SceneObjectPart p in ((SceneObjectGroup)entity).Parts) { +// m_log.DebugFormat("[SCENE GRAPH]: Part {0} has name {1}", p.UUID, p.Name); + if (p.Name == name) { sop = p; diff --git a/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs new file mode 100644 index 0000000000..3af2f76340 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs @@ -0,0 +1,117 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Reflection; +using System.Xml; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Scenes.Serialization +{ + /// + /// Serialize and deserialize coalesced scene objects. + /// + public class CoalescedSceneObjectsSerializer + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Serialize coalesced objects to Xml + /// + /// + /// + public static string ToXml(CoalescedSceneObjects coa) + { + // TODO: Should probably return an empty xml serialization rather than a blank string + if (!coa.HasObjects) + return ""; + + using (StringWriter sw = new StringWriter()) + { + using (XmlTextWriter writer = new XmlTextWriter(sw)) + { + List coaObjects = coa.Objects; + +// m_log.DebugFormat( +// "[COALESCED SCENE OBJECTS SERIALIZER]: Writing {0} objects for coalesced object", +// coaObjects.Count); + + float minX, minY, minZ; + float maxX, maxY, maxZ; + + Vector3[] offsets = Scene.GetCombinedBoundingBox(coaObjects, + out minX, out maxX, out minY, out maxY, + out minZ, out maxZ); + + writer.WriteStartElement("CoalescedObject"); + + float sizeX = maxX - minX; + float sizeY = maxY - minY; + float sizeZ = maxZ - minZ; + + writer.WriteAttributeString("x", sizeX.ToString()); + writer.WriteAttributeString("y", sizeY.ToString()); + writer.WriteAttributeString("z", sizeZ.ToString()); + + // Embed the offsets into the group XML + for (int i = 0; i < coaObjects.Count; i++) + { + SceneObjectGroup obj = coaObjects[i]; + +// m_log.DebugFormat( +// "[COALESCED SCENE OBJECTS SERIALIZER]: Writing offset for object {0}, {1}", +// i, obj.Name); + + writer.WriteStartElement("SceneObjectGroup"); + writer.WriteAttributeString("offsetx", offsets[i].X.ToString()); + writer.WriteAttributeString("offsety", offsets[i].Y.ToString()); + writer.WriteAttributeString("offsetz", offsets[i].Z.ToString()); + + SceneObjectSerializer.ToOriginalXmlFormat(obj, writer, true); + + writer.WriteEndElement(); + } + + writer.WriteEndElement(); // CoalescedObject + } + + string output = sw.ToString(); + +// m_log.Debug(output); + + return output; + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs index b412e25579..bb8a83a1af 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs @@ -139,6 +139,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization return sw.ToString(); } } + /// /// Serialize a scene object to the original xml format @@ -146,11 +147,25 @@ namespace OpenSim.Region.Framework.Scenes.Serialization /// /// public static void ToOriginalXmlFormat(SceneObjectGroup sceneObject, XmlTextWriter writer) + { + ToOriginalXmlFormat(sceneObject, writer, false); + } + + /// + /// Serialize a scene object to the original xml format + /// + /// + /// + /// If false, don't write the enclosing SceneObjectGroup element + /// + public static void ToOriginalXmlFormat(SceneObjectGroup sceneObject, XmlTextWriter writer, bool noRootElement) { //m_log.DebugFormat("[SERIALIZER]: Starting serialization of {0}", Name); //int time = System.Environment.TickCount; - writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty); + if (!noRootElement) + writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty); + writer.WriteStartElement(String.Empty, "RootPart", String.Empty); ToXmlFormat(sceneObject.RootPart, writer); writer.WriteEndElement(); @@ -170,10 +185,12 @@ namespace OpenSim.Region.Framework.Scenes.Serialization writer.WriteEndElement(); // OtherParts sceneObject.SaveScriptedState(writer); - writer.WriteEndElement(); // SceneObjectGroup + + if (!noRootElement) + writer.WriteEndElement(); // SceneObjectGroup //m_log.DebugFormat("[SERIALIZER]: Finished serialization of SOG {0}, {1}ms", Name, System.Environment.TickCount - time); - } + } protected static void ToXmlFormat(SceneObjectPart part, XmlTextWriter writer) { diff --git a/OpenSim/Tests/Common/Setup/AssetHelpers.cs b/OpenSim/Tests/Common/Setup/AssetHelpers.cs index ff4423f7ab..af66d7f0a7 100644 --- a/OpenSim/Tests/Common/Setup/AssetHelpers.cs +++ b/OpenSim/Tests/Common/Setup/AssetHelpers.cs @@ -71,6 +71,21 @@ namespace OpenSim.Tests.Common Encoding.ASCII.GetBytes(SceneObjectSerializer.ToOriginalXmlFormat(sog)), sog.OwnerID); } + + /// + /// Create an asset from the given scene object. + /// + /// + /// + /// + public static AssetBase CreateAsset(UUID assetUuid, CoalescedSceneObjects coa) + { + return CreateAsset( + assetUuid, + AssetType.Object, + Encoding.ASCII.GetBytes(CoalescedSceneObjectsSerializer.ToXml(coa)), + coa.CreatorId); + } /// /// Create an asset from the given data. From d8d28c37372718aa593ead5c85cd45ced83766a5 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 13 Apr 2011 21:31:45 +0100 Subject: [PATCH 052/103] Fix bug where rezzing coalesced objects would give all objects the same name as the item. This now only happens for the first object (which was the item selected last when the coalesce was originally taken) This matches the expected behaviour of the environment as seen on the Linden Labs grid. --- .../InventoryAccess/InventoryAccessModule.cs | 13 +++++++++---- .../Tests/InventoryAccessModuleTests.cs | 5 ++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index cfcfc79c11..1f8bad1eb5 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -762,10 +762,15 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess // affect the name stored in the serialization, transfer // the correct name from the inventory to the // object itself before we rez. - rootPart.Name = item.Name; - rootPart.Description = item.Description; - rootPart.ObjectSaleType = item.SaleType; - rootPart.SalePrice = item.SalePrice; + // + // Only do these for the first object if we are rezzing a coalescence. + if (i == 0) + { + rootPart.Name = item.Name; + rootPart.Description = item.Description; + rootPart.ObjectSaleType = item.SaleType; + rootPart.SalePrice = item.SalePrice; + } group.SetGroup(remoteClient.ActiveGroupId, remoteClient); if ((rootPart.OwnerID != item.Owner) || diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs index f8fa424738..0f2b6108ad 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs @@ -154,9 +154,8 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests Assert.That(retrievedObj1Part, Is.Not.Null); Assert.That(retrievedObj1Part.Name, Is.EqualTo(item1.Name)); - // FIXME: Can't test yet due to a bug where objects in coalescence all get the item name when rerezzed. -// SceneObjectPart retrievedObj2Part = m_scene.GetSceneObjectPart(object2.Name); -// Assert.That(retrievedObj2Part, Is.Not.Null); + SceneObjectPart retrievedObj2Part = m_scene.GetSceneObjectPart(object2.Name); + Assert.That(retrievedObj2Part, Is.Not.Null); } [Test] From 68cc5b46fefdf4db68cae1202086257ce731f8a1 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 13 Apr 2011 21:48:16 +0100 Subject: [PATCH 053/103] refactor: move code to obtain the coalescence size and object offsets into CoalescedSceneObjects from the serializer. --- .../Tests/InventoryAccessModuleTests.cs | 2 +- .../Framework/Scenes/CoalescedSceneObjects.cs | 26 ++++++++++++++++++ .../CoalescedSceneObjectsSerializer.cs | 27 +++++++++---------- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs index 0f2b6108ad..c197cb9b1b 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs @@ -84,7 +84,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests public void TestRezCoalescedObject() { TestHelper.InMethod(); - log4net.Config.XmlConfigurator.Configure(); +// log4net.Config.XmlConfigurator.Configure(); // Create asset SceneObjectGroup object1; diff --git a/OpenSim/Region/Framework/Scenes/CoalescedSceneObjects.cs b/OpenSim/Region/Framework/Scenes/CoalescedSceneObjects.cs index 51eac5f3f6..af8ccda6c2 100644 --- a/OpenSim/Region/Framework/Scenes/CoalescedSceneObjects.cs +++ b/OpenSim/Region/Framework/Scenes/CoalescedSceneObjects.cs @@ -124,5 +124,31 @@ namespace OpenSim.Region.Framework.Scenes lock (m_memberObjects) return m_memberObjects.Remove(obj); } + + /// + /// Get the total size of the coalescence (the size required to cover all the objects within it) and the + /// offsets of each of those objects. + /// + /// + /// + /// An array of offsets. The order of objects is the same as returned from the Objects property + /// + public Vector3[] GetSizeAndOffsets(out Vector3 size) + { + float minX, minY, minZ; + float maxX, maxY, maxZ; + + Vector3[] offsets + = Scene.GetCombinedBoundingBox( + Objects, out minX, out maxX, out minY, out maxY, out minZ, out maxZ); + + float sizeX = maxX - minX; + float sizeY = maxY - minY; + float sizeZ = maxZ - minZ; + + size = new Vector3(sizeX, sizeY, sizeZ); + + return offsets; + } } } \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs index 3af2f76340..a0e120a9ac 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs @@ -42,6 +42,9 @@ namespace OpenSim.Region.Framework.Scenes.Serialization /// /// Serialize and deserialize coalesced scene objects. /// + /// + /// Deserialization not yet here. + /// public class CoalescedSceneObjectsSerializer { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); @@ -60,29 +63,23 @@ namespace OpenSim.Region.Framework.Scenes.Serialization using (StringWriter sw = new StringWriter()) { using (XmlTextWriter writer = new XmlTextWriter(sw)) - { + { + Vector3 size; + List coaObjects = coa.Objects; // m_log.DebugFormat( // "[COALESCED SCENE OBJECTS SERIALIZER]: Writing {0} objects for coalesced object", // coaObjects.Count); - float minX, minY, minZ; - float maxX, maxY, maxZ; - - Vector3[] offsets = Scene.GetCombinedBoundingBox(coaObjects, - out minX, out maxX, out minY, out maxY, - out minZ, out maxZ); + // This is weak - we're relying on the set of coalesced objects still being identical + Vector3[] offsets = coa.GetSizeAndOffsets(out size); writer.WriteStartElement("CoalescedObject"); - float sizeX = maxX - minX; - float sizeY = maxY - minY; - float sizeZ = maxZ - minZ; - - writer.WriteAttributeString("x", sizeX.ToString()); - writer.WriteAttributeString("y", sizeY.ToString()); - writer.WriteAttributeString("z", sizeZ.ToString()); + writer.WriteAttributeString("x", size.X.ToString()); + writer.WriteAttributeString("y", size.Y.ToString()); + writer.WriteAttributeString("z", size.Z.ToString()); // Embed the offsets into the group XML for (int i = 0; i < coaObjects.Count; i++) @@ -100,7 +97,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization SceneObjectSerializer.ToOriginalXmlFormat(obj, writer, true); - writer.WriteEndElement(); + writer.WriteEndElement(); // SceneObjectGroup } writer.WriteEndElement(); // CoalescedObject From c81f5bfc5c3cb0bac2a89ee846099dcaae1f357b Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 13 Apr 2011 22:29:12 +0100 Subject: [PATCH 054/103] Adjust the quanterions used in the rez coalsced object tests to get sensible bounding box and offset numbers. Extend test to check position of objects in the rezzed coalescence. --- .../Tests/InventoryAccessModuleTests.cs | 16 ++++++++++++---- OpenSim/Region/Framework/Scenes/Scene.cs | 4 ++++ .../Region/Framework/Scenes/SceneObjectPart.cs | 9 ++++++++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs index c197cb9b1b..c76a30af69 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs @@ -93,12 +93,13 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); Vector3 groupPosition = new Vector3(10, 20, 30); - Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); + Quaternion rotationOffset = Quaternion.Identity; Vector3 offsetPosition = new Vector3(5, 10, 15); SceneObjectPart part1 = new SceneObjectPart( ownerId, shape, groupPosition, rotationOffset, offsetPosition); + part1.Scale = new Vector3(1, 1, 1); part1.Name = partName; object1 = new SceneObjectGroup(part1); @@ -109,13 +110,14 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests string partName = "Object2"; UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); - Vector3 groupPosition = new Vector3(10, 20, 30); - Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); + Vector3 groupPosition = new Vector3(20, 40, 60); + Quaternion rotationOffset = Quaternion.Identity; Vector3 offsetPosition = new Vector3(5, 10, 15); SceneObjectPart part1 = new SceneObjectPart( ownerId, shape, groupPosition, rotationOffset, offsetPosition); + part1.Scale = new Vector3(1, 1, 1); part1.Name = partName; object2 = new SceneObjectGroup(part1); @@ -141,7 +143,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests SceneObjectGroup so = m_iam.RezObject( - m_tc, item1Id, Vector3.Zero, Vector3.Zero, UUID.Zero, 1, false, false, false, UUID.Zero, false); + m_tc, item1Id, new Vector3(100, 100, 100), Vector3.Zero, UUID.Zero, 1, false, false, false, UUID.Zero, false); Assert.That(so, Is.Not.Null); @@ -154,8 +156,14 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests Assert.That(retrievedObj1Part, Is.Not.Null); Assert.That(retrievedObj1Part.Name, Is.EqualTo(item1.Name)); + // Bottom of coalescence is placed on ground, hence we end up with 100.5 rather than 85 since the bottom + // object is unit square. + Assert.That(retrievedObj1Part.AbsolutePosition, Is.EqualTo(new Vector3(95, 90, 100.5f))); + SceneObjectPart retrievedObj2Part = m_scene.GetSceneObjectPart(object2.Name); Assert.That(retrievedObj2Part, Is.Not.Null); + Assert.That(retrievedObj2Part.Name, Is.EqualTo(object2.Name)); + Assert.That(retrievedObj2Part.AbsolutePosition, Is.EqualTo(new Vector3(105, 110, 130.5f))); } [Test] diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index e6dd48940f..fdd5205de8 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -4870,6 +4870,10 @@ namespace OpenSim.Region.Framework.Scenes Vector3 vec = g.AbsolutePosition; g.GetAxisAlignedBoundingBoxRaw(out ominX, out omaxX, out ominY, out omaxY, out ominZ, out omaxZ); + +// m_log.DebugFormat( +// "[SCENE]: For {0} found AxisAlignedBoundingBoxRaw {1}, {2}", +// g.Name, new Vector3(ominX, ominY, ominZ), new Vector3(omaxX, omaxY, omaxZ)); ominX += vec.X; omaxX += vec.X; diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 4d5eedfc95..ce1e6b8614 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2088,7 +2088,14 @@ namespace OpenSim.Region.Framework.Scenes axPos *= parentRot; Vector3 translationOffsetPosition = axPos; - return GroupPosition + translationOffsetPosition; + +// m_log.DebugFormat("[SCENE OBJECT PART]: Found group pos {0} for part {1}", GroupPosition, Name); + + Vector3 worldPos = GroupPosition + translationOffsetPosition; + +// m_log.DebugFormat("[SCENE OBJECT PART]: Found world pos {0} for part {1}", worldPos, Name); + + return worldPos; } /// From c5465414b64d835128827976cc3da32aa462e6a3 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 13 Apr 2011 22:55:26 +0100 Subject: [PATCH 055/103] Use common coalesced serialization code for both tests and the InventoryAccessModule. Continue to restore original absolute positions of stored scene objects, though it's possible that these aren't used again --- .../InventoryAccess/InventoryAccessModule.cs | 67 ++++--------------- 1 file changed, 13 insertions(+), 54 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 1f8bad1eb5..c0946f4d52 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -222,7 +222,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess deletes[g.OwnerID].Add(g); } - // This is pethod scoped and will be returned. It will be the + // This is method scoped and will be returned. It will be the // last created asset id UUID assetID = UUID.Zero; @@ -230,8 +230,8 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess // with distinct destinations as well. foreach (List objlist in deletes.Values) { - Dictionary xmlStrings = - new Dictionary(); + CoalescedSceneObjects coa = new CoalescedSceneObjects(UUID.Zero); + Dictionary originalPositions = new Dictionary(); foreach (SceneObjectGroup objectGroup in objlist) { @@ -245,7 +245,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess : objectGroup.AbsolutePosition.X, objectGroup.AbsolutePosition.Z); - Vector3 originalPosition = objectGroup.AbsolutePosition; + originalPositions[objectGroup.UUID] = objectGroup.AbsolutePosition; objectGroup.AbsolutePosition = inventoryStoredPosition; @@ -259,59 +259,20 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess (uint)PermissionMask.Modify); objectGroup.RootPart.NextOwnerMask |= (uint)PermissionMask.Move; - - string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat(objectGroup); - - objectGroup.AbsolutePosition = originalPosition; - - xmlStrings[objectGroup.UUID] = sceneObjectXml; + + coa.Add(objectGroup); } string itemXml; if (objlist.Count > 1) - { - float minX, minY, minZ; - float maxX, maxY, maxZ; - - Vector3[] offsets = Scene.GetCombinedBoundingBox(objlist, - out minX, out maxX, out minY, out maxY, - out minZ, out maxZ); - - // CreateWrapper - XmlDocument itemDoc = new XmlDocument(); - XmlElement root = itemDoc.CreateElement("", "CoalescedObject", ""); - itemDoc.AppendChild(root); - - // Embed the offsets into the group XML - for ( int i = 0 ; i < objlist.Count ; i++ ) - { - XmlDocument doc = new XmlDocument(); - SceneObjectGroup g = objlist[i]; - doc.LoadXml(xmlStrings[g.UUID]); - XmlElement e = (XmlElement)doc.SelectSingleNode("/SceneObjectGroup"); - e.SetAttribute("offsetx", offsets[i].X.ToString()); - e.SetAttribute("offsety", offsets[i].Y.ToString()); - e.SetAttribute("offsetz", offsets[i].Z.ToString()); - - XmlNode objectNode = itemDoc.ImportNode(e, true); - root.AppendChild(objectNode); - } - - float sizeX = maxX - minX; - float sizeY = maxY - minY; - float sizeZ = maxZ - minZ; - - root.SetAttribute("x", sizeX.ToString()); - root.SetAttribute("y", sizeY.ToString()); - root.SetAttribute("z", sizeZ.ToString()); - - itemXml = itemDoc.InnerXml; - } + itemXml = CoalescedSceneObjectsSerializer.ToXml(coa); else - { - itemXml = xmlStrings[objlist[0].UUID]; - } + itemXml = SceneObjectSerializer.ToOriginalXmlFormat(objlist[0]); + + // Restore the position of each group now that it has been stored to inventory. + foreach (SceneObjectGroup objectGroup in objlist) + objectGroup.AbsolutePosition = originalPositions[objectGroup.UUID]; // Get the user info of the item destination // @@ -332,7 +293,6 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess { // All returns / deletes go to the object owner // - userID = objlist[0].RootPart.OwnerID; } @@ -346,7 +306,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess // Delete is treated like return in this case // Deleting your own items makes them go to trash // - + InventoryFolderBase folder = null; InventoryItemBase item = null; @@ -548,7 +508,6 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess return assetID; } - /// /// Rez an object into the scene from the user's inventory /// From 5b89c66c9704f2bd219a35f8a70ac60e751d7f49 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 11 Apr 2011 09:06:28 -0700 Subject: [PATCH 056/103] New tokenbucket algorithm. This one provides fair sharing of the queues when client and simulator throttles are set. This algorithm also uses pre-defined burst rate of 150% of the sustained rate for each of the throttles. Removed the "state" queue. The state queue is not a Linden queue and appeared to be used just to get kill packets sent. --- .../ClientStack/LindenUDP/LLClientView.cs | 6 +- .../ClientStack/LindenUDP/LLUDPClient.cs | 96 +++--- .../ClientStack/LindenUDP/LLUDPServer.cs | 2 +- .../ClientStack/LindenUDP/TokenBucket.cs | 314 +++++++++++------- 4 files changed, 266 insertions(+), 152 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 76d7f7964b..5980669651 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -1610,7 +1610,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - OutPacket(kill, ThrottleOutPacketType.State); + // OutPacket(kill, ThrottleOutPacketType.State); + OutPacket(kill, ThrottleOutPacketType.Task); } } @@ -2440,7 +2441,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Effect = effectBlocks; - OutPacket(packet, ThrottleOutPacketType.State); + // OutPacket(packet, ThrottleOutPacketType.State); + OutPacket(packet, ThrottleOutPacketType.Task); } public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 9a8bfd357c..5a69851b3c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -135,7 +135,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_nextOnQueueEmpty = 1; /// Throttle bucket for this agent's connection - private readonly TokenBucket m_throttle; + private readonly TokenBucket m_throttleClient; + /// Throttle bucket for this agent's connection + private readonly TokenBucket m_throttleCategory; /// Throttle buckets for each packet category private readonly TokenBucket[] m_throttleCategories; /// Outgoing queues for throttled packets @@ -174,7 +176,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_maxRTO = maxRTO; // Create a token bucket throttle for this client that has the scene token bucket as a parent - m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); + m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit); + // Create a token bucket throttle for the total categary with the client bucket as a throttle + m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit); // Create an array of token buckets for this clients different throttle categories m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; @@ -185,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Initialize the packet outboxes, where packets sit while they are waiting for tokens m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); // Initialize the token buckets that control the throttling for each category - m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); + m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type)); } // Default the retransmission timeout to three seconds @@ -206,6 +210,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_packetOutboxes[i].Clear(); m_nextPackets[i] = null; } + + // pull the throttle out of the scene throttle + m_throttleClient.Parent.UnregisterRequest(m_throttleClient); OnPacketStats = null; OnQueueEmpty = null; } @@ -216,6 +223,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Information about the client connection public ClientInfo GetClientInfo() { +/// + TokenBucket tb; + + tb = m_throttleClient.Parent; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT"); + + tb = m_throttleClient; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT"); + + tb = m_throttleCategory; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY"); + + for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) + { + tb = m_throttleCategories[i]; + m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET"); + } + +/// + // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists // of pending and needed ACKs for every client every time some method wants information about // this connection is a recipe for poor performance @@ -223,13 +250,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP info.pendingAcks = new Dictionary(); info.needAck = new Dictionary(); - info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; - info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; - info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; - info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; - info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; - info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; - info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; + info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; + info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; + info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; + info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; + // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; + info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + info.taskThrottle + info.assetThrottle + info.textureThrottle; @@ -317,8 +345,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); // State is a subcategory of task that we allocate a percentage to - int state = (int)((float)task * STATE_TASK_PERCENTAGE); - task -= state; + int state = 0; + // int state = (int)((float)task * STATE_TASK_PERCENTAGE); + // task -= state; // Make sure none of the throttles are set below our packet MTU, // otherwise a throttle could become permanently clogged @@ -339,40 +368,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Update the token buckets with new throttle values TokenBucket bucket; - bucket = m_throttle; - bucket.MaxBurst = total; + bucket = m_throttleCategory; + bucket.RequestedDripRate = total; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; - bucket.DripRate = resend; - bucket.MaxBurst = resend; + bucket.RequestedDripRate = resend; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; - bucket.DripRate = land; - bucket.MaxBurst = land; + bucket.RequestedDripRate = land; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; - bucket.DripRate = wind; - bucket.MaxBurst = wind; + bucket.RequestedDripRate = wind; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; - bucket.DripRate = cloud; - bucket.MaxBurst = cloud; + bucket.RequestedDripRate = cloud; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; - bucket.DripRate = asset; - bucket.MaxBurst = asset; + bucket.RequestedDripRate = asset; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; - bucket.DripRate = task + state; - bucket.MaxBurst = task + state; + bucket.RequestedDripRate = task; bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; - bucket.DripRate = state; - bucket.MaxBurst = state; + bucket.RequestedDripRate = state; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; - bucket.DripRate = texture; - bucket.MaxBurst = texture; + bucket.RequestedDripRate = texture; // Reset the packed throttles cached data m_packedThrottles = null; @@ -387,14 +408,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP data = new byte[7 * 4]; int i = 0; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + - m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4; m_packedThrottles = data; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 583214ca0b..d08b25f79d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -228,7 +228,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } #endregion BinaryStats - m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); + m_throttle = new TokenBucket(null, sceneThrottleBps); ThrottleRates = new ThrottleRates(configSource); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 0a8331f32a..e4d59ff829 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -26,6 +26,10 @@ */ using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using log4net; namespace OpenSim.Region.ClientStack.LindenUDP { @@ -35,89 +39,126 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public class TokenBucket { - /// Parent bucket to this bucket, or null if this is a root - /// bucket - TokenBucket parent; - /// Size of the bucket in bytes. If zero, the bucket has - /// infinite capacity - int maxBurst; - /// Rate that the bucket fills, in bytes per millisecond. If - /// zero, the bucket always remains full - int tokensPerMS; - /// Number of tokens currently in the bucket - int content; - /// Time of the last drip, in system ticks - int lastDrip; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static Int32 m_counter = 0; + + private Int32 m_identifier; + + /// + /// Number of ticks (ms) per quantum, drip rate and max burst + /// are defined over this interval. + /// + private const Int32 m_ticksPerQuantum = 1000; - #region Properties + /// + /// This is the number of quantums worth of packets that can + /// be accommodated during a burst + /// + private const Double m_quantumsPerBurst = 1.5; + + /// + /// + private const Int32 m_minimumDripRate = 1400; + + /// Time of the last drip, in system ticks + private Int32 m_lastDrip; + + /// + /// The number of bytes that can be sent at this moment. This is the + /// current number of tokens in the bucket + /// + private Int64 m_tokenCount; + + /// + /// Map of children buckets and their requested maximum burst rate + /// + private Dictionary m_children = new Dictionary(); + +#region Properties /// /// The parent bucket of this bucket, or null if this bucket has no /// parent. The parent bucket will limit the aggregate bandwidth of all /// of its children buckets /// + private TokenBucket m_parent; public TokenBucket Parent { - get { return parent; } + get { return m_parent; } + set { m_parent = value; } } /// /// Maximum burst rate in bytes per second. This is the maximum number - /// of tokens that can accumulate in the bucket at any one time + /// of tokens that can accumulate in the bucket at any one time. This + /// also sets the total request for leaf nodes /// - public int MaxBurst + private Int64 m_burstRate; + public Int64 RequestedBurstRate { - get { return maxBurst; } - set { maxBurst = (value >= 0 ? value : 0); } + get { return m_burstRate; } + set { m_burstRate = (value < 0 ? 0 : value); } } + public Int64 BurstRate + { + get { + double rate = RequestedBurstRate * BurstRateModifier(); + if (rate < m_minimumDripRate * m_quantumsPerBurst) + rate = m_minimumDripRate * m_quantumsPerBurst; + + return (Int64) rate; + } + } + /// /// The speed limit of this bucket in bytes per second. This is the - /// number of tokens that are added to the bucket per second + /// number of tokens that are added to the bucket per quantum /// /// Tokens are added to the bucket any time /// is called, at the granularity of /// the system tick interval (typically around 15-22ms) - public int DripRate + private Int64 m_dripRate; + public Int64 RequestedDripRate { - get { return tokensPerMS * 1000; } - set - { - if (value == 0) - tokensPerMS = 0; - else - { - int bpms = (int)((float)value / 1000.0f); + get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } + set { + m_dripRate = (value < 0 ? 0 : value); + m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); + m_totalDripRequest = m_dripRate; + if (m_parent != null) + m_parent.RegisterRequest(this,m_dripRate); + } + } - if (bpms <= 0) - tokensPerMS = 1; // 1 byte/ms is the minimum granularity - else - tokensPerMS = bpms; - } + public Int64 DripRate + { + get { + if (m_parent == null) + return Math.Min(RequestedDripRate,TotalDripRequest); + + double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); + if (rate < m_minimumDripRate) + rate = m_minimumDripRate; + + return (Int64)rate; } } /// - /// The speed limit of this bucket in bytes per millisecond + /// The current total of the requested maximum burst rates of + /// this bucket's children buckets. /// - public int DripPerMS - { - get { return tokensPerMS; } - } + private Int64 m_totalDripRequest; + public Int64 TotalDripRequest + { + get { return m_totalDripRequest; } + set { m_totalDripRequest = value; } + } + +#endregion Properties - /// - /// The number of bytes that can be sent at this moment. This is the - /// current number of tokens in the bucket - /// If this bucket has a parent bucket that does not have - /// enough tokens for a request, will - /// return false regardless of the content of this bucket - /// - public int Content - { - get { return content; } - } - - #endregion Properties +#region Constructor /// /// Default constructor @@ -128,56 +169,115 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// zero if this bucket has no maximum capacity /// Rate that the bucket fills, in bytes per /// second. If zero, the bucket always remains full - public TokenBucket(TokenBucket parent, int maxBurst, int dripRate) + public TokenBucket(TokenBucket parent, Int64 dripRate) { - this.parent = parent; - MaxBurst = maxBurst; - DripRate = dripRate; - lastDrip = Environment.TickCount & Int32.MaxValue; + m_identifier = m_counter++; + + Parent = parent; + RequestedDripRate = dripRate; + // TotalDripRequest = dripRate; // this will be overwritten when a child node registers + // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); + m_lastDrip = Environment.TickCount & Int32.MaxValue; } +#endregion Constructor + + /// + /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning + /// no modification if the requested bandwidth is less than the + /// max burst bandwidth all the way to the root of the throttle + /// hierarchy. However, if any of the parents is over-booked, then + /// the modifier will be less than 1. + /// + private double DripRateModifier() + { + Int64 driprate = DripRate; + return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; + } + + /// + /// + private double BurstRateModifier() + { + // for now... burst rate is always m_quantumsPerBurst (constant) + // larger than drip rate so the ratio of burst requests is the + // same as the drip ratio + return DripRateModifier(); + } + + /// + /// Register drip rate requested by a child of this throttle. Pass the + /// changes up the hierarchy. + /// + public void RegisterRequest(TokenBucket child, Int64 request) + { + m_children[child] = request; + // m_totalDripRequest = m_children.Values.Sum(); + + m_totalDripRequest = 0; + foreach (KeyValuePair cref in m_children) + m_totalDripRequest += cref.Value; + + // Pass the new values up to the parent + if (m_parent != null) + m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); + } + + /// + /// Remove the rate requested by a child of this throttle. Pass the + /// changes up the hierarchy. + /// + public void UnregisterRequest(TokenBucket child) + { + m_children.Remove(child); + // m_totalDripRequest = m_children.Values.Sum(); + + m_totalDripRequest = 0; + foreach (KeyValuePair cref in m_children) + m_totalDripRequest += cref.Value; + + // Pass the new values up to the parent + if (m_parent != null) + m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); + } + /// /// Remove a given number of tokens from the bucket /// /// Number of tokens to remove from the bucket /// True if the requested number of tokens were removed from /// the bucket, otherwise false - public bool RemoveTokens(int amount) + public bool RemoveTokens(Int64 amount) { - bool dummy; - return RemoveTokens(amount, out dummy); + // Deposit tokens for this interval + Drip(); + + // If we have enough tokens then remove them and return + if (m_tokenCount - amount >= 0) + { + if (m_parent == null || m_parent.RemoveTokens(amount)) + { + m_tokenCount -= amount; + return true; + } + } + + return false; } /// - /// Remove a given number of tokens from the bucket + /// Deposit tokens into the bucket from a child bucket that did + /// not use all of its available tokens /// - /// Number of tokens to remove from the bucket - /// True if tokens were added to the bucket - /// during this call, otherwise false - /// True if the requested number of tokens were removed from - /// the bucket, otherwise false - public bool RemoveTokens(int amount, out bool dripSucceeded) + private void Deposit(Int64 count) { - if (maxBurst == 0) - { - dripSucceeded = true; - return true; - } + m_tokenCount += count; - dripSucceeded = Drip(); - - if (content - amount >= 0) - { - if (parent != null && !parent.RemoveTokens(amount)) - return false; - - content -= amount; - return true; - } - else - { - return false; - } + // Deposit the overflow in the parent bucket, this is how we share + // unused bandwidth + Int64 burstrate = BurstRate; + if (m_tokenCount > burstrate) + m_tokenCount = burstrate; } /// @@ -186,37 +286,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// call to Drip /// /// True if tokens were added to the bucket, otherwise false - public bool Drip() + private void Drip() { - if (tokensPerMS == 0) + // This should never happen... means we are a leaf node and were created + // with no drip rate... + if (DripRate == 0) { - content = maxBurst; - return true; + m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0"); + return; } - else - { - int now = Environment.TickCount & Int32.MaxValue; - int deltaMS = now - lastDrip; + + // Determine the interval over which we are adding tokens, never add + // more than a single quantum of tokens + Int32 now = Environment.TickCount & Int32.MaxValue; + Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum); - if (deltaMS <= 0) - { - if (deltaMS < 0) - lastDrip = now; - return false; - } + m_lastDrip = now; - int dripAmount = deltaMS * tokensPerMS; + // This can be 0 in the very unusual case that the timer wrapped + // It can be 0 if we try add tokens at a sub-tick rate + if (deltaMS <= 0) + return; - content = Math.Min(content + dripAmount, maxBurst); - lastDrip = now; - - if (dripAmount < 0 || content < 0) - // sim has been idle for too long, integer has overflown - // previous calculation is meaningless, let's put it at correct max - content = maxBurst; - - return true; - } + Deposit(deltaMS * DripRate / m_ticksPerQuantum); } } } From 3fe22126ca3974b2a10365121ee0141fb54806e6 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 12:36:36 -0700 Subject: [PATCH 057/103] First pass at moving object property requests into a queue similar to the entity update queue. The number of property packets can become significant when selecting/deselecting large numbers of objects. This is experimental code. --- .../Client/MXP/ClientStack/MXPClientView.cs | 4 +- .../VWoHTTP/ClientStack/VWHClientView.cs | 4 +- OpenSim/Framework/IClientAPI.cs | 37 +- .../ClientStack/LindenUDP/LLClientView.cs | 367 +++++++++++------- .../ClientStack/LindenUDP/PriorityQueue.cs | 12 +- .../Examples/SimpleModule/MyNpcCharacter.cs | 12 +- .../Framework/Scenes/SceneObjectGroup.cs | 10 +- .../Framework/Scenes/SceneObjectPart.cs | 10 +- .../Server/IRCClientView.cs | 5 +- .../OptionalModules/World/NPC/NPCAvatar.cs | 12 +- OpenSim/Tests/Common/Mock/TestClient.cs | 11 +- 11 files changed, 269 insertions(+), 215 deletions(-) diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index d1a0440524..a604a2ee63 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -1337,12 +1337,12 @@ namespace OpenSim.Client.MXP.ClientStack // Need to translate to MXP somehow } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { //throw new System.NotImplementedException(); } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { //throw new System.NotImplementedException(); } diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index fc27f0198b..d8cd0acbd9 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -884,12 +884,12 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { throw new System.NotImplementedException(); } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { throw new System.NotImplementedException(); } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 5bf0b7b0a9..c56a756314 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -570,16 +570,30 @@ namespace OpenSim.Framework public float dwell; } - public class EntityUpdate + public class IEntityUpdate { public ISceneEntity Entity; - public PrimUpdateFlags Flags; - public float TimeDilation; + public uint Flags; - public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + public IEntityUpdate(ISceneEntity entity, uint flags) { Entity = entity; Flags = flags; + } + } + + + public class EntityUpdate : IEntityUpdate + { + // public ISceneEntity Entity; + // public PrimUpdateFlags Flags; + public float TimeDilation; + + public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + : base(entity,(uint)flags) + { + //Entity = entity; + // Flags = flags; TimeDilation = timedilation; } } @@ -1211,20 +1225,9 @@ namespace OpenSim.Framework /// void SendSimStats(SimStats stats); - void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, - uint Category, - UUID LastOwnerID, string ObjectName, string Description); + void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags); - void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, - UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, - string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, - uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice); + void SendObjectPropertiesReply(ISceneEntity Entity); void SendAgentOffline(UUID[] agentIDs); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 5980669651..33b99095a2 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -386,6 +386,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_cachedTextureSerial; private PriorityQueue m_entityUpdates; + private PriorityQueue m_entityProps; private Prioritizer m_prioritizer; private bool m_disableFacelights = false; @@ -433,9 +434,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected IAssetService m_assetService; private const bool m_checkPackets = true; - private Timer m_propertiesPacketTimer; - private List m_propertiesBlocks = new List(); - #endregion Class Members #region Properties @@ -511,6 +509,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene = scene; m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); + m_entityProps = new PriorityQueue(m_scene.Entities.Count); m_fullUpdateDataBlocksBuilder = new List(); m_killRecord = new HashSet(); // m_attachmentsSent = new HashSet(); @@ -534,9 +533,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_udpClient.OnQueueEmpty += HandleQueueEmpty; m_udpClient.OnPacketStats += PopulateStats; - m_propertiesPacketTimer = new Timer(100); - m_propertiesPacketTimer.Elapsed += ProcessObjectPropertiesPacket; - m_prioritizer = new Prioritizer(m_scene); RegisterLocalPacketHandlers(); @@ -3636,9 +3632,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); } - private Int32 m_LastQueueFill = 0; - private uint m_maxUpdates = 0; - private void ProcessEntityUpdates(int maxUpdates) { OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); @@ -3646,46 +3639,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.Lazy> terseUpdateBlocks = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy>(); + // Check to see if this is a flush if (maxUpdates <= 0) { - m_maxUpdates = Int32.MaxValue; + maxUpdates = Int32.MaxValue; } - else - { - if (m_maxUpdates == 0 || m_LastQueueFill == 0) - { - m_maxUpdates = (uint)maxUpdates; - } - else - { - if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) - m_maxUpdates += 5; - else - m_maxUpdates = m_maxUpdates >> 1; - } - m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); - } - m_LastQueueFill = Util.EnvironmentTickCount(); - + int updatesThisCall = 0; -// -// DEBUGGING CODE... REMOVE -// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates); -// // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race // condition where a kill can be processed before an out-of-date update for the same object. lock (m_killRecord) { float avgTimeDilation = 1.0f; - EntityUpdate update; + IEntityUpdate iupdate; Int32 timeinqueue; // this is just debugging code & can be dropped later - while (updatesThisCall < m_maxUpdates) + while (updatesThisCall < maxUpdates) { lock (m_entityUpdates.SyncRoot) - if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) + if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue)) break; + + EntityUpdate update = (EntityUpdate)iupdate; + avgTimeDilation += update.TimeDilation; avgTimeDilation *= 0.5f; @@ -3725,7 +3702,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region UpdateFlags to packet type conversion - PrimUpdateFlags updateFlags = update.Flags; + PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags; bool canUseCompressed = true; bool canUseImproved = true; @@ -3804,6 +3781,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Block Construction } + #region Packet Sending @@ -3904,12 +3882,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Primitive Packet/Data Sending Methods + // These are used to implement an adaptive backoff in the number + // of updates converted to packets. Since we don't want packets + // to sit in the queue with old data, only convert enough updates + // to packets that can be sent in 200ms. + private Int32 m_LastQueueFill = 0; + private Int32 m_maxUpdates = 0; + void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) { if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) { + if (m_maxUpdates == 0 || m_LastQueueFill == 0) + { + m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; + } + else + { + if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) + m_maxUpdates += 5; + else + m_maxUpdates = m_maxUpdates >> 1; + } + m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); + m_LastQueueFill = Util.EnvironmentTickCount(); + if (m_entityUpdates.Count > 0) - ProcessEntityUpdates(m_udpServer.PrimUpdatesPerCallback); + ProcessEntityUpdates(m_maxUpdates); + + if (m_entityProps.Count > 0) + ProcessEntityPropertyRequests(m_maxUpdates); } if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) @@ -4023,47 +4025,147 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) +/// ----------------------------------------------------------------- +/// +/// ----------------------------------------------------------------- + + private class ObjectPropertyUpdate : IEntityUpdate { - ObjectPropertiesFamilyPacket objPropFamilyPack = (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); - // TODO: don't create new blocks if recycling an old packet - - ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); - objPropDB.RequestFlags = RequestFlags; - objPropDB.ObjectID = ObjectUUID; - if (OwnerID == GroupID) - objPropDB.OwnerID = UUID.Zero; - else - objPropDB.OwnerID = OwnerID; - objPropDB.GroupID = GroupID; - objPropDB.BaseMask = BaseMask; - objPropDB.OwnerMask = OwnerMask; - objPropDB.GroupMask = GroupMask; - objPropDB.EveryoneMask = EveryoneMask; - objPropDB.NextOwnerMask = NextOwnerMask; - - // TODO: More properties are needed in SceneObjectPart! - objPropDB.OwnershipCost = OwnershipCost; - objPropDB.SaleType = SaleType; - objPropDB.SalePrice = SalePrice; - objPropDB.Category = Category; - objPropDB.LastOwnerID = LastOwnerID; - objPropDB.Name = Util.StringToBytes256(ObjectName); - objPropDB.Description = Util.StringToBytes256(Description); - objPropFamilyPack.ObjectData = objPropDB; - objPropFamilyPack.Header.Zerocoded = true; - OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task); + internal bool SendFamilyProps; + + public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam) + : base(entity,flags) + { + SendFamilyProps = sendfam; + } + } + + public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) + { + uint priority = m_prioritizer.GetUpdatePriority(this, entity); + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true)); } - public void SendObjectPropertiesReply( - UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) + { + uint priority = m_prioritizer.GetUpdatePriority(this, entity); + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false)); + } + + private void ProcessEntityPropertyRequests(int maxUpdates) + { + OpenSim.Framework.Lazy> objectFamilyBlocks = + new OpenSim.Framework.Lazy>(); + + OpenSim.Framework.Lazy> objectPropertiesBlocks = + new OpenSim.Framework.Lazy>(); + + IEntityUpdate iupdate; + Int32 timeinqueue; // this is just debugging code & can be dropped later + + int updatesThisCall = 0; + while (updatesThisCall < m_maxUpdates) + { + lock (m_entityProps.SyncRoot) + if (!m_entityProps.TryDequeue(out iupdate, out timeinqueue)) + break; + + ObjectPropertyUpdate update = (ObjectPropertyUpdate)iupdate; + if (update.SendFamilyProps) + { + if (update.Entity is SceneObjectPart) + { + SceneObjectPart sop = (SceneObjectPart)update.Entity; + ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); + objectFamilyBlocks.Value.Add(objPropDB); + } + } + else + { + if (update.Entity is SceneObjectPart) + { + SceneObjectPart sop = (SceneObjectPart)update.Entity; + ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); + objectPropertiesBlocks.Value.Add(objPropDB); + } + } + } + + + if (objectPropertiesBlocks.IsValueCreated) + { + List blocks = objectPropertiesBlocks.Value; + + ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); + packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; + for (int i = 0; i < blocks.Count; i++) + packet.ObjectData[i] = blocks[i]; + + packet.Header.Zerocoded = true; + OutPacket(packet, ThrottleOutPacketType.Task, true); + } + + + if (objectFamilyBlocks.IsValueCreated) + { + List blocks = objectFamilyBlocks.Value; + + // ObjectPropertiesFamilyPacket objPropFamilyPack = + // (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); + // + // objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count]; + // for (int i = 0; i < blocks.Count; i++) + // objPropFamilyPack.ObjectData[i] = blocks[i]; + // + // OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true); + + // one packet per object block... uggh... + for (int i = 0; i < blocks.Count; i++) + { + ObjectPropertiesFamilyPacket packet = + (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); + + packet.ObjectData = blocks[i]; + packet.Header.Zerocoded = true; + OutPacket(packet, ThrottleOutPacketType.Task); + } + + } + + } + + private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) + { + ObjectPropertiesFamilyPacket.ObjectDataBlock block = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); + + block.RequestFlags = requestFlags; + block.ObjectID = sop.UUID; + if (sop.OwnerID == sop.GroupID) + block.OwnerID = UUID.Zero; + else + block.OwnerID = sop.OwnerID; + block.GroupID = sop.GroupID; + block.BaseMask = sop.BaseMask; + block.OwnerMask = sop.OwnerMask; + block.GroupMask = sop.GroupMask; + block.EveryoneMask = sop.EveryoneMask; + block.NextOwnerMask = sop.NextOwnerMask; + + // TODO: More properties are needed in SceneObjectPart! + block.OwnershipCost = sop.OwnershipCost; + block.SaleType = sop.ObjectSaleType; + block.SalePrice = sop.SalePrice; + block.Category = sop.Category; + block.LastOwnerID = sop.CreatorID; // copied from old SOG call... is this right? + block.Name = Util.StringToBytes256(sop.Name); + block.Description = Util.StringToBytes256(sop.Description); + + return block; + } + + private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop) { //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); // TODO: don't create new blocks if recycling an old packet @@ -4071,85 +4173,43 @@ namespace OpenSim.Region.ClientStack.LindenUDP ObjectPropertiesPacket.ObjectDataBlock block = new ObjectPropertiesPacket.ObjectDataBlock(); - block.ItemID = ItemID; - block.CreationDate = CreationDate; - block.CreatorID = CreatorUUID; - block.FolderID = FolderUUID; - block.FromTaskID = FromTaskUUID; - block.GroupID = GroupUUID; - block.InventorySerial = InventorySerial; + block.ObjectID = sop.UUID; + block.Name = Util.StringToBytes256(sop.Name); + block.Description = Util.StringToBytes256(sop.Description); - block.LastOwnerID = LastOwnerUUID; - // proper.ObjectData[0].LastOwnerID = UUID.Zero; - - block.ObjectID = ObjectUUID; - if (OwnerUUID == GroupUUID) + block.CreationDate = (ulong)sop.CreationDate * 1000000; // viewer wants date in microseconds + block.CreatorID = sop.CreatorID; + block.GroupID = sop.GroupID; + block.LastOwnerID = sop.LastOwnerID; + if (sop.OwnerID == sop.GroupID) block.OwnerID = UUID.Zero; else - block.OwnerID = OwnerUUID; - block.TouchName = Util.StringToBytes256(TouchTitle); - block.TextureID = TextureID; - block.SitName = Util.StringToBytes256(SitTitle); - block.Name = Util.StringToBytes256(ItemName); - block.Description = Util.StringToBytes256(ItemDescription); - block.OwnerMask = OwnerMask; - block.NextOwnerMask = NextOwnerMask; - block.GroupMask = GroupMask; - block.EveryoneMask = EveryoneMask; - block.BaseMask = BaseMask; - // proper.ObjectData[0].AggregatePerms = 53; - // proper.ObjectData[0].AggregatePermTextures = 0; - // proper.ObjectData[0].AggregatePermTexturesOwner = 0; - block.SaleType = saleType; - block.SalePrice = salePrice; + block.OwnerID = sop.OwnerID; - lock (m_propertiesPacketTimer) - { - m_propertiesBlocks.Add(block); + block.ItemID = sop.FromUserInventoryItemID; + block.FolderID = UUID.Zero; // sop.FromFolderID ?? + block.FromTaskID = UUID.Zero; // ??? + block.InventorySerial = (short)sop.InventorySerial; + + SceneObjectPart root = sop.ParentGroup.RootPart; - int length = 0; - foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) - { - length += b.Length; - } - if (length > 1100) // FIXME: use real MTU - { - ProcessObjectPropertiesPacket(null, null); - m_propertiesPacketTimer.Stop(); - return; - } + block.TouchName = Util.StringToBytes256(root.TouchName); + block.TextureID = new byte[0]; // TextureID ??? + block.SitName = Util.StringToBytes256(root.SitName); + block.OwnerMask = root.OwnerMask; + block.NextOwnerMask = root.NextOwnerMask; + block.GroupMask = root.GroupMask; + block.EveryoneMask = root.EveryoneMask; + block.BaseMask = root.BaseMask; + block.SaleType = root.ObjectSaleType; + block.SalePrice = root.SalePrice; - m_propertiesPacketTimer.Stop(); - m_propertiesPacketTimer.Start(); - } - - //proper.Header.Zerocoded = true; - //OutPacket(proper, ThrottleOutPacketType.Task); + return block; } - private void ProcessObjectPropertiesPacket(Object sender, ElapsedEventArgs e) - { - ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); - - lock (m_propertiesPacketTimer) - { - m_propertiesPacketTimer.Stop(); - - proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count]; - - int index = 0; - - foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) - { - proper.ObjectData[index++] = b; - } - - m_propertiesBlocks.Clear(); - } - - proper.Header.Zerocoded = true; - OutPacket(proper, ThrottleOutPacketType.Task); - } +/// ----------------------------------------------------------------- +/// +/// ----------------------------------------------------------------- #region Estate Data Sending Methods @@ -4489,6 +4549,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendForceClientSelectObjects(List ObjectIDs) { + m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count); + bool firstCall = true; const int MAX_OBJECTS_PER_PACKET = 251; ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect); @@ -11381,6 +11443,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } + if (throttlePacketType == ThrottleOutPacketType.Task) + { + System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); // get call stack + System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) + + string stack = ""; + for (int count = 1; count < stackFrames.Length; count++) + { + stack += (stack == "" ? "" : ",") + stackFrames[count].GetMethod().Name; + if (count > 5) break; + } + + // m_log.WarnFormat("[BADGUY] {0}", stack); + } + m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 364ce4ba9e..6521a00745 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -78,7 +78,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public bool Enqueue(uint pqueue, EntityUpdate value) + public bool Enqueue(uint pqueue, IEntityUpdate value) { LookupItem lookup; @@ -99,7 +99,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return true; } - internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) + internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue) { for (int i = 0; i < m_numberOfQueues; ++i) { @@ -122,7 +122,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } timeinqueue = 0; - value = default(EntityUpdate); + value = default(IEntityUpdate); return false; } @@ -175,8 +175,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region MinHeapItem private struct MinHeapItem : IComparable { - private EntityUpdate value; - internal EntityUpdate Value { + private IEntityUpdate value; + internal IEntityUpdate Value { get { return this.value; } @@ -212,7 +212,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP this.pqueue = pqueue; } - internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) + internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value) { this.entrytime = Util.EnvironmentTickCount(); this.entryorder = entryorder; diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index d939329814..89e9e204bb 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -702,18 +702,12 @@ namespace OpenSim.Region.Examples.SimpleModule { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { + } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index ca7d9d9d57..30563d4232 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -1765,10 +1765,12 @@ namespace OpenSim.Region.Framework.Scenes /// public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags) { - remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, - RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, - RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, - RootPart.CreatorID, RootPart.Name, RootPart.Description); + remoteClient.SendObjectPropertiesFamilyData(RootPart, RequestFlags); + +// remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, +// RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, +// RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, +// RootPart.CreatorID, RootPart.Name, RootPart.Description); } public void SetPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index ce1e6b8614..8a8a6993cc 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2055,15 +2055,7 @@ namespace OpenSim.Region.Framework.Scenes public void GetProperties(IClientAPI client) { - //Viewer wants date in microseconds so multiply it by 1,000,000. - client.SendObjectPropertiesReply( - m_fromUserInventoryItemID, (ulong)_creationDate*(ulong)1e6, _creatorID, UUID.Zero, UUID.Zero, - _groupID, (short)InventorySerial, _lastOwnerID, UUID, _ownerID, - ParentGroup.RootPart.TouchName, new byte[0], ParentGroup.RootPart.SitName, Name, Description, - ParentGroup.RootPart._ownerMask, ParentGroup.RootPart._nextOwnerMask, ParentGroup.RootPart._groupMask, ParentGroup.RootPart._everyoneMask, - ParentGroup.RootPart._baseMask, - ParentGroup.RootPart.ObjectSaleType, - ParentGroup.RootPart.SalePrice); + client.SendObjectPropertiesReply(this); } public UUID GetRootPartUUID() diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 821cd4bd33..4b6e52e757 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -1332,14 +1332,13 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { - } public void SendAgentOffline(UUID[] agentIDs) diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 96760a2721..2504e3019c 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -786,18 +786,12 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { + } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index d1dc17fc9b..dca562674b 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -816,18 +816,11 @@ namespace OpenSim.Tests.Common.Mock { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } From 5e7aba4f88a90e266e01b5509c4ba04730815475 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 14:40:57 -0700 Subject: [PATCH 058/103] fixed a couple bugs with the property queues --- .../ClientStack/LindenUDP/LLClientView.cs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 33b99095a2..f926c3532f 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4091,9 +4091,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectPropertiesBlocks.Value.Add(objPropDB); } } + + updatesThisCall++; } + Int32 ppcnt = 0; + Int32 pbcnt = 0; + if (objectPropertiesBlocks.IsValueCreated) { List blocks = objectPropertiesBlocks.Value; @@ -4105,9 +4110,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Header.Zerocoded = true; OutPacket(packet, ThrottleOutPacketType.Task, true); + + pbcnt += blocks.Count; + ppcnt++; } + Int32 fpcnt = 0; + Int32 fbcnt = 0; + if (objectFamilyBlocks.IsValueCreated) { List blocks = objectFamilyBlocks.Value; @@ -4130,10 +4141,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.ObjectData = blocks[i]; packet.Header.Zerocoded = true; OutPacket(packet, ThrottleOutPacketType.Task); + + fpcnt++; + fbcnt++; } } + m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); + m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); } private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) @@ -11408,6 +11424,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, throttlePacketType, true); } +/// + Dictionary pktsrc = new Dictionary(); + uint pktcnt = 0; +/// + /// /// This is the starting point for sending a simulator packet out to the client /// @@ -11455,7 +11476,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (count > 5) break; } - // m_log.WarnFormat("[BADGUY] {0}", stack); + lock(pktsrc) + { + if (! pktsrc.ContainsKey(stack)) + pktsrc.Add(stack,0); + pktsrc[stack]++; + + if (++pktcnt > 500) + { + pktcnt = 0; + m_log.WarnFormat("[PACKETCOUNTS] START"); + foreach (KeyValuePair pkt in pktsrc) + m_log.WarnFormat("[PACKETCOUNTS] {0,8}, {1}", pkt.Value, pkt.Key); + pktsrc.Clear(); + m_log.WarnFormat("[PACKETCOUNTS] END"); + } + } } m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); From b33aac737a3e7ae28216cb98de99d9c6eba1d563 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 15:40:57 -0700 Subject: [PATCH 059/103] Fixed the update of items in the priority queue to enable both types of property updates to be specified. Not sure if one form of property update should supercede another. But for now the old OpenSim behavior is preserved by sending both. --- OpenSim/Framework/IClientAPI.cs | 5 +++++ .../ClientStack/LindenUDP/LLClientView.cs | 21 +++++++++++++------ .../ClientStack/LindenUDP/PriorityQueue.cs | 2 +- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index c56a756314..f573c32d07 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -575,6 +575,11 @@ namespace OpenSim.Framework public ISceneEntity Entity; public uint Flags; + public virtual void Update(IEntityUpdate update) + { + this.Flags |= update.Flags; + } + public IEntityUpdate(ISceneEntity entity, uint flags) { Entity = entity; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index f926c3532f..a6f2d09bb1 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4032,26 +4032,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP private class ObjectPropertyUpdate : IEntityUpdate { internal bool SendFamilyProps; + internal bool SendObjectProps; - public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam) + public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam, bool sendobj) : base(entity,flags) { SendFamilyProps = sendfam; + SendObjectProps = sendobj; + } + public void Update(ObjectPropertyUpdate update) + { + SendFamilyProps = SendFamilyProps || update.SendFamilyProps; + SendObjectProps = SendObjectProps || update.SendObjectProps; + Flags |= update.Flags; } } public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) { - uint priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = 0; // time based ordering only lock (m_entityProps.SyncRoot) - m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true)); + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); } public void SendObjectPropertiesReply(ISceneEntity entity) { - uint priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = 0; // time based ordering only lock (m_entityProps.SyncRoot) - m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false)); + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false,true)); } private void ProcessEntityPropertyRequests(int maxUpdates) @@ -4082,7 +4090,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectFamilyBlocks.Value.Add(objPropDB); } } - else + + if (update.SendObjectProps) { if (update.Entity is SceneObjectPart) { diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 6521a00745..b62ec07ed9 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -87,7 +87,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_lookupTable.TryGetValue(localid, out lookup)) { entry = lookup.Heap[lookup.Handle].EntryOrder; - value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; + value.Update(lookup.Heap[lookup.Handle].Value); lookup.Heap.Remove(lookup.Handle); } From bf91d1b07784f3c03744efd2d6dea1b621620cce Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 15:55:21 -0700 Subject: [PATCH 060/103] remove packet monitoring debugging code --- .../ClientStack/LindenUDP/LLClientView.cs | 118 +----------------- 1 file changed, 2 insertions(+), 116 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index a6f2d09bb1..613a921b4d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -300,77 +300,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Used to adjust Sun Orbit values so Linden based viewers properly position sun private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; - // First log file or time has expired, start writing to a new log file -// -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// THIS IS DEBUGGING CODE & SHOULD BE REMOVED -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- - public class QueueLogger - { - public Int32 start = 0; - public StreamWriter Log = null; - private Dictionary m_idMap = new Dictionary(); - - public QueueLogger() - { - DateTime now = DateTime.Now; - String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss")); - Log = new StreamWriter(fname); - - start = Util.EnvironmentTickCount(); - } - - public int LookupID(UUID uuid) - { - int localid; - if (! m_idMap.TryGetValue(uuid,out localid)) - { - localid = m_idMap.Count + 1; - m_idMap[uuid] = localid; - } - - return localid; - } - } - - public static QueueLogger QueueLog = null; - - // ----------------------------------------------------------------- - public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - int aid = QueueLog.LookupID(avatar); - QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue); - } - } - - // ----------------------------------------------------------------- - public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString()); - } - } -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected static Dictionary PacketHandlers = new Dictionary(); //Global/static handlers for all clients @@ -4025,10 +3954,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } -/// ----------------------------------------------------------------- -/// -/// ----------------------------------------------------------------- - private class ObjectPropertyUpdate : IEntityUpdate { internal bool SendFamilyProps; @@ -4157,8 +4082,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } - m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); - m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); + // m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); + // m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); } private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) @@ -4232,10 +4157,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return block; } -/// ----------------------------------------------------------------- -/// -/// ----------------------------------------------------------------- - #region Estate Data Sending Methods private static bool convertParamStringToBool(byte[] field) @@ -11433,11 +11354,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, throttlePacketType, true); } -/// - Dictionary pktsrc = new Dictionary(); - uint pktcnt = 0; -/// - /// /// This is the starting point for sending a simulator packet out to the client /// @@ -11472,36 +11388,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (logPacket) m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } - - if (throttlePacketType == ThrottleOutPacketType.Task) - { - System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); // get call stack - System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) - - string stack = ""; - for (int count = 1; count < stackFrames.Length; count++) - { - stack += (stack == "" ? "" : ",") + stackFrames[count].GetMethod().Name; - if (count > 5) break; - } - - lock(pktsrc) - { - if (! pktsrc.ContainsKey(stack)) - pktsrc.Add(stack,0); - pktsrc[stack]++; - - if (++pktcnt > 500) - { - pktcnt = 0; - m_log.WarnFormat("[PACKETCOUNTS] START"); - foreach (KeyValuePair pkt in pktsrc) - m_log.WarnFormat("[PACKETCOUNTS] {0,8}, {1}", pkt.Value, pkt.Key); - pktsrc.Clear(); - m_log.WarnFormat("[PACKETCOUNTS] END"); - } - } - } m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); } From 6ed6b46aad85b66dd3c50fcf17d108a8da42c5ff Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Wed, 13 Apr 2011 21:01:51 -0700 Subject: [PATCH 061/103] Comment out the warning if the prioritizer runs without a fully initialized scene presence. Not sure why we are dumping updates to a client that isn't there yet... --- OpenSim/Region/Framework/Scenes/Prioritizer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs index 4694e2b684..e3ed905c2f 100644 --- a/OpenSim/Region/Framework/Scenes/Prioritizer.cs +++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs @@ -166,7 +166,11 @@ namespace OpenSim.Region.Framework.Scenes ScenePresence presence = m_scene.GetScenePresence(client.AgentId); if (presence == null) { - m_log.WarnFormat("[PRIORITIZER] attempt to use agent {0} not in the scene",client.AgentId); + // this shouldn't happen, it basically means that we are prioritizing + // updates to send to a client that doesn't have a presence in the scene + // seems like there's race condition here... + + // m_log.WarnFormat("[PRIORITIZER] attempt to use agent {0} not in the scene",client.AgentId); // throw new InvalidOperationException("Prioritization agent not defined"); return Int32.MaxValue; } From aaf912586948a3d0d9bb918952766ba48e90fd18 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 14 Apr 2011 17:33:55 +0100 Subject: [PATCH 062/103] simplify coalesced object tests by using existing scene object set up utils this change makes it possible to set an absolute position on a group before it is put into a scene. --- .../Tests/InventoryAccessModuleTests.cs | 40 +++---------------- .../Framework/Scenes/SceneObjectGroup.cs | 21 ++++++---- 2 files changed, 20 insertions(+), 41 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs index c76a30af69..9fc77a74a6 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs @@ -87,41 +87,13 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests // log4net.Config.XmlConfigurator.Configure(); // Create asset - SceneObjectGroup object1; - { - string partName = "Object1"; - UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); - PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); - Vector3 groupPosition = new Vector3(10, 20, 30); - Quaternion rotationOffset = Quaternion.Identity; - Vector3 offsetPosition = new Vector3(5, 10, 15); - - SceneObjectPart part1 - = new SceneObjectPart( - ownerId, shape, groupPosition, rotationOffset, offsetPosition); - part1.Scale = new Vector3(1, 1, 1); - part1.Name = partName; - - object1 = new SceneObjectGroup(part1); - } + SceneObjectGroup object1 = SceneSetupHelpers.CreateSceneObject(1, m_userId, "Object1", 0x20); + object1.AbsolutePosition = new Vector3(15, 30, 45); + object1.Parts[0].Scale = new Vector3(1, 1, 1); - SceneObjectGroup object2; - { - string partName = "Object2"; - UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); - PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); - Vector3 groupPosition = new Vector3(20, 40, 60); - Quaternion rotationOffset = Quaternion.Identity; - Vector3 offsetPosition = new Vector3(5, 10, 15); - - SceneObjectPart part1 - = new SceneObjectPart( - ownerId, shape, groupPosition, rotationOffset, offsetPosition); - part1.Scale = new Vector3(1, 1, 1); - part1.Name = partName; - - object2 = new SceneObjectGroup(part1); - } + SceneObjectGroup object2 = SceneSetupHelpers.CreateSceneObject(1, m_userId, "Object2", 0x40); + object2.AbsolutePosition = new Vector3(25, 50, 75); + object2.Parts[0].Scale = new Vector3(1, 1, 1); CoalescedSceneObjects coa = new CoalescedSceneObjects(m_userId, object1, object2); diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index ca7d9d9d57..9bb50db921 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -296,11 +296,14 @@ namespace OpenSim.Region.Framework.Scenes { Vector3 val = value; - if ((m_scene.TestBorderCross(val - Vector3.UnitX, Cardinals.E) || m_scene.TestBorderCross(val + Vector3.UnitX, Cardinals.W) - || m_scene.TestBorderCross(val - Vector3.UnitY, Cardinals.N) || m_scene.TestBorderCross(val + Vector3.UnitY, Cardinals.S)) - && !IsAttachmentCheckFull() && (!m_scene.LoadingPrims)) + if (Scene != null) { - m_scene.CrossPrimGroupIntoNewRegion(val, this, true); + if ((Scene.TestBorderCross(val - Vector3.UnitX, Cardinals.E) || Scene.TestBorderCross(val + Vector3.UnitX, Cardinals.W) + || Scene.TestBorderCross(val - Vector3.UnitY, Cardinals.N) || Scene.TestBorderCross(val + Vector3.UnitY, Cardinals.S)) + && !IsAttachmentCheckFull() && (!Scene.LoadingPrims)) + { + m_scene.CrossPrimGroupIntoNewRegion(val, this, true); + } } if (RootPart.GetStatusSandbox()) @@ -308,8 +311,11 @@ namespace OpenSim.Region.Framework.Scenes if (Util.GetDistanceTo(RootPart.StatusSandboxPos, value) > 10) { RootPart.ScriptSetPhysicsStatus(false); - Scene.SimChat(Utils.StringToBytes("Hit Sandbox Limit"), - ChatTypeEnum.DebugChannel, 0x7FFFFFFF, RootPart.AbsolutePosition, Name, UUID, false); + + if (Scene != null) + Scene.SimChat(Utils.StringToBytes("Hit Sandbox Limit"), + ChatTypeEnum.DebugChannel, 0x7FFFFFFF, RootPart.AbsolutePosition, Name, UUID, false); + return; } } @@ -326,7 +332,8 @@ namespace OpenSim.Region.Framework.Scenes //m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor); //} - m_scene.EventManager.TriggerParcelPrimCountTainted(); + if (Scene != null) + Scene.EventManager.TriggerParcelPrimCountTainted(); } } From c644ab6a2c902c829a0dc88d08780a4411b4cd29 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 14 Apr 2011 20:21:44 +0100 Subject: [PATCH 063/103] Make scene object parts created by the test utils unit sized. --- .../InventoryAccess/Tests/InventoryAccessModuleTests.cs | 4 +--- OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs index 9fc77a74a6..60a932d764 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs @@ -89,11 +89,9 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests // Create asset SceneObjectGroup object1 = SceneSetupHelpers.CreateSceneObject(1, m_userId, "Object1", 0x20); object1.AbsolutePosition = new Vector3(15, 30, 45); - object1.Parts[0].Scale = new Vector3(1, 1, 1); SceneObjectGroup object2 = SceneSetupHelpers.CreateSceneObject(1, m_userId, "Object2", 0x40); - object2.AbsolutePosition = new Vector3(25, 50, 75); - object2.Parts[0].Scale = new Vector3(1, 1, 1); + object2.AbsolutePosition = new Vector3(25, 50, 75); CoalescedSceneObjects coa = new CoalescedSceneObjects(m_userId, object1, object2); diff --git a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs index 709dd78063..eb08b62f19 100644 --- a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs +++ b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs @@ -472,10 +472,10 @@ namespace OpenSim.Tests.Common.Setup /// /// public static SceneObjectPart CreateSceneObjectPart(string name, UUID id, UUID ownerId) - { + { return new SceneObjectPart( ownerId, PrimitiveBaseShape.Default, Vector3.Zero, Quaternion.Identity, Vector3.Zero) - { Name = name, UUID = id }; + { Name = name, UUID = id, Scale = new Vector3(1, 1, 1) }; } /// From e4392648248d11699772aac42db753ae4f84495f Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 14 Apr 2011 20:23:48 +0100 Subject: [PATCH 064/103] simplify TestRezObject() --- .../Tests/InventoryAccessModuleTests.cs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs index 60a932d764..7a7a8c23f7 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs @@ -143,23 +143,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests // log4net.Config.XmlConfigurator.Configure(); // Create asset - SceneObjectGroup object1; - SceneObjectPart part1; - { - string partName = "My Little Dog Object"; - UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); - PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); - Vector3 groupPosition = new Vector3(10, 20, 30); - Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); - Vector3 offsetPosition = new Vector3(5, 10, 15); - - part1 - = new SceneObjectPart( - ownerId, shape, groupPosition, rotationOffset, offsetPosition); - part1.Name = partName; - - object1 = new SceneObjectGroup(part1); - } + SceneObjectGroup object1 = SceneSetupHelpers.CreateSceneObject(1, m_userId, "My Little Dog Object", 0x40); UUID asset1Id = UUID.Parse("00000000-0000-0000-0000-000000000060"); AssetBase asset1 = AssetHelpers.CreateAsset(asset1Id, object1); From 1569148b49f188f3c46f9fd8ffa096efc70f72fa Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 14 Apr 2011 20:32:27 +0100 Subject: [PATCH 065/103] simplify TestSaveItemToIarV0_1() --- .../Archiver/Tests/InventoryArchiverTests.cs | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs index 7f156f8904..9e3e7d4e75 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs @@ -84,24 +84,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests UserProfileTestUtils.CreateUserWithInventory(m_scene, userFirstName, userLastName, userId, userPassword); // Create asset - SceneObjectGroup object1; - SceneObjectPart part1; - { - string partName = "My Little Dog Object"; - UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); - PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); - Vector3 groupPosition = new Vector3(10, 20, 30); - Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); - Vector3 offsetPosition = new Vector3(5, 10, 15); - - part1 - = new SceneObjectPart( - ownerId, shape, groupPosition, rotationOffset, offsetPosition); - part1.Name = partName; - - object1 = new SceneObjectGroup(part1); - m_scene.AddNewSceneObject(object1, false); - } + UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); + SceneObjectGroup object1 = SceneSetupHelpers.CreateSceneObject(1, ownerId, "My Little Dog Object", 0x50); UUID asset1Id = UUID.Parse("00000000-0000-0000-0000-000000000060"); AssetBase asset1 = AssetHelpers.CreateAsset(asset1Id, object1); From 55387542eaae8ab27c4709ae5b923d9d2d2a4431 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 14 Apr 2011 20:35:00 +0100 Subject: [PATCH 066/103] simplify InventoryArchiveTestCase setup --- .../Tests/InventoryArchiveTestCase.cs | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs index e5127a0118..7ee6f28285 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs @@ -105,24 +105,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests MemoryStream archiveWriteStream = new MemoryStream(); // Create asset - SceneObjectGroup object1; - SceneObjectPart part1; - { - string partName = "Ray Gun Object"; - UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); - PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); - Vector3 groupPosition = new Vector3(10, 20, 30); - Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); - Vector3 offsetPosition = new Vector3(5, 10, 15); - - part1 - = new SceneObjectPart( - ownerId, shape, groupPosition, rotationOffset, offsetPosition); - part1.Name = partName; - - object1 = new SceneObjectGroup(part1); - scene.AddNewSceneObject(object1, false); - } + UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); + SceneObjectGroup object1 = SceneSetupHelpers.CreateSceneObject(1, ownerId, "Ray Gun Object", 0x50); UUID asset1Id = UUID.Parse("00000000-0000-0000-0000-000000000060"); AssetBase asset1 = AssetHelpers.CreateAsset(asset1Id, object1); From 821e67fb950721c0bb8105318e41d7982cf986f2 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 14 Apr 2011 20:59:52 +0100 Subject: [PATCH 067/103] implement stub TestLoadCoalesecedItem(). Doesn't do what it's meant to do yet. --- .../Tests/InventoryArchiveTestCase.cs | 35 ++++++++++++++++--- .../Archiver/Tests/InventoryArchiverTests.cs | 17 +++++++++ OpenSim/Tests/Common/Setup/AssetHelpers.cs | 18 ++++++++-- 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs index 7ee6f28285..f2d050c16a 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs @@ -68,17 +68,21 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests PrincipalID = UUID.Parse("00000000-0000-0000-0000-000000000555"), FirstName = "Mr", LastName = "Tiddles" }; + protected UserAccount m_uaLL1 = new UserAccount { PrincipalID = UUID.Parse("00000000-0000-0000-0000-000000000666"), FirstName = "Lord", LastName = "Lucan" }; + protected UserAccount m_uaLL2 = new UserAccount { PrincipalID = UUID.Parse("00000000-0000-0000-0000-000000000777"), FirstName = "Lord", - LastName = "Lucan" }; + LastName = "Lucan" }; + protected string m_item1Name = "Ray Gun Item"; + protected string m_coaItemName = "Coalesced Item"; [SetUp] public virtual void SetUp() @@ -104,7 +108,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests MemoryStream archiveWriteStream = new MemoryStream(); - // Create asset + // Create scene object asset UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); SceneObjectGroup object1 = SceneSetupHelpers.CreateSceneObject(1, ownerId, "Ray Gun Object", 0x50); @@ -112,7 +116,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests AssetBase asset1 = AssetHelpers.CreateAsset(asset1Id, object1); scene.AssetService.Store(asset1); - // Create item + // Create scene object item InventoryItemBase item1 = new InventoryItemBase(); item1.Name = m_item1Name; item1.ID = UUID.Parse("00000000-0000-0000-0000-000000000020"); @@ -123,8 +127,31 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests item1.Folder = scene.InventoryService.GetRootFolder(m_uaLL1.PrincipalID).ID; scene.AddInventoryItem(item1); + // Create coalesced objects asset + SceneObjectGroup cobj1 = SceneSetupHelpers.CreateSceneObject(1, m_uaLL1.PrincipalID, "Object1", 0x120); + cobj1.AbsolutePosition = new Vector3(15, 30, 45); + + SceneObjectGroup cobj2 = SceneSetupHelpers.CreateSceneObject(1, m_uaLL1.PrincipalID, "Object2", 0x140); + cobj2.AbsolutePosition = new Vector3(25, 50, 75); + + CoalescedSceneObjects coa = new CoalescedSceneObjects(m_uaLL1.PrincipalID, cobj1, cobj2); + + AssetBase coaAsset = AssetHelpers.CreateAsset(0x160, coa); + scene.AssetService.Store(coaAsset); + + // Create coalesced objects inventory item + InventoryItemBase coaItem = new InventoryItemBase(); + coaItem.Name = m_coaItemName; + coaItem.ID = UUID.Parse("00000000-0000-0000-0000-000000000180"); + coaItem.AssetID = asset1.FullID; + coaItem.GroupID = UUID.Random(); + coaItem.CreatorIdAsUuid = m_uaLL1.PrincipalID; + coaItem.Owner = m_uaLL1.PrincipalID; + coaItem.Folder = scene.InventoryService.GetRootFolder(m_uaLL1.PrincipalID).ID; + scene.AddInventoryItem(coaItem); + archiverModule.ArchiveInventory( - Guid.NewGuid(), m_uaLL1.FirstName, m_uaLL1.LastName, m_item1Name, "hampshire", archiveWriteStream); + Guid.NewGuid(), m_uaLL1.FirstName, m_uaLL1.LastName, "/*", "hampshire", archiveWriteStream); m_iarStreamBytes = archiveWriteStream.ToArray(); } diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs index 9e3e7d4e75..0c4e364765 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs @@ -257,5 +257,22 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaMT.PrincipalID)); } + + [Test] + public void TestLoadCoalesecedItem() + { + TestHelper.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + UserProfileTestUtils.CreateUserWithInventory(m_scene, m_uaLL1, "password"); + m_archiverModule.DearchiveInventory(m_uaLL1.FirstName, m_uaLL1.LastName, "/", "password", m_iarStream); + + InventoryItemBase coaItem + = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaLL1.PrincipalID, m_coaItemName); + + Assert.That(coaItem, Is.Not.Null, "Didn't find loaded item 1"); + + // TODO: Check that the loaded coalesence is valid and that the required scene object assets are around + } } } \ No newline at end of file diff --git a/OpenSim/Tests/Common/Setup/AssetHelpers.cs b/OpenSim/Tests/Common/Setup/AssetHelpers.cs index af66d7f0a7..971c6bcf36 100644 --- a/OpenSim/Tests/Common/Setup/AssetHelpers.cs +++ b/OpenSim/Tests/Common/Setup/AssetHelpers.cs @@ -55,7 +55,7 @@ namespace OpenSim.Tests.Common AssetBase asset = CreateAsset(UUID.Random(), AssetType.Notecard, "hello", creatorId); scene.AssetService.Store(asset); return asset; - } + } /// /// Create an asset from the given scene object. @@ -72,6 +72,20 @@ namespace OpenSim.Tests.Common sog.OwnerID); } + /// + /// Create an asset from the given scene object. + /// + /// + /// The hexadecimal last part of the UUID for the asset created. A UUID of the form "00000000-0000-0000-0000-{0:XD12}" + /// will be used. + /// + /// + /// + public static AssetBase CreateAsset(int assetUuidTail, CoalescedSceneObjects coa) + { + return CreateAsset(new UUID(string.Format("00000000-0000-0000-0000-{0:X12}", assetUuidTail)), coa); + } + /// /// Create an asset from the given scene object. /// @@ -85,7 +99,7 @@ namespace OpenSim.Tests.Common AssetType.Object, Encoding.ASCII.GetBytes(CoalescedSceneObjectsSerializer.ToXml(coa)), coa.CreatorId); - } + } /// /// Create an asset from the given data. From a0d80140f2d0ad6dd63970a3a3d5c0da8320ae75 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 15 Apr 2011 00:42:06 +0100 Subject: [PATCH 068/103] Make all the objects in a coalescence reappears after being loaded from an IAR. This still doesn't work proprerly since some required textures/contained item assets might be missing. From pure code inspection, it looks like the uuid gatherer may get most asset uuids because the scene object serializer naively pulls non-root parts from all contained scene objects into one mega-object. However, root part uuids may well still be missing, and there may be other odd artifacts from this bug. It appears that storing the size of the coalescence and the offsets is redundant, since one can work out this information from the position data already in the scene object groups. --- .../Archiver/InventoryArchiveReadRequest.cs | 28 ++++++++--- .../Tests/InventoryArchiveTestCase.cs | 2 +- .../Archiver/Tests/InventoryArchiverTests.cs | 43 ++++++++++------- .../Asset/LocalAssetServiceConnector.cs | 2 + .../CoalescedSceneObjectsSerializer.cs | 47 ++++++++++++++++--- OpenSim/Services/AssetService/AssetService.cs | 10 +++- OpenSim/Services/Interfaces/IAssetService.cs | 5 ++ OpenSim/Tests/Common/Mock/MockAssetService.cs | 22 +++++++-- OpenSim/Tests/Common/Setup/AssetHelpers.cs | 7 +++ .../Tests/Common/Setup/SceneSetupHelpers.cs | 7 ++- 10 files changed, 134 insertions(+), 39 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs index 01170aaf65..f3d2f26cfe 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs @@ -471,16 +471,30 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver if (m_creatorIdForAssetId.ContainsKey(assetId)) { string xmlData = Utils.BytesToString(data); - SceneObjectGroup sog = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); - foreach (SceneObjectPart sop in sog.Parts) + List sceneObjects = new List(); + + CoalescedSceneObjects coa = null; + if (CoalescedSceneObjectsSerializer.TryFromXml(xmlData, out coa)) { - if (sop.CreatorData == null || sop.CreatorData == "") - { - sop.CreatorID = m_creatorIdForAssetId[assetId]; - } +// m_log.DebugFormat( +// "[INVENTORY ARCHIVER]: Loaded coalescence {0} has {1} objects", assetId, coa.Count); + + sceneObjects.AddRange(coa.Objects); + } + else + { + sceneObjects.Add(SceneObjectSerializer.FromOriginalXmlFormat(xmlData)); } - data = Utils.StringToBytes(SceneObjectSerializer.ToOriginalXmlFormat(sog)); + foreach (SceneObjectGroup sog in sceneObjects) + foreach (SceneObjectPart sop in sog.Parts) + if (sop.CreatorData == null || sop.CreatorData == "") + sop.CreatorID = m_creatorIdForAssetId[assetId]; + + if (coa != null) + data = Utils.StringToBytes(CoalescedSceneObjectsSerializer.ToXml(coa)); + else + data = Utils.StringToBytes(SceneObjectSerializer.ToOriginalXmlFormat(sceneObjects[0])); } } diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs index f2d050c16a..e2316f0bdd 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs @@ -143,7 +143,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests InventoryItemBase coaItem = new InventoryItemBase(); coaItem.Name = m_coaItemName; coaItem.ID = UUID.Parse("00000000-0000-0000-0000-000000000180"); - coaItem.AssetID = asset1.FullID; + coaItem.AssetID = coaAsset.FullID; coaItem.GroupID = UUID.Random(); coaItem.CreatorIdAsUuid = m_uaLL1.PrincipalID; coaItem.Owner = m_uaLL1.PrincipalID; diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs index 0c4e364765..f747c899de 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs @@ -62,9 +62,33 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests SerialiserModule serialiserModule = new SerialiserModule(); m_archiverModule = new InventoryArchiverModule(); - m_scene = SceneSetupHelpers.SetupScene("Inventory"); + m_scene = SceneSetupHelpers.SetupScene("asset inventory"); SceneSetupHelpers.SetupSceneModules(m_scene, serialiserModule, m_archiverModule); } + + [Test] + public void TestLoadCoalesecedItem() + { + TestHelper.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + UserProfileTestUtils.CreateUserWithInventory(m_scene, m_uaLL1, "password"); + m_archiverModule.DearchiveInventory(m_uaLL1.FirstName, m_uaLL1.LastName, "/", "password", m_iarStream); + + InventoryItemBase coaItem + = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaLL1.PrincipalID, m_coaItemName); + + Assert.That(coaItem, Is.Not.Null, "Didn't find loaded item 1"); + + string assetXml = AssetHelpers.ReadAssetAsString(m_scene.AssetService, coaItem.AssetID); + + CoalescedSceneObjects coa; + bool readResult = CoalescedSceneObjectsSerializer.TryFromXml(assetXml, out coa); + + Assert.That(readResult, Is.True); + + // TODO: Check that the loaded coalesence is valid and that the required scene object assets are around + } /// /// Test saving a single inventory item to a V0.1 OpenSim Inventory Archive @@ -257,22 +281,5 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaMT.PrincipalID)); } - - [Test] - public void TestLoadCoalesecedItem() - { - TestHelper.InMethod(); -// log4net.Config.XmlConfigurator.Configure(); - - UserProfileTestUtils.CreateUserWithInventory(m_scene, m_uaLL1, "password"); - m_archiverModule.DearchiveInventory(m_uaLL1.FirstName, m_uaLL1.LastName, "/", "password", m_iarStream); - - InventoryItemBase coaItem - = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaLL1.PrincipalID, m_coaItemName); - - Assert.That(coaItem, Is.Not.Null, "Didn't find loaded item 1"); - - // TODO: Check that the loaded coalesence is valid and that the required scene object assets are around - } } } \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs index 1b3419d12f..51d1d599c5 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs @@ -195,6 +195,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset public byte[] GetData(string id) { +// m_log.DebugFormat("[LOCAL ASSET SERVICES CONNECTOR]: Requesting data for asset {0}", id); + AssetBase asset = m_Cache.Get(id); if (asset != null) diff --git a/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs index a0e120a9ac..babcb54ae9 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs @@ -55,11 +55,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization /// /// public static string ToXml(CoalescedSceneObjects coa) - { - // TODO: Should probably return an empty xml serialization rather than a blank string - if (!coa.HasObjects) - return ""; - + { using (StringWriter sw = new StringWriter()) { using (XmlTextWriter writer = new XmlTextWriter(sw)) @@ -105,10 +101,49 @@ namespace OpenSim.Region.Framework.Scenes.Serialization string output = sw.ToString(); -// m_log.Debug(output); +// Console.WriteLine(output); return output; } } + + public static bool TryFromXml(string xml, out CoalescedSceneObjects coa) + { +// m_log.DebugFormat("[COALESCED SCENE OBJECTS SERIALIZER]: TryFromXml() deserializing {0}", xml); + + coa = null; + + using (StringReader sr = new StringReader(xml)) + { + using (XmlTextReader reader = new XmlTextReader(sr)) + { + reader.Read(); + if (reader.Name != "CoalescedObject") + { +// m_log.DebugFormat( +// "[COALESCED SCENE OBJECTS SERIALIZER]: TryFromXml() root element was {0} so returning false", +// reader.Name); + + return false; + } + + coa = new CoalescedSceneObjects(UUID.Zero); + reader.Read(); + + while (reader.NodeType != XmlNodeType.EndElement && reader.Name != "CoalescedObject") + { + if (reader.Name == "SceneObjectGroup") + { + string soXml = reader.ReadOuterXml(); + coa.Add(SceneObjectSerializer.FromOriginalXmlFormat(soXml)); + } + } + + reader.ReadEndElement(); // CoalescedObject + } + } + + return true; + } } } \ No newline at end of file diff --git a/OpenSim/Services/AssetService/AssetService.cs b/OpenSim/Services/AssetService/AssetService.cs index a81af4355a..e1f90b6e18 100644 --- a/OpenSim/Services/AssetService/AssetService.cs +++ b/OpenSim/Services/AssetService/AssetService.cs @@ -89,6 +89,8 @@ namespace OpenSim.Services.AssetService public virtual AssetBase Get(string id) { +// m_log.DebugFormat("[ASSET SERVICE]: Get asset for {0}", id); + UUID assetID; if (!UUID.TryParse(id, out assetID)) @@ -107,6 +109,8 @@ namespace OpenSim.Services.AssetService public virtual AssetMetadata GetMetadata(string id) { +// m_log.DebugFormat("[ASSET SERVICE]: Get asset metadata for {0}", id); + UUID assetID; if (!UUID.TryParse(id, out assetID)) @@ -121,6 +125,8 @@ namespace OpenSim.Services.AssetService public virtual byte[] GetData(string id) { +// m_log.DebugFormat("[ASSET SERVICE]: Get asset data for {0}", id); + UUID assetID; if (!UUID.TryParse(id, out assetID)) @@ -150,7 +156,9 @@ namespace OpenSim.Services.AssetService public virtual string Store(AssetBase asset) { - //m_log.DebugFormat("[ASSET SERVICE]: Store asset {0} {1}", asset.Name, asset.ID); +// m_log.DebugFormat( +// "[ASSET SERVICE]: Storing asset {0} {1}, bytes {2}", asset.Name, asset.ID, asset.Data.Length); + m_Database.StoreAsset(asset); return asset.ID; diff --git a/OpenSim/Services/Interfaces/IAssetService.cs b/OpenSim/Services/Interfaces/IAssetService.cs index 3be6815728..1ac1cec358 100644 --- a/OpenSim/Services/Interfaces/IAssetService.cs +++ b/OpenSim/Services/Interfaces/IAssetService.cs @@ -48,6 +48,11 @@ namespace OpenSim.Services.Interfaces /// AssetMetadata GetMetadata(string id); + /// + /// Get an asset's data, ignoring the metadata. + /// + /// + /// null if there is no such asset byte[] GetData(string id); /// diff --git a/OpenSim/Tests/Common/Mock/MockAssetService.cs b/OpenSim/Tests/Common/Mock/MockAssetService.cs index 4118308bed..9d7c5e29e6 100644 --- a/OpenSim/Tests/Common/Mock/MockAssetService.cs +++ b/OpenSim/Tests/Common/Mock/MockAssetService.cs @@ -54,13 +54,19 @@ namespace OpenSim.Tests.Common.Mock public AssetBase Get(string id) { - m_log.DebugFormat("[MOCK ASSET SERVICE]: Getting asset with id {0}", id); +// m_log.DebugFormat("[MOCK ASSET SERVICE]: Getting asset with id {0}", id); AssetBase asset; if (Assets.ContainsKey(id)) + { asset = Assets[id]; +// m_log.DebugFormat( +// "[MOCK ASSET SERVICE]: Got asset {0} {1}, bytes {2}", asset.Name, asset.ID, asset.Data.Length); + } else - asset = null; + { + asset = null; + } return asset; } @@ -77,7 +83,14 @@ namespace OpenSim.Tests.Common.Mock public byte[] GetData(string id) { - throw new System.NotImplementedException(); +// m_log.DebugFormat("[MOCK ASSET SERVICE]: Requesting data for asset {0}", id); + + AssetBase asset = Get(id); + + if (asset == null) + return null; + else + return asset.Data; } public bool Get(string id, object sender, AssetRetrieved handler) @@ -89,7 +102,8 @@ namespace OpenSim.Tests.Common.Mock public string Store(AssetBase asset) { - m_log.DebugFormat("[MOCK ASSET SERVICE]: Storing asset {0}", asset.ID); +// m_log.DebugFormat( +// "[MOCK ASSET SERVICE]: Storing asset {0} {1}, bytes {2}", asset.Name, asset.ID, asset.Data.Length); Assets[asset.ID] = asset; diff --git a/OpenSim/Tests/Common/Setup/AssetHelpers.cs b/OpenSim/Tests/Common/Setup/AssetHelpers.cs index 971c6bcf36..d572249997 100644 --- a/OpenSim/Tests/Common/Setup/AssetHelpers.cs +++ b/OpenSim/Tests/Common/Setup/AssetHelpers.cs @@ -30,6 +30,7 @@ using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes.Serialization; +using OpenSim.Services.Interfaces; namespace OpenSim.Tests.Common { @@ -118,5 +119,11 @@ namespace OpenSim.Tests.Common asset.Data = data; return asset; } + + public static string ReadAssetAsString(IAssetService assetService, UUID uuid) + { + byte[] assetData = assetService.GetData(uuid.ToString()); + return Encoding.ASCII.GetString(assetData); + } } } diff --git a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs index eb08b62f19..837f4e235f 100644 --- a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs +++ b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs @@ -168,14 +168,17 @@ namespace OpenSim.Tests.Common.Setup { LocalAssetServicesConnector assetService = new LocalAssetServicesConnector(); IConfigSource config = new IniConfigSource(); - config.AddConfig("Modules"); - config.AddConfig("AssetService"); + config.AddConfig("Modules"); config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector"); + + config.AddConfig("AssetService"); if (real) config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService"); else config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Tests.Common.dll:MockAssetService"); + config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll"); + assetService.Initialise(config); assetService.AddRegion(testScene); assetService.RegionLoaded(testScene); From ad70cddcfc48182f75be7643297cea2a9d20ac91 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 15 Apr 2011 00:56:40 +0100 Subject: [PATCH 069/103] extend TestLoadCoalesecedItem() to check loaded object ids and positions --- .../Inventory/Archiver/Tests/InventoryArchiverTests.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs index f747c899de..69c5dcdab1 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs @@ -86,8 +86,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests bool readResult = CoalescedSceneObjectsSerializer.TryFromXml(assetXml, out coa); Assert.That(readResult, Is.True); + Assert.That(coa.Count, Is.EqualTo(2)); - // TODO: Check that the loaded coalesence is valid and that the required scene object assets are around + List coaObjects = coa.Objects; + Assert.That(coaObjects[0].UUID, Is.EqualTo(UUID.Parse("00000000-0000-0000-0000-000000000120"))); + Assert.That(coaObjects[0].AbsolutePosition, Is.EqualTo(new Vector3(15, 30, 45))); + + Assert.That(coaObjects[1].UUID, Is.EqualTo(UUID.Parse("00000000-0000-0000-0000-000000000140"))); + Assert.That(coaObjects[1].AbsolutePosition, Is.EqualTo(new Vector3(25, 50, 75))); } /// From 79bd430e08b9e8612194d9b02504f8f4acd889f6 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 15 Apr 2011 01:10:26 +0100 Subject: [PATCH 070/103] Remove the mock inventory service for tests and just use the real one all the time with an in-memory data plugin --- .../Tests/Common/Mock/MockInventoryService.cs | 186 ------------------ .../Tests/Common/Setup/SceneSetupHelpers.cs | 19 +- 2 files changed, 6 insertions(+), 199 deletions(-) delete mode 100644 OpenSim/Tests/Common/Mock/MockInventoryService.cs diff --git a/OpenSim/Tests/Common/Mock/MockInventoryService.cs b/OpenSim/Tests/Common/Mock/MockInventoryService.cs deleted file mode 100644 index 4ac1078795..0000000000 --- a/OpenSim/Tests/Common/Mock/MockInventoryService.cs +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using OpenSim.Framework; -using OpenMetaverse; -using OpenSim.Services.Interfaces; -using Nini.Config; - -namespace OpenSim.Tests.Common.Mock -{ - public class MockInventoryService : IInventoryService - { - public MockInventoryService() {} - - public MockInventoryService(IConfigSource config) {} - - /// - /// - /// - /// - /// - public bool CreateUserInventory(UUID userId) - { - return false; - } - - /// - /// - /// - /// - /// - public List GetInventorySkeleton(UUID userId) - { - List folders = new List(); - InventoryFolderBase folder = new InventoryFolderBase(); - folder.ID = UUID.Random(); - folder.Owner = userId; - folders.Add(folder); - return folders; - } - - public InventoryFolderBase GetRootFolder(UUID userID) - { - return new InventoryFolderBase(); - } - - public InventoryCollection GetFolderContent(UUID userID, UUID folderID) - { - return null; - } - - public InventoryFolderBase GetFolderForType(UUID userID, AssetType type) - { - return null; - } - - /// - /// Returns a list of all the active gestures in a user's inventory. - /// - /// - /// The of the user - /// - /// - /// A flat list of the gesture items. - /// - public List GetActiveGestures(UUID userId) - { - return null; - } - - public InventoryCollection GetUserInventory(UUID userID) - { - return null; - } - - public void GetUserInventory(UUID userID, OpenSim.Services.Interfaces.InventoryReceiptCallback callback) - { - } - - public List GetFolderItems(UUID userID, UUID folderID) - { - return null; - } - - public bool AddFolder(InventoryFolderBase folder) - { - return false; - } - - public bool UpdateFolder(InventoryFolderBase folder) - { - return false; - } - - public bool MoveFolder(InventoryFolderBase folder) - { - return false; - } - - public bool DeleteFolders(UUID ownerID, List ids) - { - return false; - } - - public bool PurgeFolder(InventoryFolderBase folder) - { - return false; - } - - public bool AddItem(InventoryItemBase item) - { - return true; - } - - public bool UpdateItem(InventoryItemBase item) - { - return false; - } - - public bool MoveItems(UUID ownerID, List items) - { - return false; - } - - public bool DeleteItems(UUID ownerID, List itemIDs) - { - return false; - } - - public InventoryItemBase GetItem(InventoryItemBase item) - { - return null; - } - - public InventoryFolderBase GetFolder(InventoryFolderBase folder) - { - return null; - } - - public bool HasInventoryForUser(UUID userID) - { - return false; - } - - public InventoryFolderBase RequestRootFolder(UUID userID) - { - InventoryFolderBase root = new InventoryFolderBase(); - root.ID = UUID.Random(); - root.Owner = userID; - root.ParentID = UUID.Zero; - return root; - } - - public int GetAssetPermissions(UUID userID, UUID assetID) - { - return 1; - } - } -} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs index 837f4e235f..97d4acefd0 100644 --- a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs +++ b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs @@ -137,7 +137,7 @@ namespace OpenSim.Tests.Common.Setup // For now, always started a 'real' authentication service StartAuthenticationService(testScene, true); - LocalInventoryServicesConnector inventoryService = StartInventoryService(testScene, realServices.Contains("inventory")); + LocalInventoryServicesConnector inventoryService = StartInventoryService(testScene); StartGridService(testScene, true); LocalUserAccountServicesConnector userAccountService = StartUserAccountService(testScene); LocalPresenceServicesConnector presenceService = StartPresenceService(testScene); @@ -208,24 +208,17 @@ namespace OpenSim.Tests.Common.Setup //m_authenticationService = service; } - private static LocalInventoryServicesConnector StartInventoryService(Scene testScene, bool real) + private static LocalInventoryServicesConnector StartInventoryService(Scene testScene) { LocalInventoryServicesConnector inventoryService = new LocalInventoryServicesConnector(); - IConfigSource config = new IniConfigSource(); + + IConfigSource config = new IniConfigSource(); config.AddConfig("Modules"); config.AddConfig("InventoryService"); config.Configs["Modules"].Set("InventoryServices", "LocalInventoryServicesConnector"); - - if (real) - { - config.Configs["InventoryService"].Set("LocalServiceModule", "OpenSim.Services.InventoryService.dll:InventoryService"); - } - else - { - config.Configs["InventoryService"].Set("LocalServiceModule", "OpenSim.Tests.Common.dll:MockInventoryService"); - } - + config.Configs["InventoryService"].Set("LocalServiceModule", "OpenSim.Services.InventoryService.dll:InventoryService"); config.Configs["InventoryService"].Set("StorageProvider", "OpenSim.Tests.Common.dll"); + inventoryService.Initialise(config); inventoryService.AddRegion(testScene); inventoryService.RegionLoaded(testScene); From 54e885581989ddfaf525753ede12c4d4d797cba0 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 15 Apr 2011 01:23:26 +0100 Subject: [PATCH 071/103] remove mock asset service for tests in favour of always using the real one --- .../Scenes/Tests/UuidGathererTests.cs | 4 +- OpenSim/Tests/Common/Mock/MockAssetService.cs | 123 ------------------ .../Tests/Common/Setup/SceneSetupHelpers.cs | 14 +- 3 files changed, 8 insertions(+), 133 deletions(-) delete mode 100644 OpenSim/Tests/Common/Mock/MockAssetService.cs diff --git a/OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs b/OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs index 6b70865533..dbf9e0f550 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs @@ -47,7 +47,9 @@ namespace OpenSim.Region.Framework.Scenes.Tests [SetUp] public void Init() { - m_assetService = new MockAssetService(); + // FIXME: We don't need a full scene here - it would be enough to set up the asset service. + Scene scene = SceneSetupHelpers.SetupScene(); + m_assetService = scene.AssetService; m_uuidGatherer = new UuidGatherer(m_assetService); } diff --git a/OpenSim/Tests/Common/Mock/MockAssetService.cs b/OpenSim/Tests/Common/Mock/MockAssetService.cs deleted file mode 100644 index 9d7c5e29e6..0000000000 --- a/OpenSim/Tests/Common/Mock/MockAssetService.cs +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; -using System.Reflection; -using log4net; -using OpenMetaverse; -using OpenSim.Framework; -using OpenSim.Data; -using OpenSim.Services.Interfaces; -using Nini.Config; - -namespace OpenSim.Tests.Common.Mock -{ - public class MockAssetService : IAssetService - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private readonly Dictionary Assets = new Dictionary(); - - public MockAssetService() {} - - /// - /// This constructor is required if the asset service is being created reflectively (which is the case in some - /// tests). - /// - /// - public MockAssetService(IConfigSource config) {} - - public AssetBase Get(string id) - { -// m_log.DebugFormat("[MOCK ASSET SERVICE]: Getting asset with id {0}", id); - - AssetBase asset; - if (Assets.ContainsKey(id)) - { - asset = Assets[id]; -// m_log.DebugFormat( -// "[MOCK ASSET SERVICE]: Got asset {0} {1}, bytes {2}", asset.Name, asset.ID, asset.Data.Length); - } - else - { - asset = null; - } - - return asset; - } - - public AssetBase GetCached(string id) - { - return Get(id); - } - - public AssetMetadata GetMetadata(string id) - { - throw new System.NotImplementedException(); - } - - public byte[] GetData(string id) - { -// m_log.DebugFormat("[MOCK ASSET SERVICE]: Requesting data for asset {0}", id); - - AssetBase asset = Get(id); - - if (asset == null) - return null; - else - return asset.Data; - } - - public bool Get(string id, object sender, AssetRetrieved handler) - { - handler(id, sender, Get(id)); - - return true; - } - - public string Store(AssetBase asset) - { -// m_log.DebugFormat( -// "[MOCK ASSET SERVICE]: Storing asset {0} {1}, bytes {2}", asset.Name, asset.ID, asset.Data.Length); - - Assets[asset.ID] = asset; - - return asset.ID; - } - - public bool UpdateContent(string id, byte[] data) - { - throw new System.NotImplementedException(); - } - - public bool Delete(string id) - { - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs index 97d4acefd0..484a97dc87 100644 --- a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs +++ b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs @@ -132,7 +132,7 @@ namespace OpenSim.Tests.Common.Setup testScene.AddModule(godsModule.Name, godsModule); realServices = realServices.ToLower(); - LocalAssetServicesConnector assetService = StartAssetService(testScene, realServices.Contains("asset")); + LocalAssetServicesConnector assetService = StartAssetService(testScene); // For now, always started a 'real' authentication service StartAuthenticationService(testScene, true); @@ -164,19 +164,15 @@ namespace OpenSim.Tests.Common.Setup return testScene; } - private static LocalAssetServicesConnector StartAssetService(Scene testScene, bool real) + private static LocalAssetServicesConnector StartAssetService(Scene testScene) { LocalAssetServicesConnector assetService = new LocalAssetServicesConnector(); IConfigSource config = new IniConfigSource(); + config.AddConfig("Modules"); - config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector"); - + config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector"); config.AddConfig("AssetService"); - if (real) - config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService"); - else - config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Tests.Common.dll:MockAssetService"); - + config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService"); config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll"); assetService.Initialise(config); From ba73df39a3824a91ffc848d9b6dc2356d1b307cf Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 15 Apr 2011 01:26:07 +0100 Subject: [PATCH 072/103] remove any reference to a mock authentication service since it doesn't exist and we don't use it --- .../Tests/Common/Setup/SceneSetupHelpers.cs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs index 484a97dc87..b819581254 100644 --- a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs +++ b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs @@ -132,11 +132,8 @@ namespace OpenSim.Tests.Common.Setup testScene.AddModule(godsModule.Name, godsModule); realServices = realServices.ToLower(); - LocalAssetServicesConnector assetService = StartAssetService(testScene); - - // For now, always started a 'real' authentication service - StartAuthenticationService(testScene, true); - + LocalAssetServicesConnector assetService = StartAssetService(testScene); + StartAuthenticationService(testScene); LocalInventoryServicesConnector inventoryService = StartInventoryService(testScene); StartGridService(testScene, true); LocalUserAccountServicesConnector userAccountService = StartUserAccountService(testScene); @@ -183,20 +180,18 @@ namespace OpenSim.Tests.Common.Setup return assetService; } - private static void StartAuthenticationService(Scene testScene, bool real) + private static void StartAuthenticationService(Scene testScene) { ISharedRegionModule service = new LocalAuthenticationServicesConnector(); IConfigSource config = new IniConfigSource(); + config.AddConfig("Modules"); config.AddConfig("AuthenticationService"); config.Configs["Modules"].Set("AuthenticationServices", "LocalAuthenticationServicesConnector"); - if (real) - config.Configs["AuthenticationService"].Set( - "LocalServiceModule", "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService"); - else - config.Configs["AuthenticationService"].Set( - "LocalServiceModule", "OpenSim.Tests.Common.dll:MockAuthenticationService"); + config.Configs["AuthenticationService"].Set( + "LocalServiceModule", "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService"); config.Configs["AuthenticationService"].Set("StorageProvider", "OpenSim.Data.Null.dll"); + service.Initialise(config); service.AddRegion(testScene); service.RegionLoaded(testScene); From 631654a38d2ace833f8cd2dc3b6b3860352f8b13 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 15 Apr 2011 01:30:49 +0100 Subject: [PATCH 073/103] remove old code for a mock grid service, which got removed some time back --- OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs index b819581254..5d08764a30 100644 --- a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs +++ b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs @@ -135,7 +135,7 @@ namespace OpenSim.Tests.Common.Setup LocalAssetServicesConnector assetService = StartAssetService(testScene); StartAuthenticationService(testScene); LocalInventoryServicesConnector inventoryService = StartInventoryService(testScene); - StartGridService(testScene, true); + StartGridService(testScene); LocalUserAccountServicesConnector userAccountService = StartUserAccountService(testScene); LocalPresenceServicesConnector presenceService = StartPresenceService(testScene); @@ -218,24 +218,19 @@ namespace OpenSim.Tests.Common.Setup return inventoryService; } - private static LocalGridServicesConnector StartGridService(Scene testScene, bool real) + private static LocalGridServicesConnector StartGridService(Scene testScene) { IConfigSource config = new IniConfigSource(); config.AddConfig("Modules"); config.AddConfig("GridService"); config.Configs["Modules"].Set("GridServices", "LocalGridServicesConnector"); config.Configs["GridService"].Set("StorageProvider", "OpenSim.Data.Null.dll:NullRegionData"); - if (real) - config.Configs["GridService"].Set("LocalServiceModule", "OpenSim.Services.GridService.dll:GridService"); + config.Configs["GridService"].Set("LocalServiceModule", "OpenSim.Services.GridService.dll:GridService"); LocalGridServicesConnector gridService = new LocalGridServicesConnector(); gridService.Initialise(config); - - //else - // config.Configs["GridService"].Set("LocalServiceModule", "OpenSim.Tests.Common.dll:TestGridService"); gridService.AddRegion(testScene); gridService.RegionLoaded(testScene); - //testScene.AddRegionModule(m_gridService.Name, m_gridService); return gridService; } From 66a62678e5da89b0fa004eca3a370d202c239884 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 15 Apr 2011 01:37:27 +0100 Subject: [PATCH 074/103] get rid of all traces of the now used mock service infrastructure --- .../Tests/InventoryArchiveTestCase.cs | 2 +- .../Archiver/Tests/InventoryArchiverTests.cs | 2 +- .../Inventory/Archiver/Tests/PathTests.cs | 14 ++++---- .../Tests/InventoryAccessModuleTests.cs | 2 +- .../World/Serialiser/Tests/SerialiserTests.cs | 2 +- .../Scenes/Tests/StandaloneTeleportTests.cs | 4 +-- .../Scenes/Tests/TaskInventoryTests.cs | 4 +-- .../Tests/Common/Setup/SceneSetupHelpers.cs | 32 ++----------------- 8 files changed, 17 insertions(+), 45 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs index e2316f0bdd..5ba08ee094 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs @@ -101,7 +101,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests // log4net.Config.XmlConfigurator.Configure(); InventoryArchiverModule archiverModule = new InventoryArchiverModule(); - Scene scene = SceneSetupHelpers.SetupScene("Inventory"); + Scene scene = SceneSetupHelpers.SetupScene(); SceneSetupHelpers.SetupSceneModules(scene, archiverModule); UserProfileTestUtils.CreateUserWithInventory(scene, m_uaLL1, "hampshire"); diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs index 69c5dcdab1..d03f6da295 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs @@ -62,7 +62,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests SerialiserModule serialiserModule = new SerialiserModule(); m_archiverModule = new InventoryArchiverModule(); - m_scene = SceneSetupHelpers.SetupScene("asset inventory"); + m_scene = SceneSetupHelpers.SetupScene(); SceneSetupHelpers.SetupSceneModules(m_scene, serialiserModule, m_archiverModule); } diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/PathTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/PathTests.cs index 0e8f647dbc..c7dae5276b 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/PathTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/PathTests.cs @@ -63,7 +63,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests InventoryArchiverModule archiverModule = new InventoryArchiverModule(); - Scene scene = SceneSetupHelpers.SetupScene("Inventory"); + Scene scene = SceneSetupHelpers.SetupScene(); SceneSetupHelpers.SetupSceneModules(scene, archiverModule); // Create user @@ -180,7 +180,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests InventoryArchiverModule archiverModule = new InventoryArchiverModule(); // Annoyingly, we have to set up a scene even though inventory loading has nothing to do with a scene - Scene scene = SceneSetupHelpers.SetupScene("inventory"); + Scene scene = SceneSetupHelpers.SetupScene(); SceneSetupHelpers.SetupSceneModules(scene, serialiserModule, archiverModule); @@ -223,7 +223,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests SerialiserModule serialiserModule = new SerialiserModule(); InventoryArchiverModule archiverModule = new InventoryArchiverModule(); - Scene scene = SceneSetupHelpers.SetupScene("inventory"); + Scene scene = SceneSetupHelpers.SetupScene(); SceneSetupHelpers.SetupSceneModules(scene, serialiserModule, archiverModule); UserProfileTestUtils.CreateUserWithInventory(scene, m_uaMT, "password"); @@ -248,7 +248,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests InventoryArchiverModule archiverModule = new InventoryArchiverModule(); - Scene scene = SceneSetupHelpers.SetupScene("Inventory"); + Scene scene = SceneSetupHelpers.SetupScene(); SceneSetupHelpers.SetupSceneModules(scene, archiverModule); // Create user @@ -327,7 +327,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests TestHelper.InMethod(); // log4net.Config.XmlConfigurator.Configure(); - Scene scene = SceneSetupHelpers.SetupScene("inventory"); + Scene scene = SceneSetupHelpers.SetupScene(); UserAccount ua1 = UserProfileTestUtils.CreateUserWithInventory(scene); Dictionary foldersCreated = new Dictionary(); @@ -394,7 +394,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests TestHelper.InMethod(); //log4net.Config.XmlConfigurator.Configure(); - Scene scene = SceneSetupHelpers.SetupScene("inventory"); + Scene scene = SceneSetupHelpers.SetupScene(); UserAccount ua1 = UserProfileTestUtils.CreateUserWithInventory(scene); string folder1ExistingName = "a"; @@ -445,7 +445,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests TestHelper.InMethod(); // log4net.Config.XmlConfigurator.Configure(); - Scene scene = SceneSetupHelpers.SetupScene("inventory"); + Scene scene = SceneSetupHelpers.SetupScene(); UserAccount ua1 = UserProfileTestUtils.CreateUserWithInventory(scene); string folder1ExistingName = "a"; diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs index 7a7a8c23f7..8d53cf1542 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs @@ -66,7 +66,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests config.AddConfig("Modules"); config.Configs["Modules"].Set("InventoryAccessModule", "BasicInventoryAccessModule"); - m_scene = SceneSetupHelpers.SetupScene("Inventory"); + m_scene = SceneSetupHelpers.SetupScene(); SceneSetupHelpers.SetupSceneModules(m_scene, config, m_iam); // Create user diff --git a/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs b/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs index dafaa0c107..a866fd938c 100644 --- a/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs +++ b/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs @@ -237,7 +237,7 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests public void Init() { m_serialiserModule = new SerialiserModule(); - m_scene = SceneSetupHelpers.SetupScene(""); + m_scene = SceneSetupHelpers.SetupScene(); SceneSetupHelpers.SetupSceneModules(m_scene, m_serialiserModule); } diff --git a/OpenSim/Region/Framework/Scenes/Tests/StandaloneTeleportTests.cs b/OpenSim/Region/Framework/Scenes/Tests/StandaloneTeleportTests.cs index 8588f7f599..dd28416788 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/StandaloneTeleportTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/StandaloneTeleportTests.cs @@ -117,11 +117,11 @@ namespace OpenSim.Region.Framework.Scenes.Tests ISharedRegionModule interregionComms = new LocalSimulationConnectorModule(); - Scene sceneB = SceneSetupHelpers.SetupScene("sceneB", sceneBId, 1010, 1010, "grid"); + Scene sceneB = SceneSetupHelpers.SetupScene("sceneB", sceneBId, 1010, 1010); SceneSetupHelpers.SetupSceneModules(sceneB, new IniConfigSource(), interregionComms); sceneB.RegisterRegionWithGrid(); - Scene sceneA = SceneSetupHelpers.SetupScene("sceneA", sceneAId, 1000, 1000, "grid"); + Scene sceneA = SceneSetupHelpers.SetupScene("sceneA", sceneAId, 1000, 1000); SceneSetupHelpers.SetupSceneModules(sceneA, new IniConfigSource(), interregionComms); sceneA.RegisterRegionWithGrid(); diff --git a/OpenSim/Region/Framework/Scenes/Tests/TaskInventoryTests.cs b/OpenSim/Region/Framework/Scenes/Tests/TaskInventoryTests.cs index 8138bcc19a..2aef4b0d19 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/TaskInventoryTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/TaskInventoryTests.cs @@ -101,7 +101,7 @@ namespace OpenSim.Region.Framework.Tests TestHelper.InMethod(); // log4net.Config.XmlConfigurator.Configure(); - Scene scene = SceneSetupHelpers.SetupScene("inventory"); + Scene scene = SceneSetupHelpers.SetupScene(); UserAccount user1 = CreateUser(scene); SceneObjectGroup sog1 = CreateSO1(scene, user1.PrincipalID); SceneObjectPart sop1 = sog1.RootPart; @@ -127,7 +127,7 @@ namespace OpenSim.Region.Framework.Tests TestHelper.InMethod(); // log4net.Config.XmlConfigurator.Configure(); - Scene scene = SceneSetupHelpers.SetupScene("inventory"); + Scene scene = SceneSetupHelpers.SetupScene(); UserAccount user1 = CreateUser(scene); SceneObjectGroup sog1 = CreateSO1(scene, user1.PrincipalID); SceneObjectPart sop1 = sog1.RootPart; diff --git a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs index 5d08764a30..99517d2b19 100644 --- a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs +++ b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs @@ -66,32 +66,7 @@ namespace OpenSim.Tests.Common.Setup /// public static TestScene SetupScene() { - return SetupScene(""); - } - - /// - /// Set up a test scene - /// - /// - /// Starts real inventory and asset services, as opposed to mock ones, if true - /// - public static TestScene SetupScene(String realServices) - { - return SetupScene("Unit test region", UUID.Random(), 1000, 1000, realServices); - } - - /// - /// Set up a test scene - /// - /// Name of the region - /// ID of the region - /// X co-ordinate of the region - /// Y co-ordinate of the region - /// This should be the same if simulating two scenes within a standalone - /// - public static TestScene SetupScene(string name, UUID id, uint x, uint y) - { - return SetupScene(name, id, x, y, ""); + return SetupScene("Unit test region", UUID.Random(), 1000, 1000); } /// @@ -103,10 +78,8 @@ namespace OpenSim.Tests.Common.Setup /// X co-ordinate of the region /// Y co-ordinate of the region /// This should be the same if simulating two scenes within a standalone - /// Starts real inventory and asset services, as opposed to mock ones, if true /// - public static TestScene SetupScene( - string name, UUID id, uint x, uint y, String realServices) + public static TestScene SetupScene(string name, UUID id, uint x, uint y) { Console.WriteLine("Setting up test scene {0}", name); @@ -130,7 +103,6 @@ namespace OpenSim.Tests.Common.Setup IRegionModule godsModule = new GodsModule(); godsModule.Initialise(testScene, new IniConfigSource()); testScene.AddModule(godsModule.Name, godsModule); - realServices = realServices.ToLower(); LocalAssetServicesConnector assetService = StartAssetService(testScene); StartAuthenticationService(testScene); From 40bf417da7705711f33c64b82611203ff2a0adad Mon Sep 17 00:00:00 2001 From: BlueWall Date: Wed, 13 Apr 2011 16:10:18 -0400 Subject: [PATCH 075/103] Add ColladaMesh switch --- .../CoreModules/Avatar/Assets/GetMeshModule.cs | 13 +++++++++++-- .../NewFileAgentInventoryVariablePriceModule.cs | 11 ++++++++++- bin/OpenSimDefaults.ini | 6 +++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Assets/GetMeshModule.cs b/OpenSim/Region/CoreModules/Avatar/Assets/GetMeshModule.cs index 878242a447..d2278bc3de 100644 --- a/OpenSim/Region/CoreModules/Avatar/Assets/GetMeshModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Assets/GetMeshModule.cs @@ -54,6 +54,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets private Scene m_scene; private IAssetService m_assetService; + private bool m_enabled = true; #region IRegionModuleBase Members @@ -65,7 +66,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets public void Initialise(IConfigSource source) { - + IConfig startupConfig = source.Configs["Startup"]; + if (startupConfig == null) + return; + + if (!startupConfig.GetBoolean("ColladaMesh",true)) + m_enabled = false; } public void AddRegion(Scene pScene) @@ -101,16 +107,19 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets public void RegisterCaps(UUID agentID, Caps caps) { + if(!m_enabled) + return; + UUID capID = UUID.Random(); // m_log.Info("[GETMESH]: /CAPS/" + capID); + caps.RegisterHandler("GetMesh", new RestHTTPHandler("GET", "/CAPS/" + capID, delegate(Hashtable m_dhttpMethod) { return ProcessGetMesh(m_dhttpMethod, agentID, caps); })); - } #endregion diff --git a/OpenSim/Region/CoreModules/Avatar/Assets/NewFileAgentInventoryVariablePriceModule.cs b/OpenSim/Region/CoreModules/Avatar/Assets/NewFileAgentInventoryVariablePriceModule.cs index 4a42c938d5..fb07cc9ca0 100644 --- a/OpenSim/Region/CoreModules/Avatar/Assets/NewFileAgentInventoryVariablePriceModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Assets/NewFileAgentInventoryVariablePriceModule.cs @@ -56,6 +56,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets private Scene m_scene; // private IAssetService m_assetService; private bool m_dumpAssetsToFile = false; + private bool m_enabled = true; #region IRegionModuleBase Members @@ -67,7 +68,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets public void Initialise(IConfigSource source) { - + IConfig startupConfig = source.Configs["Startup"]; + if (startupConfig == null) + return; + + if (!startupConfig.GetBoolean("ColladaMesh",true)) + m_enabled = false; } public void AddRegion(Scene pScene) @@ -103,6 +109,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets public void RegisterCaps(UUID agentID, Caps caps) { + if(!m_enabled) + return; + UUID capID = UUID.Random(); // m_log.Debug("[NEW FILE AGENT INVENTORY VARIABLE PRICE]: /CAPS/" + capID); diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index f857b8da63..107e859827 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -154,7 +154,11 @@ ; mesh, and use it for collisions. This is currently experimental code and enabling ; it may cause unexpected physics problems. ;UseMeshiesPhysicsMesh = false - + + ; enable / disable Collada mesh support + ; default is true + ; ColladaMesh = true + ; Choose one of the physics engines below ; OpenDynamicsEngine is by some distance the most developed physics engine ; basicphysics effectively does not model physics at all, making all objects phantom From 52d92303926c8be46af734707274e6d8ecd70602 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 15 Apr 2011 22:47:47 +0100 Subject: [PATCH 076/103] refactor: split out some rez code into a separate CreateItemForObject() method --- .../InventoryAccess/InventoryAccessModule.cs | 302 +++++++++--------- 1 file changed, 159 insertions(+), 143 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index c0946f4d52..0f2b9a68c2 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -274,154 +274,22 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess foreach (SceneObjectGroup objectGroup in objlist) objectGroup.AbsolutePosition = originalPositions[objectGroup.UUID]; - // Get the user info of the item destination - // - UUID userID = UUID.Zero; - - if (action == DeRezAction.Take || action == DeRezAction.TakeCopy || - action == DeRezAction.SaveToExistingUserInventoryItem) - { - // Take or take copy require a taker - // Saving changes requires a local user - // - if (remoteClient == null) - return UUID.Zero; - - userID = remoteClient.AgentId; - } - else - { - // All returns / deletes go to the object owner - // - userID = objlist[0].RootPart.OwnerID; - } - - if (userID == UUID.Zero) // Can't proceed - { + InventoryItemBase item = CreateItemForObject(action, remoteClient, objlist[0], folderID); + if (item == null) return UUID.Zero; - } - - // If we're returning someone's item, it goes back to the - // owner's Lost And Found folder. - // Delete is treated like return in this case - // Deleting your own items makes them go to trash - // - - InventoryFolderBase folder = null; - InventoryItemBase item = null; - - if (DeRezAction.SaveToExistingUserInventoryItem == action) + + // Can't know creator is the same, so null it in inventory + if (objlist.Count > 1) { - item = new InventoryItemBase(objlist[0].RootPart.FromUserInventoryItemID, userID); - item = m_Scene.InventoryService.GetItem(item); - - //item = userInfo.RootFolder.FindItem( - // objectGroup.RootPart.FromUserInventoryItemID); - - if (null == item) - { - m_log.DebugFormat( - "[AGENT INVENTORY]: Object {0} {1} scheduled for save to inventory has already been deleted.", - objlist[0].Name, objlist[0].UUID); - return UUID.Zero; - } + item.CreatorId = UUID.Zero.ToString(); + item.Flags = (uint)InventoryItemFlags.ObjectHasMultipleItems; } else { - // Folder magic - // - if (action == DeRezAction.Delete) - { - // Deleting someone else's item - // - if (remoteClient == null || - objlist[0].OwnerID != remoteClient.AgentId) - { - - folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.LostAndFoundFolder); - } - else - { - folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.TrashFolder); - } - } - else if (action == DeRezAction.Return) - { - - // Dump to lost + found unconditionally - // - folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.LostAndFoundFolder); - } - - if (folderID == UUID.Zero && folder == null) - { - if (action == DeRezAction.Delete) - { - // Deletes go to trash by default - // - folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.TrashFolder); - } - else - { - if (remoteClient == null || - objlist[0].OwnerID != remoteClient.AgentId) - { - // Taking copy of another person's item. Take to - // Objects folder. - folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object); - } - else - { - // Catch all. Use lost & found - // - - folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.LostAndFoundFolder); - } - } - } - - // Override and put into where it came from, if it came - // from anywhere in inventory - // - if (action == DeRezAction.Take || action == DeRezAction.TakeCopy) - { - if (objlist[0].RootPart.FromFolderID != UUID.Zero) - { - InventoryFolderBase f = new InventoryFolderBase(objlist[0].RootPart.FromFolderID, userID); - folder = m_Scene.InventoryService.GetFolder(f); - } - } - - if (folder == null) // None of the above - { - folder = new InventoryFolderBase(folderID); - - if (folder == null) // Nowhere to put it - { - return UUID.Zero; - } - } - - item = new InventoryItemBase(); - // Can't know creator is the same, so null it in inventory - if (objlist.Count > 1) - item.CreatorId = UUID.Zero.ToString(); - else - item.CreatorId = objlist[0].RootPart.CreatorID.ToString(); - item.ID = UUID.Random(); - item.InvType = (int)InventoryType.Object; - item.Folder = folder.ID; - item.Owner = userID; - if (objlist.Count > 1) - { - item.Flags = (uint)InventoryItemFlags.ObjectHasMultipleItems; - } - else - { - item.SaleType = objlist[0].RootPart.ObjectSaleType; - item.SalePrice = objlist[0].RootPart.SalePrice; - } - } + item.CreatorId = objlist[0].RootPart.CreatorID.ToString(); + item.SaleType = objlist[0].RootPart.ObjectSaleType; + item.SalePrice = objlist[0].RootPart.SalePrice; + } AssetBase asset = CreateAsset( objlist[0].GetPartName(objlist[0].RootPart.LocalId), @@ -505,8 +373,156 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess } } } + return assetID; } + + /// + /// Create an item using details for the given scene object. + /// + /// + /// + /// + /// + /// + protected InventoryItemBase CreateItemForObject( + DeRezAction action, IClientAPI remoteClient, SceneObjectGroup so, UUID folderID) + { + // Get the user info of the item destination + // + UUID userID = UUID.Zero; + + if (action == DeRezAction.Take || action == DeRezAction.TakeCopy || + action == DeRezAction.SaveToExistingUserInventoryItem) + { + // Take or take copy require a taker + // Saving changes requires a local user + // + if (remoteClient == null) + return null; + + userID = remoteClient.AgentId; + } + else + { + // All returns / deletes go to the object owner + // + userID = so.RootPart.OwnerID; + } + + if (userID == UUID.Zero) // Can't proceed + { + return null; + } + + // If we're returning someone's item, it goes back to the + // owner's Lost And Found folder. + // Delete is treated like return in this case + // Deleting your own items makes them go to trash + // + + InventoryFolderBase folder = null; + InventoryItemBase item = null; + + if (DeRezAction.SaveToExistingUserInventoryItem == action) + { + item = new InventoryItemBase(so.RootPart.FromUserInventoryItemID, userID); + item = m_Scene.InventoryService.GetItem(item); + + //item = userInfo.RootFolder.FindItem( + // objectGroup.RootPart.FromUserInventoryItemID); + + if (null == item) + { + m_log.DebugFormat( + "[AGENT INVENTORY]: Object {0} {1} scheduled for save to inventory has already been deleted.", + so.Name, so.UUID); + + return null; + } + } + else + { + // Folder magic + // + if (action == DeRezAction.Delete) + { + // Deleting someone else's item + // + if (remoteClient == null || + so.OwnerID != remoteClient.AgentId) + { + folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.LostAndFoundFolder); + } + else + { + folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.TrashFolder); + } + } + else if (action == DeRezAction.Return) + { + // Dump to lost + found unconditionally + // + folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.LostAndFoundFolder); + } + + if (folderID == UUID.Zero && folder == null) + { + if (action == DeRezAction.Delete) + { + // Deletes go to trash by default + // + folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.TrashFolder); + } + else + { + if (remoteClient == null || so.OwnerID != remoteClient.AgentId) + { + // Taking copy of another person's item. Take to + // Objects folder. + folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object); + } + else + { + // Catch all. Use lost & found + // + + folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.LostAndFoundFolder); + } + } + } + + // Override and put into where it came from, if it came + // from anywhere in inventory + // + if (action == DeRezAction.Take || action == DeRezAction.TakeCopy) + { + if (so.RootPart.FromFolderID != UUID.Zero) + { + InventoryFolderBase f = new InventoryFolderBase(so.RootPart.FromFolderID, userID); + folder = m_Scene.InventoryService.GetFolder(f); + } + } + + if (folder == null) // None of the above + { + folder = new InventoryFolderBase(folderID); + + if (folder == null) // Nowhere to put it + { + return null; + } + } + + item = new InventoryItemBase(); + item.ID = UUID.Random(); + item.InvType = (int)InventoryType.Object; + item.Folder = folder.ID; + item.Owner = userID; + } + + return item; + } /// /// Rez an object into the scene from the user's inventory From 9c7f14bf366eaa113ca7a89d3b40a42529e7726e Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 15 Apr 2011 23:07:23 +0100 Subject: [PATCH 077/103] minor: remove a small amount of code duplication --- .../Framework/InventoryAccess/InventoryAccessModule.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 0f2b9a68c2..5d1902502f 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -298,17 +298,16 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess Utils.StringToBytes(itemXml), objlist[0].OwnerID.ToString()); m_Scene.AssetService.Store(asset); - assetID = asset.FullID; + + item.AssetID = asset.FullID; + assetID = asset.FullID; if (DeRezAction.SaveToExistingUserInventoryItem == action) { - item.AssetID = asset.FullID; m_Scene.InventoryService.UpdateItem(item); } else { - item.AssetID = asset.FullID; - uint effectivePerms = (uint)(PermissionMask.Copy | PermissionMask.Transfer | PermissionMask.Modify | PermissionMask.Move) | 7; foreach (SceneObjectGroup grp in objlist) effectivePerms &= grp.GetEffectivePermissions(); From fc365f2a31d46b9c6517159330c7f6953eb60e9f Mon Sep 17 00:00:00 2001 From: BlueWall Date: Fri, 15 Apr 2011 18:23:37 -0400 Subject: [PATCH 078/103] Thanks Snoopy for a patch that allows setting of perms on items given to new avatars --- .../RemoteController/RemoteAdminPlugin.cs | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index 1b4d1ea9f9..c6b34f9f20 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -1587,6 +1587,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController { InventoryItemBase destinationItem = new InventoryItemBase(UUID.Random(), destination); destinationItem.Name = item.Name; + destinationItem.Owner = destination; destinationItem.Description = item.Description; destinationItem.InvType = item.InvType; destinationItem.CreatorId = item.CreatorId; @@ -1606,6 +1607,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController destinationItem.Flags = item.Flags; destinationItem.CreationDate = item.CreationDate; destinationItem.Folder = destinationFolder.ID; + ApplyNextOwnerPermissions(destinationItem); m_application.SceneManager.CurrentOrFirstScene.AddInventoryItem(destinationItem); m_log.DebugFormat("[RADMIN]: Added item {0} to folder {1}", destinationItem.ID, destinationFolder.ID); @@ -1640,6 +1642,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController { InventoryItemBase destinationItem = new InventoryItemBase(UUID.Random(), destination); destinationItem.Name = item.Name; + destinationItem.Owner = destination; destinationItem.Description = item.Description; destinationItem.InvType = item.InvType; destinationItem.CreatorId = item.CreatorId; @@ -1659,6 +1662,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController destinationItem.Flags = item.Flags; destinationItem.CreationDate = item.CreationDate; destinationItem.Folder = destinationFolder.ID; + ApplyNextOwnerPermissions(destinationItem); m_application.SceneManager.CurrentOrFirstScene.AddInventoryItem(destinationItem); m_log.DebugFormat("[RADMIN]: Added item {0} to folder {1}", destinationItem.ID, destinationFolder.ID); @@ -1716,7 +1720,11 @@ namespace OpenSim.ApplicationPlugins.RemoteController { destinationFolder = new InventoryFolderBase(); destinationFolder.ID = UUID.Random(); - destinationFolder.Name = assetType.ToString(); + if (assetType == AssetType.Clothing) { + destinationFolder.Name = "Clothing"; + } else { + destinationFolder.Name = "Body Parts"; + } destinationFolder.Owner = destination; destinationFolder.Type = (short)assetType; destinationFolder.ParentID = inventoryService.GetRootFolder(destination).ID; @@ -1748,6 +1756,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController { InventoryItemBase destinationItem = new InventoryItemBase(UUID.Random(), destination); destinationItem.Name = item.Name; + destinationItem.Owner = destination; destinationItem.Description = item.Description; destinationItem.InvType = item.InvType; destinationItem.CreatorId = item.CreatorId; @@ -1767,6 +1776,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController destinationItem.Flags = item.Flags; destinationItem.CreationDate = item.CreationDate; destinationItem.Folder = extraFolder.ID; + ApplyNextOwnerPermissions(destinationItem); m_application.SceneManager.CurrentOrFirstScene.AddInventoryItem(destinationItem); inventoryMap.Add(item.ID, destinationItem.ID); @@ -1783,6 +1793,29 @@ namespace OpenSim.ApplicationPlugins.RemoteController } } + /// + /// Apply next owner permissions. + /// + + private void ApplyNextOwnerPermissions(InventoryItemBase item) + { + if (item.InvType == (int)InventoryType.Object && (item.CurrentPermissions & 7) != 0) + { + if ((item.CurrentPermissions & ((uint)PermissionMask.Copy >> 13)) == 0) + item.CurrentPermissions &= ~(uint)PermissionMask.Copy; + if ((item.CurrentPermissions & ((uint)PermissionMask.Transfer >> 13)) == 0) + item.CurrentPermissions &= ~(uint)PermissionMask.Transfer; + if ((item.CurrentPermissions & ((uint)PermissionMask.Modify >> 13)) == 0) + item.CurrentPermissions &= ~(uint)PermissionMask.Modify; + } + item.CurrentPermissions &= item.NextPermissions; + item.BasePermissions &= item.NextPermissions; + item.EveryOnePermissions &= item.NextPermissions; + // item.OwnerChanged = true; + // item.PermsMask = 0; + // item.PermsGranter = UUID.Zero; + } + /// /// This method is called if a given model avatar name can not be found. If the external /// file has already been loaded once, then control returns immediately. If not, then it From 1dba671f45b691bcf024e9ffd5568e8a7ea2165c Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 15 Apr 2011 23:32:46 +0100 Subject: [PATCH 079/103] refactor: separate out code that adds permissions --- .../InventoryAccess/InventoryAccessModule.cs | 101 ++++++++++-------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 5d1902502f..8b68dbe64a 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -308,48 +308,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess } else { - uint effectivePerms = (uint)(PermissionMask.Copy | PermissionMask.Transfer | PermissionMask.Modify | PermissionMask.Move) | 7; - foreach (SceneObjectGroup grp in objlist) - effectivePerms &= grp.GetEffectivePermissions(); - effectivePerms |= (uint)PermissionMask.Move; - - if (remoteClient != null && (remoteClient.AgentId != objlist[0].RootPart.OwnerID) && m_Scene.Permissions.PropagatePermissions()) - { - uint perms = effectivePerms; - uint nextPerms = (perms & 7) << 13; - if ((nextPerms & (uint)PermissionMask.Copy) == 0) - perms &= ~(uint)PermissionMask.Copy; - if ((nextPerms & (uint)PermissionMask.Transfer) == 0) - perms &= ~(uint)PermissionMask.Transfer; - if ((nextPerms & (uint)PermissionMask.Modify) == 0) - perms &= ~(uint)PermissionMask.Modify; - - item.BasePermissions = perms & objlist[0].RootPart.NextOwnerMask; - item.CurrentPermissions = item.BasePermissions; - item.NextPermissions = perms & objlist[0].RootPart.NextOwnerMask; - item.EveryOnePermissions = objlist[0].RootPart.EveryoneMask & objlist[0].RootPart.NextOwnerMask; - item.GroupPermissions = objlist[0].RootPart.GroupMask & objlist[0].RootPart.NextOwnerMask; - - // Magic number badness. Maybe this deserves an enum. - // bit 4 (16) is the "Slam" bit, it means treat as passed - // and apply next owner perms on rez - item.CurrentPermissions |= 16; // Slam! - } - else - { - item.BasePermissions = effectivePerms; - item.CurrentPermissions = effectivePerms; - item.NextPermissions = objlist[0].RootPart.NextOwnerMask & effectivePerms; - item.EveryOnePermissions = objlist[0].RootPart.EveryoneMask & effectivePerms; - item.GroupPermissions = objlist[0].RootPart.GroupMask & effectivePerms; - - item.CurrentPermissions &= - ((uint)PermissionMask.Copy | - (uint)PermissionMask.Transfer | - (uint)PermissionMask.Modify | - (uint)PermissionMask.Move | - 7); // Preserve folded permissions - } + AddPermissions(item, objlist[0], objlist, remoteClient); item.CreationDate = Util.UnixTimeSinceEpoch(); item.Description = asset.Description; @@ -376,6 +335,64 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess return assetID; } + /// + /// Add relevant permissions for an object to the item. + /// + /// + /// + /// + /// + /// + protected InventoryItemBase AddPermissions( + InventoryItemBase item, SceneObjectGroup so, List objsForEffectivePermissions, + IClientAPI remoteClient) + { + uint effectivePerms = (uint)(PermissionMask.Copy | PermissionMask.Transfer | PermissionMask.Modify | PermissionMask.Move) | 7; + foreach (SceneObjectGroup grp in objsForEffectivePermissions) + effectivePerms &= grp.GetEffectivePermissions(); + effectivePerms |= (uint)PermissionMask.Move; + + if (remoteClient != null && (remoteClient.AgentId != so.RootPart.OwnerID) && m_Scene.Permissions.PropagatePermissions()) + { + uint perms = effectivePerms; + uint nextPerms = (perms & 7) << 13; + if ((nextPerms & (uint)PermissionMask.Copy) == 0) + perms &= ~(uint)PermissionMask.Copy; + if ((nextPerms & (uint)PermissionMask.Transfer) == 0) + perms &= ~(uint)PermissionMask.Transfer; + if ((nextPerms & (uint)PermissionMask.Modify) == 0) + perms &= ~(uint)PermissionMask.Modify; + + item.BasePermissions = perms & so.RootPart.NextOwnerMask; + item.CurrentPermissions = item.BasePermissions; + item.NextPermissions = perms & so.RootPart.NextOwnerMask; + item.EveryOnePermissions = so.RootPart.EveryoneMask & so.RootPart.NextOwnerMask; + item.GroupPermissions = so.RootPart.GroupMask & so.RootPart.NextOwnerMask; + + // Magic number badness. Maybe this deserves an enum. + // bit 4 (16) is the "Slam" bit, it means treat as passed + // and apply next owner perms on rez + item.CurrentPermissions |= 16; // Slam! + } + else + { + item.BasePermissions = effectivePerms; + item.CurrentPermissions = effectivePerms; + item.NextPermissions = so.RootPart.NextOwnerMask & effectivePerms; + item.EveryOnePermissions = so.RootPart.EveryoneMask & effectivePerms; + item.GroupPermissions = so.RootPart.GroupMask & effectivePerms; + + item.CurrentPermissions &= + ((uint)PermissionMask.Copy | + (uint)PermissionMask.Transfer | + (uint)PermissionMask.Modify | + (uint)PermissionMask.Move | + 7); // Preserve folded permissions + } + + return item; + } + /// /// Create an item using details for the given scene object. /// From ed74be65995afe192ce03177325c98b0100ab157 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 15 Apr 2011 23:54:30 +0100 Subject: [PATCH 080/103] Make remote admin error messages more helpful on windows. Also, make log messages conform more with convention. --- .../RemoteController/RemoteAdminPlugin.cs | 161 ++++++++---------- 1 file changed, 73 insertions(+), 88 deletions(-) diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index c6b34f9f20..dc4309fbe2 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -94,7 +94,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController public void Initialise() { - m_log.Info("[RADMIN]: " + Name + " cannot be default-initialized!"); + m_log.Error("[RADMIN]: " + Name + " cannot be default-initialized!"); throw new PluginNotInitialisedException(Name); } @@ -111,7 +111,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController else { m_config = m_configSource.Configs["RemoteAdmin"]; - m_log.Info("[RADMIN]: Remote Admin Plugin Enabled"); + m_log.Debug("[RADMIN]: Remote Admin Plugin Enabled"); m_requiredPassword = m_config.GetString("access_password", String.Empty); int port = m_config.GetInt("port", 0); @@ -230,8 +230,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.ErrorFormat("[RADMIN]: Restart region: failed: {0}", e.Message); - m_log.DebugFormat("[RADMIN]: Restart region: failed: {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN]: Restart region: failed: {0} {1}", e.Message, e.StackTrace); responseData["accepted"] = false; responseData["success"] = false; responseData["rebooting"] = false; @@ -277,8 +276,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.ErrorFormat("[RADMIN]: Broadcasting: failed: {0}", e.Message); - m_log.DebugFormat("[RADMIN]: Broadcasting: failed: {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN]: Broadcasting: failed: {0}", e.Message, e.StackTrace); responseData["accepted"] = false; responseData["success"] = false; @@ -301,7 +299,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController { Hashtable requestData = (Hashtable) request.Params[0]; - m_log.DebugFormat("[RADMIN]: Load Terrain: XmlRpc {0}", request.ToString()); + m_log.DebugFormat("[RADMIN]: Load Terrain: XmlRpc {0}", request); // foreach (string k in requestData.Keys) // { // m_log.DebugFormat("[RADMIN]: Load Terrain: XmlRpc {0}: >{1}< {2}", @@ -348,8 +346,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.ErrorFormat("[RADMIN]: Terrain Loading: failed: {0}", e.Message); - m_log.DebugFormat("[RADMIN]: Terrain Loading: failed: {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN]: Terrain Loading: failed: {0} {1}", e.Message, e.StackTrace); responseData["success"] = false; responseData["error"] = e.Message; @@ -417,14 +414,14 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.ErrorFormat("[RADMIN] Shutdown: failed: {0}", e.Message); - m_log.DebugFormat("[RADMIN] Shutdown: failed: {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN]: Shutdown: failed: {0} {1}", e.Message, e.StackTrace); responseData["accepted"] = false; responseData["error"] = e.Message; response.Value = responseData; } + m_log.Info("[RADMIN]: Shutdown Administrator Request complete"); return response; } @@ -725,8 +722,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.ErrorFormat("[RADMIN] CreateRegion: failed {0}", e.Message); - m_log.DebugFormat("[RADMIN] CreateRegion: failed {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN] CreateRegion: failed {0} {1}", e.Message, e.StackTrace); responseData["success"] = false; responseData["error"] = e.Message; @@ -792,8 +788,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.ErrorFormat("[RADMIN] DeleteRegion: failed {0}", e.Message); - m_log.DebugFormat("[RADMIN] DeleteRegion: failed {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN] DeleteRegion: failed {0} {1}", e.Message, e.StackTrace); responseData["success"] = false; responseData["error"] = e.Message; @@ -884,8 +879,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.ErrorFormat("[RADMIN] CloseRegion: failed {0}", e.Message); - m_log.DebugFormat("[RADMIN] CloseRegion: failed {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN]: CloseRegion: failed {0} {1}", e.Message, e.StackTrace); responseData["success"] = false; responseData["error"] = e.Message; @@ -982,8 +976,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.ErrorFormat("[RADMIN] ModifyRegion: failed {0}", e.Message); - m_log.DebugFormat("[RADMIN] ModifyRegion: failed {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN] ModifyRegion: failed {0} {1}", e.Message, e.StackTrace); responseData["success"] = false; responseData["error"] = e.Message; @@ -1106,8 +1099,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.ErrorFormat("[RADMIN] CreateUser: failed: {0}", e.Message); - m_log.DebugFormat("[RADMIN] CreateUser: failed: {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN]: CreateUser: failed: {0} {1}", e.Message, e.StackTrace); responseData["success"] = false; responseData["avatar_uuid"] = UUID.Zero.ToString(); @@ -1198,8 +1190,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.ErrorFormat("[RADMIN] UserExists: failed: {0}", e.Message); - m_log.DebugFormat("[RADMIN] UserExists: failed: {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN]: UserExists: failed: {0} {1}", e.Message, e.StackTrace); responseData["success"] = false; responseData["error"] = e.Message; @@ -1372,9 +1363,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - - m_log.ErrorFormat("[RADMIN] UpdateUserAccount: failed: {0}", e.Message); - m_log.DebugFormat("[RADMIN] UpdateUserAccount: failed: {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN] UpdateUserAccount: failed: {0} {1}", e.Message, e.StackTrace); responseData["success"] = false; responseData["avatar_uuid"] = UUID.Zero.ToString(); @@ -1382,6 +1371,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController response.Value = responseData; } + m_log.Info("[RADMIN]: UpdateUserAccount: request complete"); return response; } @@ -1397,7 +1387,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController private void UpdateUserAppearance(Hashtable responseData, Hashtable requestData, UUID userid) { - m_log.DebugFormat("[RADMIN] updateUserAppearance"); + m_log.DebugFormat("[RADMIN]: updateUserAppearance"); string defaultMale = m_config.GetString("default_male", "Default Male"); string defaultFemale = m_config.GetString("default_female", "Default Female"); @@ -1437,16 +1427,16 @@ namespace OpenSim.ApplicationPlugins.RemoteController if (String.IsNullOrEmpty(model)) { - m_log.DebugFormat("[RADMIN] Appearance update not requested"); + m_log.DebugFormat("[RADMIN]: Appearance update not requested"); return; } - m_log.DebugFormat("[RADMIN] Setting appearance for avatar {0}, using model <{1}>", userid, model); + m_log.DebugFormat("[RADMIN]: Setting appearance for avatar {0}, using model <{1}>", userid, model); string[] modelSpecifiers = model.Split(); if (modelSpecifiers.Length != 2) { - m_log.WarnFormat("[RADMIN] User appearance not set for {0}. Invalid model name : <{1}>", userid, model); + m_log.WarnFormat("[RADMIN]: User appearance not set for {0}. Invalid model name : <{1}>", userid, model); // modelSpecifiers = dmodel.Split(); return; } @@ -1457,7 +1447,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController if (modelProfile == null) { - m_log.WarnFormat("[RADMIN] Requested model ({0}) not found. Appearance unchanged", model); + m_log.WarnFormat("[RADMIN]: Requested model ({0}) not found. Appearance unchanged", model); return; } @@ -1467,7 +1457,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController EstablishAppearance(userid, modelProfile.PrincipalID); - m_log.DebugFormat("[RADMIN] Finished setting appearance for avatar {0}, using model {1}", + m_log.DebugFormat("[RADMIN]: Finished setting appearance for avatar {0}, using model {1}", userid, model); } @@ -1479,7 +1469,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController private void EstablishAppearance(UUID destination, UUID source) { - m_log.DebugFormat("[RADMIN] Initializing inventory for {0} from {1}", destination, source); + m_log.DebugFormat("[RADMIN]: Initializing inventory for {0} from {1}", destination, source); Scene scene = m_application.SceneManager.CurrentOrFirstScene; // If the model has no associated appearance we're done. @@ -1501,7 +1491,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.WarnFormat("[RADMIN] Error transferring appearance for {0} : {1}", + m_log.WarnFormat("[RADMIN]: Error transferring appearance for {0} : {1}", destination, e.Message); } @@ -1532,7 +1522,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.WarnFormat("[RADMIN] Error transferring appearance for {0} : {1}", + m_log.WarnFormat("[RADMIN]: Error transferring appearance for {0} : {1}", destination, e.Message); } @@ -1567,7 +1557,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController destinationFolder.ParentID = inventoryService.GetRootFolder(destination).ID; destinationFolder.Version = 1; inventoryService.AddFolder(destinationFolder); // store base record - m_log.ErrorFormat("[RADMIN] Created folder for destination {0}", source); + m_log.ErrorFormat("[RADMIN]: Created folder for destination {0}", source); } // Wearables @@ -1730,7 +1720,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController destinationFolder.ParentID = inventoryService.GetRootFolder(destination).ID; destinationFolder.Version = 1; inventoryService.AddFolder(destinationFolder); // store base record - m_log.ErrorFormat("[RADMIN] Created folder for destination {0}", source); + m_log.ErrorFormat("[RADMIN]: Created folder for destination {0}", source); } InventoryFolderBase extraFolder; @@ -1748,7 +1738,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController extraFolder.ParentID = destinationFolder.ID; inventoryService.AddFolder(extraFolder); - m_log.DebugFormat("[RADMIN] Added folder {0} to folder {1}", extraFolder.ID, sourceFolder.ID); + m_log.DebugFormat("[RADMIN]: Added folder {0} to folder {1}", extraFolder.ID, sourceFolder.ID); List items = inventoryService.GetFolderContent(source, folder.ID).Items; @@ -1825,7 +1815,6 @@ namespace OpenSim.ApplicationPlugins.RemoteController /// indicate which outfit is the default, and this outfit will be automatically worn. The /// other outfits are provided to allow "real" avatars a way to easily change their outfits. /// - private bool CreateDefaultAvatars() { // Only load once @@ -1834,7 +1823,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController return false; } - m_log.DebugFormat("[RADMIN] Creating default avatar entries"); + m_log.DebugFormat("[RADMIN]: Creating default avatar entries"); m_defaultAvatarsLoaded = true; @@ -1890,7 +1879,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController foreach (XmlElement avatar in avatars) { - m_log.DebugFormat("[RADMIN] Loading appearance for {0}, gender = {1}", + m_log.DebugFormat("[RADMIN]: Loading appearance for {0}, gender = {1}", GetStringAttribute(avatar,"name","?"), GetStringAttribute(avatar,"gender","?")); // Create the user identified by the avatar entry @@ -1912,7 +1901,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController account = CreateUser(scopeID, names[0], names[1], password, email); if (null == account) { - m_log.ErrorFormat("[RADMIN] Avatar {0} {1} was not created", names[0], names[1]); + m_log.ErrorFormat("[RADMIN]: Avatar {0} {1} was not created", names[0], names[1]); return false; } } @@ -1930,12 +1919,12 @@ namespace OpenSim.ApplicationPlugins.RemoteController ID = account.PrincipalID; - m_log.DebugFormat("[RADMIN] User {0}[{1}] created or retrieved", name, ID); + m_log.DebugFormat("[RADMIN]: User {0}[{1}] created or retrieved", name, ID); include = true; } catch (Exception e) { - m_log.DebugFormat("[RADMIN] Error creating user {0} : {1}", name, e.Message); + m_log.DebugFormat("[RADMIN]: Error creating user {0} : {1}", name, e.Message); include = false; } @@ -1975,7 +1964,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController clothingFolder.ParentID = inventoryService.GetRootFolder(ID).ID; clothingFolder.Version = 1; inventoryService.AddFolder(clothingFolder); // store base record - m_log.ErrorFormat("[RADMIN] Created clothing folder for {0}/{1}", name, ID); + m_log.ErrorFormat("[RADMIN]: Created clothing folder for {0}/{1}", name, ID); } // OK, now we have an inventory for the user, read in the outfits from the @@ -1988,7 +1977,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController foreach (XmlElement outfit in outfits) { - m_log.DebugFormat("[RADMIN] Loading outfit {0} for {1}", + m_log.DebugFormat("[RADMIN]: Loading outfit {0} for {1}", GetStringAttribute(outfit,"name","?"), GetStringAttribute(avatar,"name","?")); outfitName = GetStringAttribute(outfit,"name",""); @@ -2012,7 +2001,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController // Otherwise, we must create the folder. if (extraFolder == null) { - m_log.DebugFormat("[RADMIN] Creating outfit folder {0} for {1}", outfitName, name); + m_log.DebugFormat("[RADMIN]: Creating outfit folder {0} for {1}", outfitName, name); extraFolder = new InventoryFolderBase(); extraFolder.ID = UUID.Random(); extraFolder.Name = outfitName; @@ -2021,7 +2010,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController extraFolder.Version = 1; extraFolder.ParentID = clothingFolder.ID; inventoryService.AddFolder(extraFolder); - m_log.DebugFormat("[RADMIN] Adding outfile folder {0} to folder {1}", extraFolder.ID, clothingFolder.ID); + m_log.DebugFormat("[RADMIN]: Adding outfile folder {0} to folder {1}", extraFolder.ID, clothingFolder.ID); } // Now get the pieces that make up the outfit @@ -2036,7 +2025,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController switch (child.Name) { case "Permissions" : - m_log.DebugFormat("[RADMIN] Permissions specified"); + m_log.DebugFormat("[RADMIN]: Permissions specified"); perms = child; break; case "Asset" : @@ -2086,7 +2075,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController inventoryItem.Folder = extraFolder.ID; // Parent folder m_application.SceneManager.CurrentOrFirstScene.AddInventoryItem(inventoryItem); - m_log.DebugFormat("[RADMIN] Added item {0} to folder {1}", inventoryItem.ID, extraFolder.ID); + m_log.DebugFormat("[RADMIN]: Added item {0} to folder {1}", inventoryItem.ID, extraFolder.ID); } // Attach item, if attachpoint is specified @@ -2094,7 +2083,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController if (attachpoint != 0) { avatarAppearance.SetAttachment(attachpoint, inventoryItem.ID, inventoryItem.AssetID); - m_log.DebugFormat("[RADMIN] Attached {0}", inventoryItem.ID); + m_log.DebugFormat("[RADMIN]: Attached {0}", inventoryItem.ID); } // Record whether or not the item is to be initially worn @@ -2107,32 +2096,32 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.WarnFormat("[RADMIN] Error wearing item {0} : {1}", inventoryItem.ID, e.Message); + m_log.WarnFormat("[RADMIN]: Error wearing item {0} : {1}", inventoryItem.ID, e.Message); } } // foreach item in outfit - m_log.DebugFormat("[RADMIN] Outfit {0} load completed", outfitName); + m_log.DebugFormat("[RADMIN]: Outfit {0} load completed", outfitName); } // foreach outfit - m_log.DebugFormat("[RADMIN] Inventory update complete for {0}", name); + m_log.DebugFormat("[RADMIN]: Inventory update complete for {0}", name); scene.AvatarService.SetAppearance(ID, avatarAppearance); } catch (Exception e) { - m_log.WarnFormat("[RADMIN] Inventory processing incomplete for user {0} : {1}", + m_log.WarnFormat("[RADMIN]: Inventory processing incomplete for user {0} : {1}", name, e.Message); } } // End of include } - m_log.DebugFormat("[RADMIN] Default avatar loading complete"); + m_log.DebugFormat("[RADMIN]: Default avatar loading complete"); } else { - m_log.DebugFormat("[RADMIN] No default avatar information available"); + m_log.DebugFormat("[RADMIN]: No default avatar information available"); return false; } } catch (Exception e) { - m_log.WarnFormat("[RADMIN] Exception whilst loading default avatars ; {0}", e.Message); + m_log.WarnFormat("[RADMIN]: Exception whilst loading default avatars ; {0}", e.Message); return false; } @@ -2227,8 +2216,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.InfoFormat("[RADMIN] LoadOAR: {0}", e.Message); - m_log.DebugFormat("[RADMIN] LoadOAR: {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN]: LoadOAR: {0} {1}", e.Message, e.StackTrace); responseData["loaded"] = false; responseData["error"] = e.Message; @@ -2333,8 +2321,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.InfoFormat("[RADMIN] SaveOAR: {0}", e.Message); - m_log.DebugFormat("[RADMIN] SaveOAR: {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN]: SaveOAR: {0} {1}", e.Message, e.StackTrace); responseData["saved"] = false; responseData["error"] = e.Message; @@ -2348,7 +2335,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController private void RemoteAdminOarSaveCompleted(Guid uuid, string name) { - m_log.DebugFormat("[RADMIN] File processing complete for {0}", name); + m_log.DebugFormat("[RADMIN]: File processing complete for {0}", name); lock (m_saveOarLock) Monitor.Pulse(m_saveOarLock); } @@ -2386,14 +2373,14 @@ namespace OpenSim.ApplicationPlugins.RemoteController UUID region_uuid = (UUID) (string) requestData["region_uuid"]; if (!m_application.SceneManager.TrySetCurrentScene(region_uuid)) throw new Exception(String.Format("failed to switch to region {0}", region_uuid.ToString())); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_uuid.ToString()); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_uuid.ToString()); } else if (requestData.Contains("region_name")) { string region_name = (string) requestData["region_name"]; if (!m_application.SceneManager.TrySetCurrentScene(region_name)) throw new Exception(String.Format("failed to switch to region {0}", region_name)); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_name); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_name); } else throw new Exception("neither region_name nor region_uuid given"); @@ -2424,8 +2411,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.InfoFormat("[RADMIN] LoadXml: {0}", e.Message); - m_log.DebugFormat("[RADMIN] LoadXml: {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN] LoadXml: {0} {1}", e.Message, e.StackTrace); responseData["loaded"] = false; responseData["switched"] = false; @@ -2471,14 +2457,14 @@ namespace OpenSim.ApplicationPlugins.RemoteController UUID region_uuid = (UUID) (string) requestData["region_uuid"]; if (!m_application.SceneManager.TrySetCurrentScene(region_uuid)) throw new Exception(String.Format("failed to switch to region {0}", region_uuid.ToString())); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_uuid.ToString()); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_uuid.ToString()); } else if (requestData.Contains("region_name")) { string region_name = (string) requestData["region_name"]; if (!m_application.SceneManager.TrySetCurrentScene(region_name)) throw new Exception(String.Format("failed to switch to region {0}", region_name)); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_name); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_name); } else throw new Exception("neither region_name nor region_uuid given"); @@ -2510,8 +2496,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.InfoFormat("[RADMIN] SaveXml: {0}", e.Message); - m_log.DebugFormat("[RADMIN] SaveXml: {0}", e.ToString()); + m_log.ErrorFormat("[RADMIN]: SaveXml: {0} {1}", e.Message, e.StackTrace); responseData["saved"] = false; responseData["switched"] = false; @@ -2550,14 +2535,14 @@ namespace OpenSim.ApplicationPlugins.RemoteController UUID region_uuid = (UUID) (string) requestData["region_uuid"]; if (!m_application.SceneManager.TrySetCurrentScene(region_uuid)) throw new Exception(String.Format("failed to switch to region {0}", region_uuid.ToString())); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_uuid.ToString()); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_uuid.ToString()); } else if (requestData.Contains("region_name")) { string region_name = (string) requestData["region_name"]; if (!m_application.SceneManager.TrySetCurrentScene(region_name)) throw new Exception(String.Format("failed to switch to region {0}", region_name)); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_name); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_name); } else throw new Exception("neither region_name nor region_uuid given"); @@ -2569,7 +2554,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.InfoFormat("[RADMIN] RegionQuery: {0}", e.Message); + m_log.InfoFormat("[RADMIN]: RegionQuery: {0}", e.Message); responseData["success"] = false; responseData["error"] = e.Message; @@ -2610,7 +2595,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.InfoFormat("[RADMIN] ConsoleCommand: {0}", e.Message); + m_log.InfoFormat("[RADMIN]: ConsoleCommand: {0}", e.Message); responseData["success"] = false; responseData["error"] = e.Message; @@ -2647,14 +2632,14 @@ namespace OpenSim.ApplicationPlugins.RemoteController UUID region_uuid = (UUID) (string) requestData["region_uuid"]; if (!m_application.SceneManager.TrySetCurrentScene(region_uuid)) throw new Exception(String.Format("failed to switch to region {0}", region_uuid.ToString())); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_uuid.ToString()); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_uuid.ToString()); } else if (requestData.Contains("region_name")) { string region_name = (string) requestData["region_name"]; if (!m_application.SceneManager.TrySetCurrentScene(region_name)) throw new Exception(String.Format("failed to switch to region {0}", region_name)); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_name); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_name); } else throw new Exception("neither region_name nor region_uuid given"); @@ -2665,7 +2650,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.InfoFormat("[RADMIN] Access List Clear Request: {0}", e.Message); + m_log.ErrorFormat("[RADMIN]: Access List Clear Request: {0} {1}", e.Message, e.StackTrace); responseData["success"] = false; responseData["error"] = e.Message; @@ -2704,14 +2689,14 @@ namespace OpenSim.ApplicationPlugins.RemoteController UUID region_uuid = (UUID) (string) requestData["region_uuid"]; if (!m_application.SceneManager.TrySetCurrentScene(region_uuid)) throw new Exception(String.Format("failed to switch to region {0}", region_uuid.ToString())); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_uuid.ToString()); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_uuid.ToString()); } else if (requestData.Contains("region_name")) { string region_name = (string) requestData["region_name"]; if (!m_application.SceneManager.TrySetCurrentScene(region_name)) throw new Exception(String.Format("failed to switch to region {0}", region_name)); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_name); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_name); } else throw new Exception("neither region_name nor region_uuid given"); @@ -2731,7 +2716,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController if (account != null) { uuids.Add(account.PrincipalID); - m_log.DebugFormat("[RADMIN] adding \"{0}\" to ACL for \"{1}\"", name, scene.RegionInfo.RegionName); + m_log.DebugFormat("[RADMIN]: adding \"{0}\" to ACL for \"{1}\"", name, scene.RegionInfo.RegionName); } } List accessControlList = new List(scene.RegionInfo.EstateSettings.EstateAccess); @@ -2752,7 +2737,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.InfoFormat("[RADMIN] Access List Add Request: {0}", e.Message); + m_log.ErrorFormat("[RADMIN]: Access List Add Request: {0} {1}", e.Message, e.StackTrace); responseData["success"] = false; responseData["error"] = e.Message; @@ -2791,14 +2776,14 @@ namespace OpenSim.ApplicationPlugins.RemoteController UUID region_uuid = (UUID) (string) requestData["region_uuid"]; if (!m_application.SceneManager.TrySetCurrentScene(region_uuid)) throw new Exception(String.Format("failed to switch to region {0}", region_uuid.ToString())); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_uuid.ToString()); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_uuid.ToString()); } else if (requestData.Contains("region_name")) { string region_name = (string) requestData["region_name"]; if (!m_application.SceneManager.TrySetCurrentScene(region_name)) throw new Exception(String.Format("failed to switch to region {0}", region_name)); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_name); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_name); } else throw new Exception("neither region_name nor region_uuid given"); @@ -2839,7 +2824,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.InfoFormat("[RADMIN] Access List Remove Request: {0}", e.Message); + m_log.ErrorFormat("[RADMIN]: Access List Remove Request: {0} {1}", e.Message, e.StackTrace); responseData["success"] = false; responseData["error"] = e.Message; @@ -2878,14 +2863,14 @@ namespace OpenSim.ApplicationPlugins.RemoteController UUID region_uuid = (UUID) (string) requestData["region_uuid"]; if (!m_application.SceneManager.TrySetCurrentScene(region_uuid)) throw new Exception(String.Format("failed to switch to region {0}", region_uuid.ToString())); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_uuid.ToString()); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_uuid.ToString()); } else if (requestData.Contains("region_name")) { string region_name = (string) requestData["region_name"]; if (!m_application.SceneManager.TrySetCurrentScene(region_name)) throw new Exception(String.Format("failed to switch to region {0}", region_name)); - m_log.InfoFormat("[RADMIN] Switched to region {0}", region_name); + m_log.InfoFormat("[RADMIN]: Switched to region {0}", region_name); } else throw new Exception("neither region_name nor region_uuid given"); @@ -2907,7 +2892,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { - m_log.InfoFormat("[RADMIN] Acces List List: {0}", e.Message); + m_log.ErrorFormat("[RADMIN]: Access List List: {0} {1}", e.Message, e.StackTrace); responseData["success"] = false; responseData["error"] = e.Message; From cd6dce11086a4e8a06257659c19eaa66d3b1a951 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Fri, 15 Apr 2011 16:42:40 -0700 Subject: [PATCH 081/103] Fixes mantis #5431 -- coalesced objects in HG-enabled regions. Needs more testing to make sure HG inventory isn't broken by this (it shouldn't). --- .../HGInventoryAccessModule.cs | 27 +++---------------- .../InventoryAccess/InventoryAccessModule.cs | 10 ++++++- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs index 4565d103ff..8c99983a95 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs @@ -129,35 +129,14 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess } /// - /// DeleteToInventory + /// Used in DeleteToInventory /// - public override UUID DeleteToInventory(DeRezAction action, UUID folderID, List objectGroups, IClientAPI remoteClient) + protected override void ExportAsset(UUID agentID, UUID assetID) { - UUID ret = UUID.Zero; - - // HACK: Only works for lists of length one. - // Intermediate version, just to make things compile - foreach (SceneObjectGroup g in objectGroups) - ret = DeleteToInventory(action, folderID, g, remoteClient); - - return ret; - } - - // DO NOT OVERRIDE THE BASE METHOD - public new virtual UUID DeleteToInventory(DeRezAction action, UUID folderID, - SceneObjectGroup objectGroup, IClientAPI remoteClient) - { - UUID assetID = base.DeleteToInventory(action, folderID, new List() {objectGroup}, remoteClient); - if (!assetID.Equals(UUID.Zero)) - { - if (remoteClient != null) - UploadInventoryItem(remoteClient.AgentId, assetID, "", 0); - } + UploadInventoryItem(agentID, assetID, "", 0); else m_log.Debug("[HGScene]: Scene.Inventory did not create asset"); - - return assetID; } /// diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 8b68dbe64a..a716326924 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -330,11 +330,19 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess } } } + + // This is a hook to do some per-asset post-processing for subclasses that need that + ExportAsset(remoteClient.AgentId, assetID); } return assetID; } - + + protected virtual void ExportAsset(UUID agentID, UUID assetID) + { + // nothing to do here + } + /// /// Add relevant permissions for an object to the item. /// From 3e0e1057acffa2c92a6f949cef0183193033d8c2 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Fri, 15 Apr 2011 16:44:53 -0700 Subject: [PATCH 082/103] Remove the call to remove tokens from the parent. Under heavy load this appears to cause problems with the system timer resolution. This caused a problem with tokens going into the root throttle as bursts leading to some starvation. Also changed EnqueueOutgoing to always queue a packet if there are already packets in the queue. Ensures consistent ordering of packet sends. --- OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 10 ++++++++++ OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs | 9 ++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 5a69851b3c..7be8a0ae59 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -440,6 +440,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.LocklessQueue queue = m_packetOutboxes[category]; TokenBucket bucket = m_throttleCategories[category]; + // Don't send this packet if there is already a packet waiting in the queue + // even if we have the tokens to send it, tokens should go to the already + // queued packets + if (queue.Count > 0) + { + queue.Enqueue(packet); + return true; + } + + if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength)) { // Enough tokens were removed from the bucket, the packet will not be queued diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index e4d59ff829..07b0a1df7a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -255,11 +255,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP // If we have enough tokens then remove them and return if (m_tokenCount - amount >= 0) { - if (m_parent == null || m_parent.RemoveTokens(amount)) - { - m_tokenCount -= amount; - return true; - } + // we don't have to remove from the parent, the drip rate is already + // reflective of the drip rate limits in the parent + m_tokenCount -= amount; + return true; } return false; From 70084c5e3a34e681357baf5506bd0c8f9815049b Mon Sep 17 00:00:00 2001 From: Dan Lake Date: Sat, 16 Apr 2011 15:29:28 -0700 Subject: [PATCH 083/103] More descriptive debug msg for ServiceOSDRequest failures. --- OpenSim/Framework/WebUtil.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs index 64cd014f5c..9d70f63189 100644 --- a/OpenSim/Framework/WebUtil.cs +++ b/OpenSim/Framework/WebUtil.cs @@ -222,8 +222,8 @@ namespace OpenSim.Framework m_log.InfoFormat("[WEB UTIL]: osd request <{0}> (URI:{1}, METHOD:{2}) took {3}ms overall, {4}ms writing", reqnum,url,method,tickdiff,tickdata); } - - m_log.WarnFormat("[WEB UTIL] <{0}> osd request failed: {1}",reqnum,errorMessage); + + m_log.WarnFormat("[WEB UTIL]: <{0}> osd request for {1}, method {2} FAILED: {3}", reqnum, url, method, errorMessage); return ErrorResponseMap(errorMessage); } From 03e725ad87916965ebcd8cb264f7a614342da9b7 Mon Sep 17 00:00:00 2001 From: Melanie Date: Sun, 17 Apr 2011 21:43:16 +0100 Subject: [PATCH 084/103] Mantis #5442: Add admin_save_heightmap --- .../RemoteController/RemoteAdminPlugin.cs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index dc4309fbe2..5a011cecb3 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -130,6 +130,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController availableMethods["admin_broadcast"] = XmlRpcAlertMethod; availableMethods["admin_restart"] = XmlRpcRestartMethod; availableMethods["admin_load_heightmap"] = XmlRpcLoadHeightmapMethod; + availableMethods["admin_save_heightmap"] = XmlRpcSaveHeightmapMethod; // User management availableMethods["admin_create_user"] = XmlRpcCreateUserMethod; availableMethods["admin_create_user_email"] = XmlRpcCreateUserMethod; @@ -357,6 +358,61 @@ namespace OpenSim.ApplicationPlugins.RemoteController return response; } + public XmlRpcResponse XmlRpcSaveHeightmapMethod(XmlRpcRequest request, IPEndPoint remoteClient) + + { + XmlRpcResponse response = new XmlRpcResponse(); + Hashtable responseData = new Hashtable(); + + m_log.Info("[RADMIN]: Save height maps request started"); + + try + { + Hashtable requestData = (Hashtable)request.Params[0]; + + m_log.DebugFormat("[RADMIN]: Save Terrain: XmlRpc {0}", request.ToString()); + + CheckStringParameters(request, new string[] { "password", "filename", "regionid" }); + + if (m_requiredPassword != String.Empty && + (!requestData.Contains("password") || (string)requestData["password"] != m_requiredPassword)) + throw new Exception("wrong password"); + + string file = (string)requestData["filename"]; + UUID regionID = (UUID)(string)requestData["regionid"]; + m_log.InfoFormat("[RADMIN]: Terrain Saving: {0}", file); + + responseData["accepted"] = true; + + Scene region = null; + + if (!m_application.SceneManager.TryGetScene(regionID, out region)) + throw new Exception("1: unable to get a scene with that name"); + + ITerrainModule terrainModule = region.RequestModuleInterface(); + if (null == terrainModule) throw new Exception("terrain module not available"); + + terrainModule.SaveToFile(file); + + responseData["success"] = false; + + response.Value = responseData; + } + catch (Exception e) + { + m_log.ErrorFormat("[RADMIN]: Terrain Saving: failed: {0}", e.Message); + m_log.DebugFormat("[RADMIN]: Terrain Saving: failed: {0}", e.ToString()); + + responseData["success"] = false; + responseData["error"] = e.Message; + + } + + m_log.Info("[RADMIN]: Save height maps request complete"); + + return response; + } + public XmlRpcResponse XmlRpcShutdownMethod(XmlRpcRequest request, IPEndPoint remoteClient) { m_log.Info("[RADMIN]: Received Shutdown Administrator Request"); From 419fc9427ef14eea443ec6f57501c6915c2de3b9 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 18 Apr 2011 20:03:53 +0100 Subject: [PATCH 085/103] Provide a configuration setting to control whether multiple taken objects are coalesced to inventory This is the CoalesceMultipleObjectsToInventory setting in [Inventory] in OpenSimDefaults.ini Default is true. --- .../HGInventoryAccessModule.cs | 3 + .../InventoryAccess/InventoryAccessModule.cs | 59 ++++++++++++++----- bin/OpenSimDefaults.ini | 7 +++ 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs index 8c99983a95..52791cb046 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs @@ -75,6 +75,9 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess if (name == Name) { m_Enabled = true; + + InitialiseCommon(source); + m_log.InfoFormat("[HG INVENTORY ACCESS MODULE]: {0} enabled.", Name); IConfig thisModuleConfig = source.Configs["HGInventoryAccessModule"]; diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index a716326924..e030ba11f7 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -64,7 +64,8 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess return m_UserManagement; } } - + + public bool CoalesceMultipleObjectsToInventory { get; set; } #region INonSharedRegionModule @@ -87,10 +88,28 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess if (name == Name) { m_Enabled = true; - m_log.InfoFormat("[INVENTORY ACCESS MODULE]: {0} enabled.", Name); + + InitialiseCommon(source); + + m_log.InfoFormat("[INVENTORY ACCESS MODULE]: {0} enabled.", Name); } } } + + /// + /// Common module config for both this and descendant classes. + /// + /// + protected virtual void InitialiseCommon(IConfigSource source) + { + IConfig inventoryConfig = source.Configs["Inventory"]; + + if (inventoryConfig != null) + CoalesceMultipleObjectsToInventory + = inventoryConfig.GetBoolean("CoalesceMultipleObjectsToInventory", true); + else + CoalesceMultipleObjectsToInventory = true; + } public virtual void PostInitialise() { @@ -206,20 +225,30 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess public virtual UUID DeleteToInventory(DeRezAction action, UUID folderID, List objectGroups, IClientAPI remoteClient) { - UUID ret = UUID.Zero; - - // The following code groups the SOG's by owner. No objects - // belonging to different people can be coalesced, for obvious - // reasons. - Dictionary> deletes = - new Dictionary>(); - - foreach (SceneObjectGroup g in objectGroups) + Dictionary> deletes = new Dictionary>(); + + if (CoalesceMultipleObjectsToInventory) { - if (!deletes.ContainsKey(g.OwnerID)) - deletes[g.OwnerID] = new List(); - - deletes[g.OwnerID].Add(g); + // The following code groups the SOG's by owner. No objects + // belonging to different people can be coalesced, for obvious + // reasons. + foreach (SceneObjectGroup g in objectGroups) + { + if (!deletes.ContainsKey(g.OwnerID)) + deletes[g.OwnerID] = new List(); + + deletes[g.OwnerID].Add(g); + } + } + else + { + // If we don't want to coalesce then put every object in its own bundle. + foreach (SceneObjectGroup g in objectGroups) + { + List bundle = new List(); + bundle.Add(g); + deletes[g.UUID] = bundle; + } } // This is method scoped and will be returned. It will be the diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index 107e859827..2e0a22bc8a 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -453,6 +453,13 @@ ; ForwardOfflineGroupMessages = true +[Inventory] + ; Control whether multiple objects sent to inventory should be coaleseced into a single item + ; There are still some issues with coalescence, including the fact that rotation is not restored + ; and some assets may be missing from archive files. + CoalesceMultipleObjectsToInventory = true + + [ODEPhysicsSettings] ;## ;## World Settings From 5a404a9ab1b8c684e5704efe09205f44d4a2e353 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 18 Apr 2011 21:07:11 +0100 Subject: [PATCH 086/103] Fix bug where objects were being coalesced in the wrong positions. This addresses http://opensimulator.org/mantis/view.php?id=5441 The bug was due to a pre-existing mistake in creating the inventory stored position --- .../Framework/InventoryAccess/InventoryAccessModule.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index e030ba11f7..326ef28b9e 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -269,9 +269,9 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess ? 250 : objectGroup.AbsolutePosition.X) , - (objectGroup.AbsolutePosition.X > (int)Constants.RegionSize) + (objectGroup.AbsolutePosition.Y > (int)Constants.RegionSize) ? 250 - : objectGroup.AbsolutePosition.X, + : objectGroup.AbsolutePosition.Y, objectGroup.AbsolutePosition.Z); originalPositions[objectGroup.UUID] = objectGroup.AbsolutePosition; From 71114d4ad1c4aacbfedea9f4ab5307162ae5d9e0 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 18 Apr 2011 21:34:26 +0100 Subject: [PATCH 087/103] refactor: rename DeleteToInventory() to CopyToInventory() DeleteToInventory() is misleading - it is the caller that decides whether to delete or not --- .../InventoryAccess/InventoryAccessModule.cs | 2 +- .../Interfaces/IInventoryAccessModule.cs | 18 +++++++++++++++++- .../Scenes/AsyncSceneObjectGroupDeleter.cs | 3 ++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 326ef28b9e..666f35339c 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -222,7 +222,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess /// /// /// - public virtual UUID DeleteToInventory(DeRezAction action, UUID folderID, + public virtual UUID CopyToInventory(DeRezAction action, UUID folderID, List objectGroups, IClientAPI remoteClient) { Dictionary> deletes = new Dictionary>(); diff --git a/OpenSim/Region/Framework/Interfaces/IInventoryAccessModule.cs b/OpenSim/Region/Framework/Interfaces/IInventoryAccessModule.cs index 05fc2add3e..305975e855 100644 --- a/OpenSim/Region/Framework/Interfaces/IInventoryAccessModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IInventoryAccessModule.cs @@ -38,7 +38,23 @@ namespace OpenSim.Region.Framework.Interfaces public interface IInventoryAccessModule { UUID CapsUpdateInventoryItemAsset(IClientAPI remoteClient, UUID itemID, byte[] data); - UUID DeleteToInventory(DeRezAction action, UUID folderID, List objectGroups, IClientAPI remoteClient); + + /// + /// Copy objects to a user's inventory. + /// + /// + /// Is it left to the caller to delete them from the scene if required. + /// + /// + /// + /// + /// + /// + /// Returns the UUID of the newly created item asset (not the item itself). + /// FIXME: This is not very useful. It would be far more useful to return a list of items instead. + /// + UUID CopyToInventory(DeRezAction action, UUID folderID, List objectGroups, IClientAPI remoteClient); + SceneObjectGroup RezObject(IClientAPI remoteClient, UUID itemID, Vector3 RayEnd, Vector3 RayStart, UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment); diff --git a/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs b/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs index 8feb0227fc..342354257d 100644 --- a/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs +++ b/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs @@ -143,7 +143,8 @@ namespace OpenSim.Region.Framework.Scenes { IInventoryAccessModule invAccess = m_scene.RequestModuleInterface(); if (invAccess != null) - invAccess.DeleteToInventory(x.action, x.folderID, x.objectGroups, x.remoteClient); + invAccess.CopyToInventory(x.action, x.folderID, x.objectGroups, x.remoteClient); + if (x.permissionToDelete) { foreach (SceneObjectGroup g in x.objectGroups) From 61619ddefc96be2c1f802360cca48b1b36be8bb2 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 18 Apr 2011 21:59:01 +0100 Subject: [PATCH 088/103] refactor: split out the code which actually copies a particular bundle to inventory --- .../InventoryAccess/InventoryAccessModule.cs | 237 +++++++++--------- 1 file changed, 123 insertions(+), 114 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 666f35339c..6b3df9ddd6 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -213,19 +213,11 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess return UUID.Zero; } - - /// - /// Delete a scene object from a scene and place in the given avatar's inventory. - /// Returns the UUID of the newly created asset. - /// - /// - /// - /// - /// + public virtual UUID CopyToInventory(DeRezAction action, UUID folderID, List objectGroups, IClientAPI remoteClient) { - Dictionary> deletes = new Dictionary>(); + Dictionary> bundlesToCopy = new Dictionary>(); if (CoalesceMultipleObjectsToInventory) { @@ -234,10 +226,10 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess // reasons. foreach (SceneObjectGroup g in objectGroups) { - if (!deletes.ContainsKey(g.OwnerID)) - deletes[g.OwnerID] = new List(); + if (!bundlesToCopy.ContainsKey(g.OwnerID)) + bundlesToCopy[g.OwnerID] = new List(); - deletes[g.OwnerID].Add(g); + bundlesToCopy[g.OwnerID].Add(g); } } else @@ -247,7 +239,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess { List bundle = new List(); bundle.Add(g); - deletes[g.UUID] = bundle; + bundlesToCopy[g.UUID] = bundle; } } @@ -257,112 +249,129 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess // Each iteration is really a separate asset being created, // with distinct destinations as well. - foreach (List objlist in deletes.Values) + foreach (List bundle in bundlesToCopy.Values) + assetID = CopyBundleToInventory(action, folderID, bundle, remoteClient); + + return assetID; + } + + /// + /// Copy a bundle of objects to inventory. If there is only one object, then this will create an object + /// item. If there are multiple objects then these will be saved as a single coalesced item. + /// + /// + /// + /// + /// + /// + protected UUID CopyBundleToInventory( + DeRezAction action, UUID folderID, List objlist, IClientAPI remoteClient) + { + UUID assetID = UUID.Zero; + + CoalescedSceneObjects coa = new CoalescedSceneObjects(UUID.Zero); + Dictionary originalPositions = new Dictionary(); + + foreach (SceneObjectGroup objectGroup in objlist) { - CoalescedSceneObjects coa = new CoalescedSceneObjects(UUID.Zero); - Dictionary originalPositions = new Dictionary(); + Vector3 inventoryStoredPosition = new Vector3 + (((objectGroup.AbsolutePosition.X > (int)Constants.RegionSize) + ? 250 + : objectGroup.AbsolutePosition.X) + , + (objectGroup.AbsolutePosition.Y > (int)Constants.RegionSize) + ? 250 + : objectGroup.AbsolutePosition.Y, + objectGroup.AbsolutePosition.Z); - foreach (SceneObjectGroup objectGroup in objlist) - { - Vector3 inventoryStoredPosition = new Vector3 - (((objectGroup.AbsolutePosition.X > (int)Constants.RegionSize) - ? 250 - : objectGroup.AbsolutePosition.X) - , - (objectGroup.AbsolutePosition.Y > (int)Constants.RegionSize) - ? 250 - : objectGroup.AbsolutePosition.Y, - objectGroup.AbsolutePosition.Z); + originalPositions[objectGroup.UUID] = objectGroup.AbsolutePosition; - originalPositions[objectGroup.UUID] = objectGroup.AbsolutePosition; + objectGroup.AbsolutePosition = inventoryStoredPosition; - objectGroup.AbsolutePosition = inventoryStoredPosition; - - // Make sure all bits but the ones we want are clear - // on take. - // This will be applied to the current perms, so - // it will do what we want. - objectGroup.RootPart.NextOwnerMask &= - ((uint)PermissionMask.Copy | - (uint)PermissionMask.Transfer | - (uint)PermissionMask.Modify); - objectGroup.RootPart.NextOwnerMask |= - (uint)PermissionMask.Move; - - coa.Add(objectGroup); - } - - string itemXml; - - if (objlist.Count > 1) - itemXml = CoalescedSceneObjectsSerializer.ToXml(coa); - else - itemXml = SceneObjectSerializer.ToOriginalXmlFormat(objlist[0]); + // Make sure all bits but the ones we want are clear + // on take. + // This will be applied to the current perms, so + // it will do what we want. + objectGroup.RootPart.NextOwnerMask &= + ((uint)PermissionMask.Copy | + (uint)PermissionMask.Transfer | + (uint)PermissionMask.Modify); + objectGroup.RootPart.NextOwnerMask |= + (uint)PermissionMask.Move; - // Restore the position of each group now that it has been stored to inventory. - foreach (SceneObjectGroup objectGroup in objlist) - objectGroup.AbsolutePosition = originalPositions[objectGroup.UUID]; - - InventoryItemBase item = CreateItemForObject(action, remoteClient, objlist[0], folderID); - if (item == null) - return UUID.Zero; - - // Can't know creator is the same, so null it in inventory - if (objlist.Count > 1) - { - item.CreatorId = UUID.Zero.ToString(); - item.Flags = (uint)InventoryItemFlags.ObjectHasMultipleItems; - } - else - { - item.CreatorId = objlist[0].RootPart.CreatorID.ToString(); - item.SaleType = objlist[0].RootPart.ObjectSaleType; - item.SalePrice = objlist[0].RootPart.SalePrice; - } - - AssetBase asset = CreateAsset( - objlist[0].GetPartName(objlist[0].RootPart.LocalId), - objlist[0].GetPartDescription(objlist[0].RootPart.LocalId), - (sbyte)AssetType.Object, - Utils.StringToBytes(itemXml), - objlist[0].OwnerID.ToString()); - m_Scene.AssetService.Store(asset); - - item.AssetID = asset.FullID; - assetID = asset.FullID; - - if (DeRezAction.SaveToExistingUserInventoryItem == action) - { - m_Scene.InventoryService.UpdateItem(item); - } - else - { - AddPermissions(item, objlist[0], objlist, remoteClient); - - item.CreationDate = Util.UnixTimeSinceEpoch(); - item.Description = asset.Description; - item.Name = asset.Name; - item.AssetType = asset.Type; - - m_Scene.AddInventoryItem(item); - - if (remoteClient != null && item.Owner == remoteClient.AgentId) - { - remoteClient.SendInventoryItemCreateUpdate(item, 0); - } - else - { - ScenePresence notifyUser = m_Scene.GetScenePresence(item.Owner); - if (notifyUser != null) - { - notifyUser.ControllingClient.SendInventoryItemCreateUpdate(item, 0); - } - } - } - - // This is a hook to do some per-asset post-processing for subclasses that need that - ExportAsset(remoteClient.AgentId, assetID); + coa.Add(objectGroup); } + + string itemXml; + + if (objlist.Count > 1) + itemXml = CoalescedSceneObjectsSerializer.ToXml(coa); + else + itemXml = SceneObjectSerializer.ToOriginalXmlFormat(objlist[0]); + + // Restore the position of each group now that it has been stored to inventory. + foreach (SceneObjectGroup objectGroup in objlist) + objectGroup.AbsolutePosition = originalPositions[objectGroup.UUID]; + + InventoryItemBase item = CreateItemForObject(action, remoteClient, objlist[0], folderID); + if (item == null) + return UUID.Zero; + + // Can't know creator is the same, so null it in inventory + if (objlist.Count > 1) + { + item.CreatorId = UUID.Zero.ToString(); + item.Flags = (uint)InventoryItemFlags.ObjectHasMultipleItems; + } + else + { + item.CreatorId = objlist[0].RootPart.CreatorID.ToString(); + item.SaleType = objlist[0].RootPart.ObjectSaleType; + item.SalePrice = objlist[0].RootPart.SalePrice; + } + + AssetBase asset = CreateAsset( + objlist[0].GetPartName(objlist[0].RootPart.LocalId), + objlist[0].GetPartDescription(objlist[0].RootPart.LocalId), + (sbyte)AssetType.Object, + Utils.StringToBytes(itemXml), + objlist[0].OwnerID.ToString()); + m_Scene.AssetService.Store(asset); + + item.AssetID = asset.FullID; + assetID = asset.FullID; + + if (DeRezAction.SaveToExistingUserInventoryItem == action) + { + m_Scene.InventoryService.UpdateItem(item); + } + else + { + AddPermissions(item, objlist[0], objlist, remoteClient); + + item.CreationDate = Util.UnixTimeSinceEpoch(); + item.Description = asset.Description; + item.Name = asset.Name; + item.AssetType = asset.Type; + + m_Scene.AddInventoryItem(item); + + if (remoteClient != null && item.Owner == remoteClient.AgentId) + { + remoteClient.SendInventoryItemCreateUpdate(item, 0); + } + else + { + ScenePresence notifyUser = m_Scene.GetScenePresence(item.Owner); + if (notifyUser != null) + { + notifyUser.ControllingClient.SendInventoryItemCreateUpdate(item, 0); + } + } + } + + // This is a hook to do some per-asset post-processing for subclasses that need that + ExportAsset(remoteClient.AgentId, assetID); return assetID; } From e00e518692bd3d123db69dceb98b80d3bdd59156 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 18 Apr 2011 22:24:42 +0100 Subject: [PATCH 089/103] add test to ensure that an IAR starts with the control file --- .../Archiver/InventoryArchiveReadRequest.cs | 21 +++++++------- .../Archiver/Tests/InventoryArchiverTests.cs | 29 ++++++++++++++++++- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs index f3d2f26cfe..9acdc9040d 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs @@ -77,12 +77,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver /// private Stream m_loadStream; - /// - /// FIXME: Do not perform this check since older versions of OpenSim do save the control file after other things - /// (I thought they weren't). We will need to bump the version number and perform this check on all - /// subsequent IAR versions only - /// - protected bool m_controlFileLoaded = true; + public bool ControlFileLoaded { get; private set; } + protected bool m_assetsLoaded; protected bool m_inventoryNodesLoaded; @@ -131,6 +127,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver m_userInfo = userInfo; m_invPath = invPath; m_loadStream = loadStream; + + // FIXME: Do not perform this check since older versions of OpenSim do save the control file after other things + // (I thought they weren't). We will need to bump the version number and perform this check on all + // subsequent IAR versions only + ControlFileLoaded = true; } /// @@ -522,7 +523,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver /// /// /// - protected void LoadControlFile(string path, byte[] data) + public void LoadControlFile(string path, byte[] data) { XDocument doc = XDocument.Parse(Encoding.ASCII.GetString(data)); XElement archiveElement = doc.Element("archive"); @@ -538,7 +539,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver majorVersion, MAX_MAJOR_VERSION)); } - m_controlFileLoaded = true; + ControlFileLoaded = true; m_log.InfoFormat("[INVENTORY ARCHIVER]: Loading IAR with version {0}", version); } @@ -550,7 +551,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver /// protected void LoadInventoryFile(string path, TarArchiveReader.TarEntryType entryType, byte[] data) { - if (!m_controlFileLoaded) + if (!ControlFileLoaded) throw new Exception( string.Format( "The IAR you are trying to load does not list {0} before {1}. Aborting load", @@ -597,7 +598,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver /// protected void LoadAssetFile(string path, byte[] data) { - if (!m_controlFileLoaded) + if (!ControlFileLoaded) throw new Exception( string.Format( "The IAR you are trying to load does not list {0} before {1}. Aborting load", diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs index d03f6da295..52232a0c9e 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs @@ -94,7 +94,34 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests Assert.That(coaObjects[1].UUID, Is.EqualTo(UUID.Parse("00000000-0000-0000-0000-000000000140"))); Assert.That(coaObjects[1].AbsolutePosition, Is.EqualTo(new Vector3(25, 50, 75))); - } + } + + /// + /// Test that the IAR has the required files in the right order. + /// + /// + /// At the moment, the only thing that matters is that the control file is the very first one. + /// + [Test] + public void TestOrder() + { + TestHelper.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + MemoryStream archiveReadStream = new MemoryStream(m_iarStreamBytes); + TarArchiveReader tar = new TarArchiveReader(archiveReadStream); + string filePath; + TarArchiveReader.TarEntryType tarEntryType; + + byte[] data = tar.ReadEntry(out filePath, out tarEntryType); + Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH)); + + InventoryArchiveReadRequest iarr + = new InventoryArchiveReadRequest(null, null, null, (Stream)null, false); + iarr.LoadControlFile(filePath, data); + + Assert.That(iarr.ControlFileLoaded, Is.True); + } /// /// Test saving a single inventory item to a V0.1 OpenSim Inventory Archive From 6600aa2baf56e1f58e50eb5589de876e910131bb Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 18 Apr 2011 22:35:33 +0100 Subject: [PATCH 090/103] Add regression test to check that OARs start with the control file. --- .../Archiver/InventoryArchiveReadRequest.cs | 3 +++ .../World/Archiver/ArchiveReadRequest.cs | 9 ++++++++- .../World/Archiver/Tests/ArchiverTests.cs | 18 ++++++++++-------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs index 9acdc9040d..a12931f5e9 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs @@ -77,6 +77,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver /// private Stream m_loadStream; + /// + /// Has the control file been loaded for this archive? + /// public bool ControlFileLoaded { get; private set; } protected bool m_assetsLoaded; diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs index fd8f546737..82bef48c8c 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs @@ -57,6 +57,11 @@ namespace OpenSim.Region.CoreModules.World.Archiver /// bumps here should be compatible. /// public static int MAX_MAJOR_VERSION = 1; + + /// + /// Has the control file been loaded for this archive? + /// + public bool ControlFileLoaded { get; private set; } protected Scene m_scene; protected Stream m_loadStream; @@ -527,7 +532,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver /// /// /// - protected void LoadControlFile(string path, byte[] data) + public void LoadControlFile(string path, byte[] data) { XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable()); XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None); @@ -573,6 +578,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver } currentRegionSettings.Save(); + + ControlFileLoaded = true; } } } \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs index e2760a2065..2307c8e585 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs @@ -171,7 +171,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests MemoryStream archiveReadStream = new MemoryStream(archive); TarArchiveReader tar = new TarArchiveReader(archiveReadStream); - bool gotControlFile = false; bool gotNcAssetFile = false; string expectedNcAssetFileName = string.Format("{0}_{1}", ncAssetUuid, "notecard.txt"); @@ -182,15 +181,19 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests expectedPaths.Add(ArchiveHelpers.CreateObjectPath(sog2)); string filePath; - TarArchiveReader.TarEntryType tarEntryType; + TarArchiveReader.TarEntryType tarEntryType; + byte[] data = tar.ReadEntry(out filePath, out tarEntryType); + Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH)); + + ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty); + arr.LoadControlFile(filePath, data); + + Assert.That(arr.ControlFileLoaded, Is.True); + while (tar.ReadEntry(out filePath, out tarEntryType) != null) { - if (ArchiveConstants.CONTROL_FILE_PATH == filePath) - { - gotControlFile = true; - } - else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH)) + if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH)) { string fileName = filePath.Remove(0, ArchiveConstants.ASSETS_PATH.Length); @@ -203,7 +206,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests } } - Assert.That(gotControlFile, Is.True, "No control file in archive"); Assert.That(gotNcAssetFile, Is.True, "No notecard asset file in archive"); Assert.That(foundPaths, Is.EquivalentTo(expectedPaths)); From 61096050386c8ec63d8d92c6ac7b183ddaa77c26 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 18 Apr 2011 22:42:36 +0100 Subject: [PATCH 091/103] bump oar version number to 0.6. This commit contains no data changes - the version bump is to establish a version at which the control file must come first in the archive. --- .../World/Archiver/ArchiveWriteRequestPreparation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs index f2d487ef94..597b7809e5 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs @@ -206,7 +206,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver /// public static string CreateControlFile(Dictionary options) { - int majorVersion = MAX_MAJOR_VERSION, minorVersion = 5; + int majorVersion = MAX_MAJOR_VERSION, minorVersion = 6; // // if (options.ContainsKey("version")) // { From f5a041d01292adb2b1e650cb9eba0806121d2ddf Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 18 Apr 2011 23:00:41 +0100 Subject: [PATCH 092/103] bump default IAR version to 0.2 and 1.1 for the --profile version. There are no changes in this bump, they just signal a point at which the control file comes first in the archive. --- .../Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs index dc4900f5a7..c039b5a387 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs @@ -394,12 +394,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver if (options.ContainsKey("profile")) { majorVersion = 1; - minorVersion = 0; + minorVersion = 1; } else { majorVersion = 0; - minorVersion = 1; + minorVersion = 2; } m_log.InfoFormat("[INVENTORY ARCHIVER]: Creating version {0}.{1} IAR", majorVersion, minorVersion); From 36c4e94ef739f2d58ff26ae2313e5a930ff02021 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 18 Apr 2011 23:22:04 +0100 Subject: [PATCH 093/103] Move mesh on/off swtich from [Startup] to [Mesh] in anticipation of future config parameters. Default remains true. OpenSimDefault.ini changed so if you haven't overriden this switch then you don't need to do anything. --- .../Region/CoreModules/Avatar/Assets/GetMeshModule.cs | 7 +++---- .../Assets/NewFileAgentInventoryVariablePriceModule.cs | 7 +++---- .../Inventory/Archiver/InventoryArchiveReadRequest.cs | 6 ++++++ bin/OpenSimDefaults.ini | 10 ++++++---- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Assets/GetMeshModule.cs b/OpenSim/Region/CoreModules/Avatar/Assets/GetMeshModule.cs index d2278bc3de..deec444e8b 100644 --- a/OpenSim/Region/CoreModules/Avatar/Assets/GetMeshModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Assets/GetMeshModule.cs @@ -66,12 +66,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets public void Initialise(IConfigSource source) { - IConfig startupConfig = source.Configs["Startup"]; - if (startupConfig == null) + IConfig meshConfig = source.Configs["Mesh"]; + if (meshConfig == null) return; - if (!startupConfig.GetBoolean("ColladaMesh",true)) - m_enabled = false; + m_enabled = meshConfig.GetBoolean("ColladaMesh", true); } public void AddRegion(Scene pScene) diff --git a/OpenSim/Region/CoreModules/Avatar/Assets/NewFileAgentInventoryVariablePriceModule.cs b/OpenSim/Region/CoreModules/Avatar/Assets/NewFileAgentInventoryVariablePriceModule.cs index fb07cc9ca0..d651cb26f8 100644 --- a/OpenSim/Region/CoreModules/Avatar/Assets/NewFileAgentInventoryVariablePriceModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Assets/NewFileAgentInventoryVariablePriceModule.cs @@ -68,12 +68,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Assets public void Initialise(IConfigSource source) { - IConfig startupConfig = source.Configs["Startup"]; - if (startupConfig == null) + IConfig meshConfig = source.Configs["Mesh"]; + if (meshConfig == null) return; - if (!startupConfig.GetBoolean("ColladaMesh",true)) - m_enabled = false; + m_enabled = meshConfig.GetBoolean("ColladaMesh", true); } public void AddRegion(Scene pScene) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs index a12931f5e9..6b247180d8 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs @@ -82,6 +82,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver /// public bool ControlFileLoaded { get; private set; } + /// + /// Do we want to enforce the check. IAR versions before 0.2 and 1.1 do not guarantee this order, so we can't + /// enforce. + /// + public bool EnforceControlFileCheck { get; private set; } + protected bool m_assetsLoaded; protected bool m_inventoryNodesLoaded; diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index 2e0a22bc8a..e72e85103f 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -155,10 +155,6 @@ ; it may cause unexpected physics problems. ;UseMeshiesPhysicsMesh = false - ; enable / disable Collada mesh support - ; default is true - ; ColladaMesh = true - ; Choose one of the physics engines below ; OpenDynamicsEngine is by some distance the most developed physics engine ; basicphysics effectively does not model physics at all, making all objects phantom @@ -460,6 +456,12 @@ CoalesceMultipleObjectsToInventory = true +[Mesh] + ; enable / disable Collada mesh support + ; default is true + ; ColladaMesh = true + + [ODEPhysicsSettings] ;## ;## World Settings From 2fa210243b64bb10fbca37f89176f10efeb25f41 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 19 Apr 2011 21:54:26 +0100 Subject: [PATCH 094/103] Clean up freeswitch config to what is currently required. Add explanation to config parameters. Clean up some log messages. --- .../FreeSwitchVoice/FreeSwitchVoiceModule.cs | 28 +++++++------ .../FreeswitchService/FreeswitchService.cs | 29 +++++++------ .../FreeswitchServiceBase.cs | 2 +- bin/OpenSim.ini.example | 42 +++++++++++++------ bin/Robust.ini.example | 38 ++++++++++++----- .../StandaloneCommon.ini.example | 18 +++----- 6 files changed, 95 insertions(+), 62 deletions(-) diff --git a/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs index 7909d8a8ec..962b5cabfb 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs @@ -118,7 +118,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice if (serviceDll == String.Empty) { - m_log.Error("[FreeSwitchVoice]: No LocalServiceModule named in section FreeSwitchVoice"); + m_log.Error("[FreeSwitchVoice]: No LocalServiceModule named in section FreeSwitchVoice. Not starting."); return; } @@ -143,8 +143,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice if (String.IsNullOrEmpty(m_freeSwitchRealm) || String.IsNullOrEmpty(m_freeSwitchAPIPrefix)) { - m_log.Error("[FreeSwitchVoice] plugin mis-configured"); - m_log.Info("[FreeSwitchVoice] plugin disabled: incomplete configuration"); + m_log.Error("[FreeSwitchVoice]: Freeswitch service mis-configured. Not starting."); return; } @@ -172,16 +171,15 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice MainServer.Instance.AddHTTPHandler(String.Format("{0}/viv_buddy.php", m_freeSwitchAPIPrefix), FreeSwitchSLVoiceBuddyHTTPHandler); - m_log.InfoFormat("[FreeSwitchVoice] using FreeSwitch server {0}", m_freeSwitchRealm); + m_log.InfoFormat("[FreeSwitchVoice]: using FreeSwitch server {0}", m_freeSwitchRealm); m_Enabled = true; - m_log.Info("[FreeSwitchVoice] plugin enabled"); + m_log.Info("[FreeSwitchVoice]: plugin enabled"); } catch (Exception e) { - m_log.ErrorFormat("[FreeSwitchVoice] plugin initialization failed: {0}", e.Message); - m_log.DebugFormat("[FreeSwitchVoice] plugin initialization failed: {0}", e.ToString()); + m_log.ErrorFormat("[FreeSwitchVoice]: plugin initialization failed: {0} {1}", e.Message, e.StackTrace); return; } @@ -240,7 +238,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice { if (m_Enabled) { - m_log.Info("[FreeSwitchVoice] registering IVoiceModule with the scene"); + m_log.Info("[FreeSwitchVoice]: registering IVoiceModule with the scene"); // register the voice interface for this module, so the script engine can call us scene.RegisterModuleInterface(this); @@ -302,7 +300,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice // public void OnRegisterCaps(Scene scene, UUID agentID, Caps caps) { - m_log.DebugFormat("[FreeSwitchVoice] OnRegisterCaps: agentID {0} caps {1}", agentID, caps); + m_log.DebugFormat( + "[FreeSwitchVoice]: OnRegisterCaps called with agentID {0} caps {1} in scene {2}", + agentID, caps, scene.RegionInfo.RegionName); string capsBase = "/CAPS/" + caps.CapsObjectPath; caps.RegisterHandler("ProvisionVoiceAccountRequest", @@ -558,7 +558,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice public Hashtable FreeSwitchSLVoiceGetPreloginHTTPHandler(Hashtable request) { - m_log.Debug("[FreeSwitchVoice] FreeSwitchSLVoiceGetPreloginHTTPHandler called"); + m_log.Debug("[FreeSwitchVoice]: FreeSwitchSLVoiceGetPreloginHTTPHandler called"); Hashtable response = new Hashtable(); response["content_type"] = "text/xml"; @@ -664,7 +664,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice public Hashtable FreeSwitchSLVoiceSigninHTTPHandler(Hashtable request) { - m_log.Debug("[FreeSwitchVoice] FreeSwitchSLVoiceSigninHTTPHandler called"); + m_log.Debug("[FreeSwitchVoice]: FreeSwitchSLVoiceSigninHTTPHandler called"); // string requestbody = (string)request["body"]; // string uri = (string)request["uri"]; // string contenttype = (string)request["content-type"]; @@ -795,16 +795,18 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice response["keepalive"] = false; response["int_response_code"] = 500; - Hashtable requestBody = ParseRequestBody((string) request["body"]); + Hashtable requestBody = ParseRequestBody((string)request["body"]); string section = (string) requestBody["section"]; + + m_log.DebugFormat("[FreeSwitchVoice]: Received request for config section {0}", section); if (section == "directory") response = m_FreeswitchService.HandleDirectoryRequest(requestBody); else if (section == "dialplan") response = m_FreeswitchService.HandleDialplanRequest(requestBody); else - m_log.WarnFormat("[FreeSwitchVoice]: section was {0}", section); + m_log.WarnFormat("[FreeSwitchVoice]: Unknown section {0} was requested.", section); return response; } diff --git a/OpenSim/Services/FreeswitchService/FreeswitchService.cs b/OpenSim/Services/FreeswitchService/FreeswitchService.cs index fe6f5cd503..1ec89da5f7 100644 --- a/OpenSim/Services/FreeswitchService/FreeswitchService.cs +++ b/OpenSim/Services/FreeswitchService/FreeswitchService.cs @@ -50,13 +50,13 @@ namespace OpenSim.Services.FreeswitchService public Hashtable HandleDialplanRequest(Hashtable request) { - m_log.DebugFormat("[FreeSwitchVoice] HandleDialplanRequest called with {0}",request.ToString()); + m_log.DebugFormat("[FreeSwitchVoice]: HandleDialplanRequest called with {0}",request.ToString()); Hashtable response = new Hashtable(); foreach (DictionaryEntry item in request) { - m_log.InfoFormat("[FreeSwitchDirectory] requestBody item {0} {1}",item.Key, item.Value); + m_log.InfoFormat("[FreeSwitchDirectory]: requestBody item {0} {1}",item.Key, item.Value); } string requestcontext = (string) request["Hunt-Context"]; @@ -66,7 +66,7 @@ namespace OpenSim.Services.FreeswitchService if (m_freeSwitchContext != String.Empty && m_freeSwitchContext != requestcontext) { - m_log.Debug("[FreeSwitchDirectory] returning empty as it's for another context"); + m_log.Debug("[FreeSwitchDirectory]: returning empty as it's for another context"); response["str_response_string"] = ""; } else @@ -116,13 +116,16 @@ namespace OpenSim.Services.FreeswitchService { Hashtable response = new Hashtable(); string domain = (string) request["domain"]; - if (domain != m_freeSwitchRealm) { + if (domain != m_freeSwitchRealm) + { response["content_type"] = "text/xml"; response["keepalive"] = false; response["int_response_code"] = 200; response["str_response_string"] = ""; - } else { - m_log.DebugFormat("[FreeSwitchDirectory] HandleDirectoryRequest called with {0}",request.ToString()); + } + else + { +// m_log.DebugFormat("[FreeSwitchDirectory]: HandleDirectoryRequest called with {0}",request.ToString()); // information in the request we might be interested in @@ -145,7 +148,7 @@ namespace OpenSim.Services.FreeswitchService foreach (DictionaryEntry item in request) { - m_log.InfoFormat("[FreeSwitchDirectory] requestBody item {0} {1}", item.Key, item.Value); + m_log.DebugFormat("[FreeSwitchDirectory]: requestBody item {0} {1}", item.Key, item.Value); } string eventCallingFunction = (string) request["Event-Calling-Function"]; @@ -173,7 +176,7 @@ namespace OpenSim.Services.FreeswitchService } else { - m_log.ErrorFormat("[FreeSwitchVoice] HandleDirectoryRequest unknown sip_auth_method {0}",sipAuthMethod); + m_log.ErrorFormat("[FreeSwitchVoice]: HandleDirectoryRequest unknown sip_auth_method {0}",sipAuthMethod); response["int_response_code"] = 404; response["content_type"] = "text/xml"; response["str_response_string"] = ""; @@ -205,7 +208,7 @@ namespace OpenSim.Services.FreeswitchService } else { - m_log.ErrorFormat("[FreeSwitchVoice] HandleDirectoryRequest unknown Event-Calling-Function {0}",eventCallingFunction); + m_log.ErrorFormat("[FreeSwitchVoice]: HandleDirectoryRequest unknown Event-Calling-Function {0}",eventCallingFunction); response["int_response_code"] = 404; response["keepalive"] = false; response["content_type"] = "text/xml"; @@ -217,7 +220,7 @@ namespace OpenSim.Services.FreeswitchService private Hashtable HandleRegister(string Context, string Realm, Hashtable request) { - m_log.Info("[FreeSwitchDirectory] HandleRegister called"); + m_log.Info("[FreeSwitchDirectory]: HandleRegister called"); // TODO the password we return needs to match that sent in the request, this is hard coded for now string password = "1234"; @@ -254,7 +257,7 @@ namespace OpenSim.Services.FreeswitchService private Hashtable HandleInvite(string Context, string Realm, Hashtable request) { - m_log.Info("[FreeSwitchDirectory] HandleInvite called"); + m_log.Info("[FreeSwitchDirectory]: HandleInvite called"); // TODO the password we return needs to match that sent in the request, this is hard coded for now string password = "1234"; @@ -301,7 +304,7 @@ namespace OpenSim.Services.FreeswitchService private Hashtable HandleLocateUser(String Realm, Hashtable request) { - m_log.Info("[FreeSwitchDirectory] HandleLocateUser called"); + m_log.Info("[FreeSwitchDirectory]: HandleLocateUser called"); // TODO the password we return needs to match that sent in the request, this is hard coded for now string domain = (string) request["domain"]; @@ -335,7 +338,7 @@ namespace OpenSim.Services.FreeswitchService private Hashtable HandleConfigSofia(string Context, string Realm, Hashtable request) { - m_log.Info("[FreeSwitchDirectory] HandleConfigSofia called"); + m_log.Info("[FreeSwitchDirectory]: HandleConfigSofia called."); // TODO the password we return needs to match that sent in the request, this is hard coded for now string domain = (string) request["domain"]; diff --git a/OpenSim/Services/FreeswitchService/FreeswitchServiceBase.cs b/OpenSim/Services/FreeswitchService/FreeswitchServiceBase.cs index ebbb1b06ec..25c18b6a23 100644 --- a/OpenSim/Services/FreeswitchService/FreeswitchServiceBase.cs +++ b/OpenSim/Services/FreeswitchService/FreeswitchServiceBase.cs @@ -64,7 +64,7 @@ namespace OpenSim.Services.FreeswitchService m_freeSwitchDefaultWellKnownIP = freeswitchConfig.GetString("ServerAddress", String.Empty); if (m_freeSwitchDefaultWellKnownIP == String.Empty) { - m_log.Error("[FREESWITCH]: No FreeswitchServerAddress given, can't continue"); + m_log.Error("[FREESWITCH]: No ServerAddress given, cannot start service."); return; } diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index c05c3de2c4..fbaa590aed 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -621,10 +621,11 @@ ;; You need to load a local service for a standalone, and a remote service ;; for a grid region. Use one of the lines below, as appropriate + ;; If you're using Freeswitch on a standalone then you will also need to configure the [FreeswitchService] section ; LocalServiceModule = OpenSim.Services.FreeswitchService.dll:FreeswitchService ; LocalServiceModule = OpenSim.Services.Connectors.dll:RemoteFreeswitchConnector - ;; If using a remote module, specify the server URL + ;; If using a remote connector, specify the server URL ; FreeswitchServiceURL = http://my.grid.server:8003/fsapi @@ -632,20 +633,37 @@ ;; !!!!!!!!!!!!!!!!!!!!!!!!!!! ;; !!!!!!STANDALONE ONLY!!!!!! ;; !!!!!!!!!!!!!!!!!!!!!!!!!!! - ;; IP of your FS server - ;ServerAddress = 85.25.142.92 + ;; The IP address of your FreeSWITCH server. The common case is for this to be the same as the server running the OpenSim standalone + ;; This has to be set for the FreeSWITCH service to work + ;ServerAddress = 127.0.0.1 - ;; All other options are - well - optional - ; Realm = "127.0.0.1" - ; SIPProxy = "127.0.0.1:5060" - ; EchoServer = "127.0.0.1" - ; EchoPort = 50505 - ; AttemptSTUN = "false" + ;; The following configuration parameters are optional + + ;; By default, this is the same as the ServerAddress + ; Realm = 127.0.0.1 + + ;; By default, this is the same as the ServerAddress on port 5060 + ; SIPProxy = 127.0.0.1:5060 + + ;; Default is 5000ms ; DefaultTimeout = 5000 - ; Context = "default" - ; UserName = "freeswitch" - ; Password = "password" + ;; The dial plan context. Default is "default" + ; Context = default + + ;; Currently unused + ; UserName = freeswitch + + ;; Currently unused + ; Password = password + + ;; The following parameters are for STUN = Simple Traversal of UDP through NATs + ;; See http://wiki.freeswitch.org/wiki/NAT_Traversal + ;; stun.freeswitch.org is not guaranteed to be running so use it in + ;; production at your own risk + ; EchoServer = 127.0.0.1 + ; EchoPort = 50505 + ; AttemptSTUN = false [Groups] ;# {Enabled} {} {Enable groups?} {true false} false diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index 7c13076eb8..4d16236044 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -69,19 +69,37 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 ; * This is the configuration for the freeswitch server in grid mode [FreeswitchService] LocalServiceModule = "OpenSim.Services.FreeswitchService.dll:FreeswitchService" - ;; IP of your FS server + + ;; The IP address of your FreeSWITCH server. ; ServerAddress = 127.0.0.1 - ;; All other options are - well - optional - ; Realm = "127.0.0.1" - ; SIPProxy = "127.0.0.1:5060" - ; EchoServer = "127.0.0.1" - ; EchoPort = 50505 - ; AttemptSTUN = "false" + ;; The following configuration parameters are optional + + ;; By default, this is the same as the ServerAddress + ; Realm = 127.0.0.1 + + ;; By default, this is the same as the ServerAddress on port 5060 + ; SIPProxy = 127.0.0.1:5060 + + ;; Default is 5000ms ; DefaultTimeout = 5000 - ; Context = "default" - ; UserName = "freeswitch" - ; Password = "password" + + ;; The dial plan context. Default is "default" + ; Context = default + + ;; Currently unused + ; UserName = freeswitch + + ;; Currently unused + ; Password = password + + ;; The following parameters are for STUN = Simple Traversal of UDP through NATs + ;; See http://wiki.freeswitch.org/wiki/NAT_Traversal + ;; stun.freeswitch.org is not guaranteed to be running so use it in + ;; production at your own risk + ; EchoServer = 127.0.0.1 + ; EchoPort = 50505 + ; AttemptSTUN = false ; * This is the new style authentication service. Currently, only MySQL ; * is implemented. diff --git a/bin/config-include/StandaloneCommon.ini.example b/bin/config-include/StandaloneCommon.ini.example index d6f15bbf3b..dcebd63184 100644 --- a/bin/config-include/StandaloneCommon.ini.example +++ b/bin/config-include/StandaloneCommon.ini.example @@ -28,10 +28,10 @@ [HGInventoryAccessModule] ProfileServerURI = "http://127.0.0.1:9000/profiles" - ;; If you want to protect your assets from being copied by foreign visitors - ;; uncomment the next line. You may want to do this on sims that have licensed content. - ; OutboundPermission = False + ;; If you want to protect your assets from being copied by foreign visitors + ;; uncomment the next line. You may want to do this on sims that have licensed content. + ; OutboundPermission = False [Modules] ;; Choose 0 or 1 cache modules, and the corresponding config file, if it exists. @@ -45,16 +45,9 @@ AssetCaching = "CenomeMemoryAssetCache" Include-CenomeCache = "config-include/CenomeCache.ini" - ;; Enable this to use Freeswitch on a standalone - ;FreeswitchServiceInConnector = True - ;; Authorization is not on by default, as it depends on external php ;AuthorizationServices = "LocalAuthorizationServicesConnector" -[FreeswitchService] - ;; Configuration for the freeswitch service goes here - LocalServiceModule = "OpenSim.Services.FreeswitchService.dll:FreeswitchService" - [GridService] ;; For in-memory region storage (default) StorageProvider = "OpenSim.Data.Null.dll:NullRegionData" @@ -74,9 +67,8 @@ Region_Welcome_Area = "DefaultRegion, FallbackRegion" ; === HG ONLY === - ;; change this to the address of your simulator - Gatekeeper="http://127.0.0.1:9000" - + ;; change this to the address of your simulator + Gatekeeper="http://127.0.0.1:9000" [LibraryModule] ; Set this if you want to change the name of the OpenSim Library From 575257f3fe7c18bd831c6b0ad63ba30488b075cc Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 19 Apr 2011 21:58:55 +0100 Subject: [PATCH 095/103] For consistency, uncomment ColladaMesh option in OpenSimDefaults.ini. Thanks for the spot, Ai Austin. --- bin/OpenSimDefaults.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index e72e85103f..30ebf84dcc 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -459,7 +459,7 @@ [Mesh] ; enable / disable Collada mesh support ; default is true - ; ColladaMesh = true + ColladaMesh = true [ODEPhysicsSettings] From b786860bac7adec165843374ebe72b28f9519b78 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 19 Apr 2011 22:38:15 +0100 Subject: [PATCH 096/103] synchronize Robust.HG.ini.example FreeSWITCH config with other config files --- bin/Robust.HG.ini.example | 42 +++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index f12a1434ef..ced2857331 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -85,19 +85,41 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 ; * This is the configuration for the freeswitch server in grid mode [FreeswitchService] LocalServiceModule = "OpenSim.Services.FreeswitchService.dll:FreeswitchService" - ;; IP of your FS server + + ;; The IP address of your FreeSWITCH server. ; ServerAddress = 127.0.0.1 - ;; All other options are - well - optional - ; Realm = "127.0.0.1" - ; SIPProxy = "127.0.0.1:5060" - ; EchoServer = "127.0.0.1" - ; EchoPort = 50505 - ; AttemptSTUN = "false" + ;; The following configuration parameters are optional + + ;; By default, this is the same as the ServerAddress + ; Realm = 127.0.0.1 + + ;; By default, this is the same as the ServerAddress on port 5060 + ; SIPProxy = 127.0.0.1:5060 + + ;; Default is 5000ms ; DefaultTimeout = 5000 - ; Context = "default" - ; UserName = "freeswitch" - ; Password = "password" + + ;; The dial plan context. Default is "default" + ; Context = default + + ;; Currently unused + ; UserName = freeswitch + + ;; Currently unused + ; Password = password + + ;; The following parameters are for STUN = Simple Traversal of UDP through NATs + ;; See http://wiki.freeswitch.org/wiki/NAT_Traversal + ;; stun.freeswitch.org is not guaranteed to be running so use it in + ;; production at your own risk + ; EchoServer = 127.0.0.1 + ; EchoPort = 50505 + ; AttemptSTUN = false + + LocalServiceModule = "OpenSim.Services.FreeswitchService.dll:FreeswitchService" + ;; IP of your FS server + ; ServerAddress = 127.0.0.1 ; * This is the new style authentication service. Currently, only MySQL ; * is implemented. From ccc26f74436f0e3069587efd96497053e4129c3c Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 20 Apr 2011 01:02:40 +0100 Subject: [PATCH 097/103] Get Viewer 2 voice working with OpenSim. See http://opensimulator.org/mantis/view.php?id=5336 It turns out that viewer 2 was upset by the lack of a response to viv_watcher.php. This would send it into a continuous login loop. Viewer 1 was quite happy to ignore the lack of response. This commit puts in the bare minimum 'OK' message in response to viv_watcher.php. This allows viewer 2 voice to connect and appears to work. However, at some point we need to fill out the watcher response, whatever that is. --- .../Servers/HttpServer/BaseHttpServer.cs | 2 +- .../FreeSwitchVoice/FreeSwitchVoiceModule.cs | 79 ++++++++++++++++--- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index ccec9b70eb..ba89e2117b 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -430,7 +430,7 @@ namespace OpenSim.Framework.Servers.HttpServer string path = request.RawUrl; string handlerKey = GetHandlerKey(request.HttpMethod, path); - //m_log.DebugFormat("[BASE HTTP SERVER]: Handling {0} request for {1}", request.HttpMethod, path); +// m_log.DebugFormat("[BASE HTTP SERVER]: Handling {0} request for {1}", request.HttpMethod, path); if (TryGetStreamHandler(handlerKey, out requestHandler)) { diff --git a/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs index 962b5cabfb..373ffeb00b 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs @@ -163,13 +163,14 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice // String.Format("{0}/viv_get_prelogin.php", m_freeSwitchAPIPrefix), FreeSwitchSLVoiceGetPreloginHTTPHandler); // MainServer.Instance.AddStreamHandler(h); - - MainServer.Instance.AddHTTPHandler(String.Format("{0}/viv_signin.php", m_freeSwitchAPIPrefix), FreeSwitchSLVoiceSigninHTTPHandler); MainServer.Instance.AddHTTPHandler(String.Format("{0}/viv_buddy.php", m_freeSwitchAPIPrefix), FreeSwitchSLVoiceBuddyHTTPHandler); + + MainServer.Instance.AddHTTPHandler(String.Format("{0}/viv_watcher.php", m_freeSwitchAPIPrefix), + FreeSwitchSLVoiceWatcherHTTPHandler); m_log.InfoFormat("[FreeSwitchVoice]: using FreeSwitch server {0}", m_freeSwitchRealm); @@ -301,7 +302,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice public void OnRegisterCaps(Scene scene, UUID agentID, Caps caps) { m_log.DebugFormat( - "[FreeSwitchVoice]: OnRegisterCaps called with agentID {0} caps {1} in scene {2}", + "[FreeSwitchVoice]: OnRegisterCaps() called with agentID {0} caps {1} in scene {2}", agentID, caps, scene.RegionInfo.RegionName); string capsBase = "/CAPS/" + caps.CapsObjectPath; @@ -344,6 +345,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice public string ProvisionVoiceAccountRequest(Scene scene, string request, string path, string param, UUID agentID, Caps caps) { + m_log.DebugFormat( + "[FreeSwitchVoice][PROVISIONVOICE]: ProvisionVoiceAccountRequest() request: {0}, path: {1}, param: {2}", request, path, param); + ScenePresence avatar = scene.GetScenePresence(agentID); if (avatar == null) { @@ -357,9 +361,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice try { - //m_log.DebugFormat("[FreeSwitchVoice][PROVISIONVOICE]: request: {0}, path: {1}, param: {2}", - // request, path, param); - //XmlElement resp; string agentname = "x" + Convert.ToBase64String(agentID.GetBytes()); string password = "1234";//temp hack//new UUID(Guid.NewGuid()).ToString().Replace('-','Z').Substring(0,16); @@ -416,6 +417,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice public string ParcelVoiceInfoRequest(Scene scene, string request, string path, string param, UUID agentID, Caps caps) { +// m_log.DebugFormat( +// "[FreeSwitchVoice][PARCELVOICE]: ParcelVoiceInfoRequest() on {0} for {1}", +// scene.RegionInfo.RegionName, agentID); + ScenePresence avatar = scene.GetScenePresence(agentID); string avatarName = avatar.Name; @@ -502,6 +507,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice m_log.DebugFormat("[FreeSwitchVoice][CHATSESSION]: avatar \"{0}\": request: {1}, path: {2}, param: {3}", avatarName, request, path, param); + return "true"; } @@ -555,7 +561,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice return response; } - public Hashtable FreeSwitchSLVoiceGetPreloginHTTPHandler(Hashtable request) { m_log.Debug("[FreeSwitchVoice]: FreeSwitchSLVoiceGetPreloginHTTPHandler called"); @@ -592,6 +597,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice public Hashtable FreeSwitchSLVoiceBuddyHTTPHandler(Hashtable request) { + m_log.Debug("[FreeSwitchVoice]: FreeSwitchSLVoiceBuddyHTTPHandler called"); + Hashtable response = new Hashtable(); response["int_response_code"] = 200; response["str_response_string"] = string.Empty; @@ -650,18 +657,61 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice A {3} - ", ids[i],i,m_freeSwitchRealm,dt)); + ", ids[i], i ,m_freeSwitchRealm, dt)); } resp.Append(""); response["str_response_string"] = resp.ToString(); -// Regex normalizeEndLines = new Regex(@"\r\n", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.Multiline); - - //m_log.DebugFormat("[FREESWITCH]: {0}", normalizeEndLines.Replace((string)response["str_response_string"],"")); +// Regex normalizeEndLines = new Regex(@"(\r\n|\n)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.Multiline); +// +// m_log.DebugFormat( +// "[FREESWITCH]: FreeSwitchSLVoiceBuddyHTTPHandler() response {0}", +// normalizeEndLines.Replace((string)response["str_response_string"],"")); + return response; } + public Hashtable FreeSwitchSLVoiceWatcherHTTPHandler(Hashtable request) + { + m_log.Debug("[FreeSwitchVoice]: FreeSwitchSLVoiceWatcherHTTPHandler called"); + + Hashtable response = new Hashtable(); + response["int_response_code"] = 200; + response["content-type"] = "text/xml"; + + Hashtable requestBody = ParseRequestBody((string)request["body"]); + + string auth_token = (string)requestBody["auth_token"]; + //string[] auth_tokenvals = auth_token.Split(':'); + //string username = auth_tokenvals[0]; + + StringBuilder resp = new StringBuilder(); + resp.Append(""); + + // FIXME: This is enough of a response to stop viewer 2 complaining about a login failure and get voice to work. If we don't + // give an OK response, then viewer 2 engages in an continuous viv_signin.php, viv_buddy.php, viv_watcher.php loop + // Viewer 1 appeared happy to ignore the lack of reply and still works with this reply. + // + // However, really we need to fill in whatever watcher data should be here (whatever that is). + resp.Append(string.Format(@" + OK + lib_session + {0} + {0} + ", auth_token)); + + response["str_response_string"] = resp.ToString(); + +// Regex normalizeEndLines = new Regex(@"(\r\n|\n)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.Multiline); +// +// m_log.DebugFormat( +// "[FREESWITCH]: FreeSwitchSLVoiceWatcherHTTPHandler() response {0}", +// normalizeEndLines.Replace((string)response["str_response_string"],"")); + + return response; + } + public Hashtable FreeSwitchSLVoiceSigninHTTPHandler(Hashtable request) { m_log.Debug("[FreeSwitchVoice]: FreeSwitchSLVoiceSigninHTTPHandler called"); @@ -709,7 +759,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice ", userid, pos, avatarName); - response["int_response_code"] = 200; + response["int_response_code"] = 200; + +// m_log.DebugFormat("[FreeSwitchVoice]: Sending FreeSwitchSLVoiceSigninHTTPHandler response"); + return response; } @@ -823,4 +876,4 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice #endregion } -} +} \ No newline at end of file From 212326eeb0a7aa2180661e4c0ad253e2e7ae4515 Mon Sep 17 00:00:00 2001 From: dahlia Date: Tue, 19 Apr 2011 22:16:10 -0700 Subject: [PATCH 098/103] fix meshing failure on sculpt maps smaller than 64x64 --- OpenSim/Region/Physics/Meshing/SculptMap.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/OpenSim/Region/Physics/Meshing/SculptMap.cs b/OpenSim/Region/Physics/Meshing/SculptMap.cs index d2d71deade..740424ef91 100644 --- a/OpenSim/Region/Physics/Meshing/SculptMap.cs +++ b/OpenSim/Region/Physics/Meshing/SculptMap.cs @@ -62,6 +62,8 @@ namespace PrimMesher bool needsScaling = false; + bool smallMap = bmW * bmH <= lod * lod; + width = bmW; height = bmH; while (width * height > numLodPixels) @@ -104,9 +106,14 @@ namespace PrimMesher { for (int x = 0; x <= width; x++) { - int bmY = y < height ? y * 2 : y * 2 - 1; - int bmX = x < width ? x * 2 : x * 2 - 1; - Color c = bm.GetPixel(bmX, bmY); + Color c; + + if (smallMap) + c = bm.GetPixel(x < width ? x : x - 1, + y < height ? y : y - 1); + else + c = bm.GetPixel(x < width ? x * 2 : x * 2 - 1, + y < height ? y * 2 : y * 2 - 1); redBytes[byteNdx] = c.R; greenBytes[byteNdx] = c.G; From 63c1b7e4754271ec898592ba6209dbc776469370 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 21 Apr 2011 18:12:29 +0100 Subject: [PATCH 099/103] Alter uuid gather so that it properly analyzes coalesced objects. This should correct save all the assets required for the items within the coalesced objects in an IAR. This should also correctly gather the items on hypergrid takes. --- .../CoalescedSceneObjectsSerializer.cs | 51 +++++++++++-------- .../Region/Framework/Scenes/UuidGatherer.cs | 18 +++++-- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs index babcb54ae9..55455cc434 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs @@ -117,29 +117,40 @@ namespace OpenSim.Region.Framework.Scenes.Serialization { using (XmlTextReader reader = new XmlTextReader(sr)) { - reader.Read(); - if (reader.Name != "CoalescedObject") + try { -// m_log.DebugFormat( -// "[COALESCED SCENE OBJECTS SERIALIZER]: TryFromXml() root element was {0} so returning false", -// reader.Name); + reader.Read(); + if (reader.Name != "CoalescedObject") + { + // m_log.DebugFormat( + // "[COALESCED SCENE OBJECTS SERIALIZER]: TryFromXml() root element was {0} so returning false", + // reader.Name); + + return false; + } + + coa = new CoalescedSceneObjects(UUID.Zero); + reader.Read(); + + while (reader.NodeType != XmlNodeType.EndElement && reader.Name != "CoalescedObject") + { + if (reader.Name == "SceneObjectGroup") + { + string soXml = reader.ReadOuterXml(); + coa.Add(SceneObjectSerializer.FromOriginalXmlFormat(soXml)); + } + } + + reader.ReadEndElement(); // CoalescedObject + } + catch (Exception e) + { + m_log.ErrorFormat( + "[COALESCED SCENE OBJECTS SERIALIZER]: Deserialization of xml failed with {0} {1}", + e.Message, e.StackTrace); return false; - } - - coa = new CoalescedSceneObjects(UUID.Zero); - reader.Read(); - - while (reader.NodeType != XmlNodeType.EndElement && reader.Name != "CoalescedObject") - { - if (reader.Name == "SceneObjectGroup") - { - string soXml = reader.ReadOuterXml(); - coa.Add(SceneObjectSerializer.FromOriginalXmlFormat(soXml)); - } - } - - reader.ReadEndElement(); // CoalescedObject + } } } diff --git a/OpenSim/Region/Framework/Scenes/UuidGatherer.cs b/OpenSim/Region/Framework/Scenes/UuidGatherer.cs index 83906d745f..77b1535563 100644 --- a/OpenSim/Region/Framework/Scenes/UuidGatherer.cs +++ b/OpenSim/Region/Framework/Scenes/UuidGatherer.cs @@ -298,10 +298,20 @@ namespace OpenSim.Region.Framework.Scenes if (null != objectAsset) { string xml = Utils.BytesToString(objectAsset.Data); - SceneObjectGroup sog = SceneObjectSerializer.FromOriginalXmlFormat(xml); - - if (null != sog) - GatherAssetUuids(sog, assetUuids); + + CoalescedSceneObjects coa; + if (CoalescedSceneObjectsSerializer.TryFromXml(xml, out coa)) + { + foreach (SceneObjectGroup sog in coa.Objects) + GatherAssetUuids(sog, assetUuids); + } + else + { + SceneObjectGroup sog = SceneObjectSerializer.FromOriginalXmlFormat(xml); + + if (null != sog) + GatherAssetUuids(sog, assetUuids); + } } } From e36cab99fddc4dca338386101d5c204e6de5d2c5 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 21 Apr 2011 18:45:05 +0100 Subject: [PATCH 100/103] minor: small amount of method doc and some commented out odds and ends --- .../Scenes/SceneObjectGroup.Inventory.cs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs index e8095c0831..4bca3d0099 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs @@ -81,16 +81,20 @@ namespace OpenSim.Region.Framework.Scenes } /// - /// Add an inventory item to a prim in this group. + /// Add an inventory item from a user's inventory to a prim in this scene object. /// - /// - /// - /// + /// The client adding the item. + /// The local ID of the part receiving the add. + /// The user inventory item being added. /// The item UUID that should be used by the new item. /// public bool AddInventoryItem(IClientAPI remoteClient, uint localID, InventoryItemBase item, UUID copyItemID) { +// m_log.DebugFormat( +// "[PRIM INVENTORY]: Adding inventory item {0} from {1} to part with local ID {2}", +// item.Name, remoteClient.Name, localID); + UUID newItemId = (copyItemID != UUID.Zero) ? copyItemID : item.ID; SceneObjectPart part = GetChildPart(localID); @@ -132,15 +136,20 @@ namespace OpenSim.Region.Framework.Scenes taskItem.GroupPermissions = item.GroupPermissions; taskItem.NextPermissions = item.NextPermissions; } - + taskItem.Flags = item.Flags; + +// m_log.DebugFormat( +// "[PRIM INVENTORY]: Flags are 0x{0:X} for item {1} added to part {2} by {3}", +// taskItem.Flags, taskItem.Name, localID, remoteClient.Name); + // TODO: These are pending addition of those fields to TaskInventoryItem // taskItem.SalePrice = item.SalePrice; // taskItem.SaleType = item.SaleType; taskItem.CreationDate = (uint)item.CreationDate; bool addFromAllowedDrop = false; - if (remoteClient!=null) + if (remoteClient != null) { addFromAllowedDrop = remoteClient.AgentId != part.OwnerID; } From 60685c35179c74c5714abdb0c17611fb59d52c10 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 21 Apr 2011 19:17:38 +0100 Subject: [PATCH 101/103] Adjust freeswitch logging to be somewhat less noisy. However, there is still quite a large amount of logging present for debug purposes. --- .../FreeSwitchVoice/FreeSwitchVoiceModule.cs | 27 ++++++++++++------- .../FreeswitchService/FreeswitchService.cs | 8 +++--- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs index 373ffeb00b..42efd67d53 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs @@ -391,7 +391,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice string r = LLSDHelpers.SerialiseLLSDReply(voiceAccountResponse); - m_log.DebugFormat("[FreeSwitchVoice][PROVISIONVOICE]: avatar \"{0}\": {1}", avatarName, r); +// m_log.DebugFormat("[FreeSwitchVoice][PROVISIONVOICE]: avatar \"{0}\": {1}", avatarName, r); return r; } @@ -458,8 +458,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice if ((land.Flags & (uint)ParcelFlags.AllowVoiceChat) == 0) { - m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": voice not enabled for parcel", - scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName); +// m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": voice not enabled for parcel", +// scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName); channelUri = String.Empty; } else @@ -474,8 +474,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice parcelVoiceInfo = new LLSDParcelVoiceInfoResponse(scene.RegionInfo.RegionName, land.LocalID, creds); string r = LLSDHelpers.SerialiseLLSDReply(parcelVoiceInfo); - m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": {4}", - scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName, r); +// m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": {4}", +// scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName, r); return r; } catch (Exception e) @@ -850,16 +850,25 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice Hashtable requestBody = ParseRequestBody((string)request["body"]); - string section = (string) requestBody["section"]; - - m_log.DebugFormat("[FreeSwitchVoice]: Received request for config section {0}", section); + string section = (string) requestBody["section"]; if (section == "directory") + { + string eventCallingFunction = (string)requestBody["Event-Calling-Function"]; + m_log.DebugFormat( + "[FreeSwitchVoice]: Received request for config section directory, event calling function '{0}'", + eventCallingFunction); + response = m_FreeswitchService.HandleDirectoryRequest(requestBody); + } else if (section == "dialplan") + { + m_log.DebugFormat("[FreeSwitchVoice]: Received request for config section dialplan"); + response = m_FreeswitchService.HandleDialplanRequest(requestBody); + } else - m_log.WarnFormat("[FreeSwitchVoice]: Unknown section {0} was requested.", section); + m_log.WarnFormat("[FreeSwitchVoice]: Unknown section {0} was requested from config.", section); return response; } diff --git a/OpenSim/Services/FreeswitchService/FreeswitchService.cs b/OpenSim/Services/FreeswitchService/FreeswitchService.cs index 1ec89da5f7..c3f10560d7 100644 --- a/OpenSim/Services/FreeswitchService/FreeswitchService.cs +++ b/OpenSim/Services/FreeswitchService/FreeswitchService.cs @@ -56,7 +56,7 @@ namespace OpenSim.Services.FreeswitchService foreach (DictionaryEntry item in request) { - m_log.InfoFormat("[FreeSwitchDirectory]: requestBody item {0} {1}",item.Key, item.Value); +// m_log.InfoFormat("[FreeSwitchDirectory]: requestBody item {0} {1}",item.Key, item.Value); } string requestcontext = (string) request["Hunt-Context"]; @@ -146,10 +146,8 @@ namespace OpenSim.Services.FreeswitchService //domain=9.20.151.43 //ip=9.167.220.137 // this is the correct IP rather than sip_contact_host above when through a vpn or NAT setup - foreach (DictionaryEntry item in request) - { - m_log.DebugFormat("[FreeSwitchDirectory]: requestBody item {0} {1}", item.Key, item.Value); - } +// foreach (DictionaryEntry item in request) +// m_log.DebugFormat("[FreeSwitchDirectory]: requestBody item {0} {1}", item.Key, item.Value); string eventCallingFunction = (string) request["Event-Calling-Function"]; if (eventCallingFunction == null) From 223c90c7510e9dcf0bbf15535dbd6e9e9d2cfa96 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 21 Apr 2011 19:31:37 +0100 Subject: [PATCH 102/103] Remove duplicated freeswitch settings. --- bin/Robust.HG.ini.example | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index ced2857331..e2e9624831 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -117,10 +117,6 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 ; EchoPort = 50505 ; AttemptSTUN = false - LocalServiceModule = "OpenSim.Services.FreeswitchService.dll:FreeswitchService" - ;; IP of your FS server - ; ServerAddress = 127.0.0.1 - ; * This is the new style authentication service. Currently, only MySQL ; * is implemented. ; * From d9055c8dc3f5a768b300aa72318c813bbc024e09 Mon Sep 17 00:00:00 2001 From: BlueWall Date: Thu, 21 Apr 2011 15:12:52 -0400 Subject: [PATCH 103/103] Group collada meshies settings under [Mesh] in OpensimDefaults.ini --- OpenSim/Region/Physics/Meshing/Meshmerizer.cs | 3 ++- bin/OpenSimDefaults.ini | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs index 211a0a787f..64774d8ee5 100644 --- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs +++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs @@ -84,10 +84,11 @@ namespace OpenSim.Region.Physics.Meshing public Meshmerizer(IConfigSource config) { IConfig start_config = config.Configs["Startup"]; + IConfig mesh_config = config.Configs["Mesh"]; decodedSculptMapPath = start_config.GetString("DecodedSculptMapPath","j2kDecodeCache"); cacheSculptMaps = start_config.GetBoolean("CacheSculptMaps", cacheSculptMaps); - useMeshiesPhysicsMesh = start_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh); + useMeshiesPhysicsMesh = mesh_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh); try { diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index e72e85103f..b134b7ee36 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -149,12 +149,6 @@ ; to false if you have compatibility problems. ;CacheSculptMaps = true - ; if you use Meshmerizer and want collisions for meshies, setting this to true - ; will cause OpenSim to attempt to decode meshies assets, extract the physics - ; mesh, and use it for collisions. This is currently experimental code and enabling - ; it may cause unexpected physics problems. - ;UseMeshiesPhysicsMesh = false - ; Choose one of the physics engines below ; OpenDynamicsEngine is by some distance the most developed physics engine ; basicphysics effectively does not model physics at all, making all objects phantom @@ -461,6 +455,12 @@ ; default is true ; ColladaMesh = true + ; if you use Meshmerizer and want collisions for meshies, setting this to true + ; will cause OpenSim to attempt to decode meshies assets, extract the physics + ; mesh, and use it for collisions. This is currently experimental code and enabling + ; it may cause unexpected physics problems. + ;UseMeshiesPhysicsMesh = false + [ODEPhysicsSettings] ;##