From e77f91311678154390d09b586fbd8162cd3bb601 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 16 Nov 2016 22:34:56 +0000 Subject: [PATCH 01/16] minor: dont let rcvd agentupdates time jump back --- OpenSim/Framework/AgentUpdateArgs.cs | 1 + .../ClientStack/Linden/UDP/LLClientView.cs | 7 +++- .../Region/Framework/Scenes/ScenePresence.cs | 38 +++++++++++++------ 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/OpenSim/Framework/AgentUpdateArgs.cs b/OpenSim/Framework/AgentUpdateArgs.cs index eaa7902d8d..f04d692092 100644 --- a/OpenSim/Framework/AgentUpdateArgs.cs +++ b/OpenSim/Framework/AgentUpdateArgs.cs @@ -82,6 +82,7 @@ namespace OpenSim.Framework public Vector3 ClientAgentPosition; public bool UseClientAgentPosition; public bool NeedsCameraCollision; + public uint lastpacketSequence; public AgentUpdateArgs() { diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 46c6a19e9d..8d07bae155 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -6251,9 +6251,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP return false; } + uint seq = packet.Header.Sequence; + TotalAgentUpdates++; // dont let ignored updates pollute this throttles - if(SceneAgent == null || SceneAgent.IsChildAgent || SceneAgent.IsInTransit) + if(SceneAgent == null || SceneAgent.IsChildAgent || + SceneAgent.IsInTransit || seq <= m_thisAgentUpdateArgs.lastpacketSequence ) { // throttle reset is done at MoveAgentIntoRegion() // called by scenepresence on completemovement @@ -6261,6 +6264,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP return true; } + m_thisAgentUpdateArgs.lastpacketSequence = seq; + bool movement = CheckAgentMovementUpdateSignificance(x); bool camera = CheckAgentCameraUpdateSignificance(x); diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 3378eade7c..f73d54e249 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -2318,7 +2318,7 @@ namespace OpenSim.Region.Framework.Scenes Vector3 tocam = CameraPosition - posAdjusted; float distTocamlen = tocam.LengthSquared(); - if (distTocamlen > 0.08f && distTocamlen < 400) + if (distTocamlen > 0.01f && distTocamlen < 400) { distTocamlen = (float)Math.Sqrt(distTocamlen); tocam *= (1.0f / distTocamlen); @@ -4378,16 +4378,12 @@ namespace OpenSim.Region.Framework.Scenes m_log.DebugFormat( "[SCENE PRESENCE]: Closing child agents. Checking {0} regions in {1}", knownRegions.Count, Scene.RegionInfo.RegionName); - //DumpKnownRegions(); Util.RegionHandleToRegionLoc(newRegionHandle, out newRegionX, out newRegionY); uint x, y; spRegionSizeInfo regInfo; - // this should not be here - IEventQueue eventQueue = Scene.RequestModuleInterface(); - foreach (ulong handle in knownRegions) { // Don't close the agent on this region yet @@ -4400,16 +4396,10 @@ namespace OpenSim.Region.Framework.Scenes Util.RegionHandleToRegionLoc(handle, out x, out y); if (m_knownChildRegionsSizeInfo.TryGetValue(handle, out regInfo)) { - - // m_log.Debug("---> x: " + x + "; newx:" + newRegionX + "; Abs:" + (int)Math.Abs((int)(x - newRegionX))); - // m_log.Debug("---> y: " + y + "; newy:" + newRegionY + "; Abs:" + (int)Math.Abs((int)(y - newRegionY))); if (Util.IsOutsideView(RegionViewDistance, x, newRegionX, y, newRegionY, regInfo.sizeX, regInfo.sizeY, newRegionSizeX, newRegionSizeY)) { byebyeRegions.Add(handle); - // this should not be here -// if(eventQueue != null) -/// eventQueue.DisableSimulator(handle,UUID); } } else @@ -4445,6 +4435,32 @@ namespace OpenSim.Region.Framework.Scenes } } + public void closeAllChildAgents() + { + List byebyeRegions = new List(); + List knownRegions = KnownRegionHandles; + foreach (ulong handle in knownRegions) + { + if (handle != Scene.RegionInfo.RegionHandle) + { + byebyeRegions.Add(handle); + RemoveNeighbourRegion(handle); + Scene.CapsModule.DropChildSeed(UUID, handle); + } + } + + if (byebyeRegions.Count > 0) + { + m_log.Debug("[SCENE PRESENCE]: Closing " + byebyeRegions.Count + " child agents"); + + AgentCircuitData acd = Scene.AuthenticateHandler.GetAgentCircuitData(UUID); + string auth = string.Empty; + if (acd != null) + auth = acd.SessionID.ToString(); + m_scene.SceneGridService.SendCloseChildAgentConnections(ControllingClient.AgentId, auth, byebyeRegions); + } + } + #endregion /// From 5645abf69c3a6951f87ff75854e0daefbe625e01 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 17 Nov 2016 00:13:21 +0000 Subject: [PATCH 02/16] fix a debug message --- OpenSim/Server/Handlers/Grid/GridServerPostHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Server/Handlers/Grid/GridServerPostHandler.cs b/OpenSim/Server/Handlers/Grid/GridServerPostHandler.cs index 86fda36e81..38629b29d7 100644 --- a/OpenSim/Server/Handlers/Grid/GridServerPostHandler.cs +++ b/OpenSim/Server/Handlers/Grid/GridServerPostHandler.cs @@ -579,13 +579,13 @@ namespace OpenSim.Server.Handlers.Grid if (request.ContainsKey("SCOPEID")) UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); else - m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get neighbours"); + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get RegionFlags"); UUID regionID = UUID.Zero; if (request.ContainsKey("REGIONID")) UUID.TryParse(request["REGIONID"].ToString(), out regionID); else - m_log.WarnFormat("[GRID HANDLER]: no regionID in request to get neighbours"); + m_log.WarnFormat("[GRID HANDLER]: no regionID in request to get RegionFlags"); int flags = m_GridService.GetRegionFlags(scopeID, regionID); // m_log.DebugFormat("[GRID HANDLER]: flags for region {0}: {1}", regionID, flags); From 3d78388e55b056b92d97471ee79cfda87f710950 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 17 Nov 2016 01:44:47 +0000 Subject: [PATCH 03/16] restore large useracconts expire time for testing --- .../ServiceConnectorsOut/UserAccounts/UserAccountCache.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs index 6c1cc525b2..6d2efc6fa0 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs @@ -36,8 +36,10 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts { public class UserAccountCache : IUserAccountCacheModule { - private const double CACHE_EXPIRATION_SECONDS = 3600.0; // 1 hour! - private const double CACHE_NULL_EXPIRATION_SECONDS = 600; // 10minutes +// private const double CACHE_EXPIRATION_SECONDS = 3600.0; // 1 hour! +// private const double CACHE_NULL_EXPIRATION_SECONDS = 600; // 10minutes + private const double CACHE_EXPIRATION_SECONDS = 120000.0; + private const double CACHE_NULL_EXPIRATION_SECONDS = 120000.0; // private static readonly ILog m_log = // LogManager.GetLogger( From 2db22bf064f14afcbfecacfb8006da5c44a6040c Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 17 Nov 2016 13:57:45 +0000 Subject: [PATCH 04/16] minor changes to ubMeshmerizer memory use --- .../PhysicsModules/ubOdeMeshing/Mesh.cs | 129 +++++++++--------- .../ubOdeMeshing/Meshmerizer.cs | 4 +- 2 files changed, 66 insertions(+), 67 deletions(-) diff --git a/OpenSim/Region/PhysicsModules/ubOdeMeshing/Mesh.cs b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Mesh.cs index d9ea4a4991..0cdaa60c15 100644 --- a/OpenSim/Region/PhysicsModules/ubOdeMeshing/Mesh.cs +++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Mesh.cs @@ -39,6 +39,24 @@ namespace OpenSim.Region.PhysicsModule.ubODEMeshing { public class MeshBuildingData { + private class vertexcomp : IEqualityComparer + { + public bool Equals(Vertex v1, Vertex v2) + { + if (v1.X == v2.X && v1.Y == v2.Y && v1.Z == v2.Z) + return true; + else + return false; + } + public int GetHashCode(Vertex v) + { + int a = v.X.GetHashCode(); + int b = v.Y.GetHashCode(); + int c = v.Z.GetHashCode(); + return (a << 16) ^ (b << 8) ^ c; + } + } + public Dictionary m_vertices; public List m_triangles; public float m_obbXmin; @@ -49,6 +67,21 @@ namespace OpenSim.Region.PhysicsModule.ubODEMeshing public float m_obbZmax; public Vector3 m_centroid; public int m_centroidDiv; + + public MeshBuildingData() + { + vertexcomp vcomp = new vertexcomp(); + m_vertices = new Dictionary(vcomp); + m_triangles = new List(); + m_centroid = Vector3.Zero; + m_centroidDiv = 0; + m_obbXmin = float.MaxValue; + m_obbXmax = float.MinValue; + m_obbYmin = float.MaxValue; + m_obbYmax = float.MinValue; + m_obbZmin = float.MaxValue; + m_obbZmax = float.MinValue; + } } [Serializable()] @@ -76,50 +109,20 @@ namespace OpenSim.Region.PhysicsModule.ubODEMeshing public int RefCount { get; set; } public AMeshKey Key { get; set; } - private class vertexcomp : IEqualityComparer + public Mesh(bool forbuild) { - public bool Equals(Vertex v1, Vertex v2) - { - if (v1.X == v2.X && v1.Y == v2.Y && v1.Z == v2.Z) - return true; - else - return false; - } - public int GetHashCode(Vertex v) - { - int a = v.X.GetHashCode(); - int b = v.Y.GetHashCode(); - int c = v.Z.GetHashCode(); - return (a << 16) ^ (b << 8) ^ c; - } - } - - public Mesh() - { - vertexcomp vcomp = new vertexcomp(); - - m_bdata = new MeshBuildingData(); - m_bdata.m_vertices = new Dictionary(vcomp); - m_bdata.m_triangles = new List(); - m_bdata.m_centroid = Vector3.Zero; - m_bdata.m_centroidDiv = 0; - m_bdata.m_obbXmin = float.MaxValue; - m_bdata.m_obbXmax = float.MinValue; - m_bdata.m_obbYmin = float.MaxValue; - m_bdata.m_obbYmax = float.MinValue; - m_bdata.m_obbZmin = float.MaxValue; - m_bdata.m_obbZmax = float.MinValue; + if(forbuild) + m_bdata = new MeshBuildingData(); m_obb = new Vector3(0.5f, 0.5f, 0.5f); m_obboffset = Vector3.Zero; } - public Mesh Scale(Vector3 scale) { if (m_verticesPtr == null || m_indicesPtr == null) return null; - Mesh result = new Mesh(); + Mesh result = new Mesh(false); float x = scale.X; float y = scale.Y; @@ -167,7 +170,7 @@ namespace OpenSim.Region.PhysicsModule.ubODEMeshing public Mesh Clone() { - Mesh result = new Mesh(); + Mesh result = new Mesh(false); if (m_bdata != null) { @@ -514,8 +517,6 @@ namespace OpenSim.Region.PhysicsModule.ubODEMeshing if (m_indicesPtr == IntPtr.Zero) indexes = getIndexListAsInt(); - pinMemory(); - float x, y, z; if (m_bdata.m_centroidDiv > 0) @@ -543,55 +544,53 @@ namespace OpenSim.Region.PhysicsModule.ubODEMeshing m_obb = new Vector3(x, y, z); releaseBuildingMeshData(); + pinMemory(); } + public bool ToStream(Stream st) { if (m_indicesPtr == IntPtr.Zero || m_verticesPtr == IntPtr.Zero) return false; - BinaryWriter bw = new BinaryWriter(st); bool ok = true; try { + using(BinaryWriter bw = new BinaryWriter(st)) + { + bw.Write(m_vertexCount); + bw.Write(m_indexCount); - bw.Write(m_vertexCount); - bw.Write(m_indexCount); - - for (int i = 0; i < 3 * m_vertexCount; i++) - bw.Write(vertices[i]); - for (int i = 0; i < m_indexCount; i++) - bw.Write(indexes[i]); - bw.Write(m_obb.X); - bw.Write(m_obb.Y); - bw.Write(m_obb.Z); - bw.Write(m_obboffset.X); - bw.Write(m_obboffset.Y); - bw.Write(m_obboffset.Z); + for (int i = 0; i < 3 * m_vertexCount; i++) + bw.Write(vertices[i]); + for (int i = 0; i < m_indexCount; i++) + bw.Write(indexes[i]); + bw.Write(m_obb.X); + bw.Write(m_obb.Y); + bw.Write(m_obb.Z); + bw.Write(m_obboffset.X); + bw.Write(m_obboffset.Y); + bw.Write(m_obboffset.Z); + bw.Flush(); + bw.Close(); + } } catch { ok = false; } - if (bw != null) - { - bw.Flush(); - bw.Close(); - } - return ok; } public static Mesh FromStream(Stream st, AMeshKey key) { - Mesh mesh = new Mesh(); - mesh.releaseBuildingMeshData(); + Mesh mesh = new Mesh(false); bool ok = true; - using(BinaryReader br = new BinaryReader(st)) + try { - try + using(BinaryReader br = new BinaryReader(st)) { mesh.m_vertexCount = br.ReadInt32(); mesh.m_indexCount = br.ReadInt32(); @@ -613,10 +612,10 @@ namespace OpenSim.Region.PhysicsModule.ubODEMeshing mesh.m_obboffset.Y = br.ReadSingle(); mesh.m_obboffset.Z = br.ReadSingle(); } - catch - { - ok = false; - } + } + catch + { + ok = false; } if (ok) diff --git a/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs index ca94034b10..98c2e71caf 100644 --- a/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs +++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs @@ -182,7 +182,7 @@ namespace OpenSim.Region.PhysicsModule.ubODEMeshing /// private static Mesh CreateSimpleBoxMesh(float minX, float maxX, float minY, float maxY, float minZ, float maxZ) { - Mesh box = new Mesh(); + Mesh box = new Mesh(true); List vertices = new List(); // bottom @@ -357,7 +357,7 @@ namespace OpenSim.Region.PhysicsModule.ubODEMeshing int numCoords = coords.Count; int numFaces = faces.Count; - Mesh mesh = new Mesh(); + Mesh mesh = new Mesh(true); // Add the corresponding triangles to the mesh for (int i = 0; i < numFaces; i++) { From 8599a9a1cae6dc7a0760448b4ac8e9a773348c1e Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 17 Nov 2016 17:37:22 +0000 Subject: [PATCH 05/16] add a missing dispose --- OpenSim/Framework/TaskInventoryDictionary.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/OpenSim/Framework/TaskInventoryDictionary.cs b/OpenSim/Framework/TaskInventoryDictionary.cs index 2c20ef7719..c270d98ee7 100644 --- a/OpenSim/Framework/TaskInventoryDictionary.cs +++ b/OpenSim/Framework/TaskInventoryDictionary.cs @@ -64,6 +64,13 @@ namespace OpenSim.Framework /// private volatile System.Threading.ReaderWriterLockSlim m_itemLock = new System.Threading.ReaderWriterLockSlim(); + + ~TaskInventoryDictionary() + { + m_itemLock.Dispose(); + m_itemLock = null; + } + /// /// Are we readlocked by the calling thread? /// From 35b37510fca537d12c2eabc68be51e370dc6d6ce Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 17 Nov 2016 19:15:28 +0000 Subject: [PATCH 06/16] explicitly remove some references, and other useless changes --- OpenSim/Framework/WebUtil.cs | 8 +- .../ClientStack/Linden/UDP/LLClientView.cs | 160 +++++++++--------- .../ClientStack/Linden/UDP/LLUDPClient.cs | 22 ++- .../Linden/UDP/UnackedPacketCollection.cs | 9 + .../Region/Framework/Scenes/ScenePresence.cs | 24 ++- 5 files changed, 126 insertions(+), 97 deletions(-) diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs index 51d87bdf2d..2bbf785661 100644 --- a/OpenSim/Framework/WebUtil.cs +++ b/OpenSim/Framework/WebUtil.cs @@ -1062,11 +1062,10 @@ namespace OpenSim.Framework if (WebUtil.DebugLevel >= 5) WebUtil.LogOutgoingDetail("SEND", reqnum, System.Text.Encoding.UTF8.GetString(data)); - Stream requestStream = null; try { - requestStream = request.GetRequestStream(); - requestStream.Write(data, 0, length); + using(Stream requestStream = request.GetRequestStream()) + requestStream.Write(data,0,length); } catch (Exception e) { @@ -1076,9 +1075,6 @@ namespace OpenSim.Framework } finally { - if (requestStream != null) - requestStream.Dispose(); - // capture how much time was spent writing tickdata = Util.EnvironmentTickCountSubtract(tickstart); } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 8d07bae155..37937123b4 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -346,12 +346,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP private bool m_VelocityInterpolate = false; private const uint MaxTransferBytesPerPacket = 600; - /// - /// List used in construction of data blocks for an object update packet. This is to stop us having to - /// continually recreate it. - /// - protected List m_fullUpdateDataBlocksBuilder; - /// /// Maintain a record of all the objects killed. This allows us to stop an update being sent from the /// thread servicing the m_primFullUpdates queue after a kill. If this happens the object persists as an @@ -511,7 +505,6 @@ 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 List(); // m_attachmentsSent = new HashSet(); @@ -594,13 +587,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(disable, ThrottleOutPacketType.Unknown); } - // Shutdown the image manager - ImageManager.Close(); // Fire the callback for this connection closing if (OnConnectionClosed != null) OnConnectionClosed(this); + // Flush all of the packets out of the UDP server for this client if (m_udpServer != null) m_udpServer.Flush(m_udpClient); @@ -615,8 +607,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Disable UDP handling for this client m_udpClient.Shutdown(); - - + + m_udpClient.OnQueueEmpty -= HandleQueueEmpty; + m_udpClient.HasUpdates -= HandleHasUpdates; + m_udpClient.OnPacketStats -= PopulateStats; + + // Shutdown the image manager + ImageManager.Close(); + ImageManager = null; + + m_entityUpdates = null; + m_entityProps = null; + m_killRecord.Clear(); + GroupsInView.Clear(); + m_scene = null; //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false)); //GC.Collect(); //m_log.InfoFormat("[CLIENTVIEW] Memory post GC {0}", System.GC.GetTotalMemory(true)); @@ -814,7 +818,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void ProcessSpecificPacketAsync(object state) { AsyncPacketProcess packetObject = (AsyncPacketProcess)state; - + try { packetObject.result = packetObject.Method(packetObject.ClientView, packetObject.Pack); @@ -4095,19 +4099,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP ResendPrimUpdate(update); } + private List objectUpdateBlocks = new List(); + private List compressedUpdateBlocks = new List(); + private List terseUpdateBlocks = new List(); + private List terseAgentUpdateBlocks = new List(); + private void ProcessEntityUpdates(int maxUpdatesBytes) { - OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); - OpenSim.Framework.Lazy> compressedUpdateBlocks = new OpenSim.Framework.Lazy>(); - OpenSim.Framework.Lazy> terseUpdateBlocks = new OpenSim.Framework.Lazy>(); - OpenSim.Framework.Lazy> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy>(); - OpenSim.Framework.Lazy> objectUpdates = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> compressedUpdates = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseUpdates = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseAgentUpdates = new OpenSim.Framework.Lazy>(); - // Check to see if this is a flush if (maxUpdatesBytes <= 0) { @@ -4328,7 +4331,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP ablock = CreateAvatarUpdateBlock((ScenePresence)update.Entity); else ablock = CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId); - objectUpdateBlocks.Value.Add(ablock); + objectUpdateBlocks.Add(ablock); objectUpdates.Value.Add(update); maxUpdatesBytes -= ablock.Length; @@ -4337,7 +4340,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { ObjectUpdateCompressedPacket.ObjectDataBlock ablock = CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags); - compressedUpdateBlocks.Value.Add(ablock); + compressedUpdateBlocks.Add(ablock); compressedUpdates.Value.Add(update); maxUpdatesBytes -= ablock.Length; } @@ -4348,14 +4351,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // ALL presence updates go into a special list ablock = CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)); - terseAgentUpdateBlocks.Value.Add(ablock); + terseAgentUpdateBlocks.Add(ablock); terseAgentUpdates.Value.Add(update); } else { // Everything else goes here ablock = CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)); - terseUpdateBlocks.Value.Add(ablock); + terseUpdateBlocks.Add(ablock); terseUpdates.Value.Add(update); } maxUpdatesBytes -= ablock.Length; @@ -4366,74 +4369,69 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Packet Sending -// const float TIME_DILATION = 1.0f; ushort timeDilation; -// if(updatesThisCall > 0) -// timeDilation = Utils.FloatToUInt16(avgTimeDilation/updatesThisCall, 0.0f, 1.0f); -// else -// timeDilation = ushort.MaxValue; // 1.0; timeDilation = Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f); - if (terseAgentUpdateBlocks.IsValueCreated) + if (terseAgentUpdateBlocks.Count > 0) { - List blocks = terseAgentUpdateBlocks.Value; - ImprovedTerseObjectUpdatePacket packet = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.TimeDilation = timeDilation; - packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; + packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[terseAgentUpdateBlocks.Count]; - for (int i = 0; i < blocks.Count; i++) - packet.ObjectData[i] = blocks[i]; + for (int i = 0; i < terseAgentUpdateBlocks.Count; i++) + packet.ObjectData[i] = terseAgentUpdateBlocks[i]; + + terseAgentUpdateBlocks.Clear(); OutPacket(packet, ThrottleOutPacketType.Unknown, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseAgentUpdates.Value, oPacket); }); } - if (objectUpdateBlocks.IsValueCreated) + if (objectUpdateBlocks.Count > 0) { - List blocks = objectUpdateBlocks.Value; - ObjectUpdatePacket packet = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.TimeDilation = timeDilation; - packet.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[blocks.Count]; + packet.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[objectUpdateBlocks.Count]; - for (int i = 0; i < blocks.Count; i++) - packet.ObjectData[i] = blocks[i]; + for (int i = 0; i < objectUpdateBlocks.Count; i++) + packet.ObjectData[i] = objectUpdateBlocks[i]; + + objectUpdateBlocks.Clear(); OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(objectUpdates.Value, oPacket); }); } - if (compressedUpdateBlocks.IsValueCreated) + if (compressedUpdateBlocks.Count > 0) { - List blocks = compressedUpdateBlocks.Value; - ObjectUpdateCompressedPacket packet = (ObjectUpdateCompressedPacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdateCompressed); packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.TimeDilation = timeDilation; - packet.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[blocks.Count]; + packet.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[compressedUpdateBlocks.Count]; - for (int i = 0; i < blocks.Count; i++) - packet.ObjectData[i] = blocks[i]; + for (int i = 0; i < compressedUpdateBlocks.Count; i++) + packet.ObjectData[i] = compressedUpdateBlocks[i]; + + compressedUpdateBlocks.Clear(); OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(compressedUpdates.Value, oPacket); }); } - if (terseUpdateBlocks.IsValueCreated) + if (terseUpdateBlocks.Count > 0) { - List blocks = terseUpdateBlocks.Value; - ImprovedTerseObjectUpdatePacket packet = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket( PacketType.ImprovedTerseObjectUpdate); packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.TimeDilation = timeDilation; - packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; + packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[terseUpdateBlocks.Count]; - for (int i = 0; i < blocks.Count; i++) - packet.ObjectData[i] = blocks[i]; + for (int i = 0; i < terseUpdateBlocks.Count; i++) + packet.ObjectData[i] = terseUpdateBlocks[i]; + + terseUpdateBlocks.Clear(); OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates.Value, oPacket); }); } @@ -4828,21 +4826,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false,true)); } + List objectFamilyBlocks = new + List(); + List objectPropertiesBlocks = + new List(); + List needPhysics = new List(); + private void ProcessEntityPropertyRequests(int maxUpdateBytes) { - OpenSim.Framework.Lazy> objectFamilyBlocks = - new OpenSim.Framework.Lazy>(); +// OpenSim.Framework.Lazy> familyUpdates = +// new OpenSim.Framework.Lazy>(); - OpenSim.Framework.Lazy> objectPropertiesBlocks = - new OpenSim.Framework.Lazy>(); - - OpenSim.Framework.Lazy> familyUpdates = - new OpenSim.Framework.Lazy>(); - - OpenSim.Framework.Lazy> propertyUpdates = - new OpenSim.Framework.Lazy>(); +// OpenSim.Framework.Lazy> propertyUpdates = +// new OpenSim.Framework.Lazy>(); - List needPhysics = new List(); EntityUpdate iupdate; Int32 timeinqueue; // this is just debugging code & can be dropped later @@ -4860,8 +4857,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP { SceneObjectPart sop = (SceneObjectPart)update.Entity; ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); - objectFamilyBlocks.Value.Add(objPropDB); - familyUpdates.Value.Add(update); + objectFamilyBlocks.Add(objPropDB); +// familyUpdates.Value.Add(update); maxUpdateBytes -= objPropDB.Length; } } @@ -4873,23 +4870,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP SceneObjectPart sop = (SceneObjectPart)update.Entity; needPhysics.Add(sop); ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); - objectPropertiesBlocks.Value.Add(objPropDB); - propertyUpdates.Value.Add(update); + objectPropertiesBlocks.Add(objPropDB); +// propertyUpdates.Value.Add(update); maxUpdateBytes -= objPropDB.Length; } } } - if (objectPropertiesBlocks.IsValueCreated) + if (objectPropertiesBlocks.Count > 0) { - List blocks = objectPropertiesBlocks.Value; - List updates = propertyUpdates.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.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[objectPropertiesBlocks.Count]; + for (int i = 0; i < objectPropertiesBlocks.Count; i++) + packet.ObjectData[i] = objectPropertiesBlocks[i]; + + objectPropertiesBlocks.Clear(); packet.Header.Zerocoded = true; // Pass in the delegate so that if this packet needs to be resent, we send the current properties @@ -4898,7 +4894,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP //OutPacket(packet, ThrottleOutPacketType.Task, true, // delegate(OutgoingPacket oPacket) // { - // ResendPropertyUpdates(updates, oPacket); + // ResendPropertyUpdates(propertyUpdates.Value, oPacket); // }); OutPacket(packet, ThrottleOutPacketType.Task, true); @@ -4909,23 +4905,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Int32 fpcnt = 0; // Int32 fbcnt = 0; - if (objectFamilyBlocks.IsValueCreated) - { - List blocks = objectFamilyBlocks.Value; - + if (objectFamilyBlocks.Count > 0) + { // one packet per object block... uggh... - for (int i = 0; i < blocks.Count; i++) + for (int i = 0; i < objectFamilyBlocks.Count; i++) { ObjectPropertiesFamilyPacket packet = (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); - packet.ObjectData = blocks[i]; + packet.ObjectData = objectFamilyBlocks[i]; packet.Header.Zerocoded = true; // Pass in the delegate so that if this packet needs to be resent, we send the current properties // of the object rather than the properties when the packet was created - List updates = new List(); - updates.Add(familyUpdates.Value[i]); +// List updates = new List(); +// updates.Add(familyUpdates.Value[i]); // HACK : Remove intelligent resending until it's fixed in core //OutPacket(packet, ThrottleOutPacketType.Task, true, // delegate(OutgoingPacket oPacket) @@ -4937,6 +4931,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // fpcnt++; // fbcnt++; } + objectFamilyBlocks.Clear(); } if(needPhysics.Count > 0) @@ -4962,6 +4957,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP eq.Enqueue(BuildEvent("ObjectPhysicsProperties", llsdBody),AgentId); } + needPhysics.Clear(); } // m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index d59b7614ed..e85cee2524 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs @@ -120,13 +120,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Circuit code that this client is connected on public readonly uint CircuitCode; /// Sequence numbers of packets we've received (for duplicate checking) - public readonly IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200); + public IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200); /// Packets we have sent that need to be ACKed by the client - public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); + public UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); /// ACKs that are queued up, waiting to be sent to the client - public readonly DoubleLocklessQueue PendingAcks = new DoubleLocklessQueue(); + public DoubleLocklessQueue PendingAcks = new DoubleLocklessQueue(); /// Current packet sequence number public int CurrentSequence; @@ -170,7 +170,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private double m_nextOnQueueEmpty = 0; /// Throttle bucket for this agent's connection - private readonly AdaptiveTokenBucket m_throttleClient; + private AdaptiveTokenBucket m_throttleClient; public AdaptiveTokenBucket FlowThrottle { get { return m_throttleClient; } @@ -179,10 +179,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Throttle buckets for each packet category private readonly TokenBucket[] m_throttleCategories; /// Outgoing queues for throttled packets - private readonly DoubleLocklessQueue[] m_packetOutboxes = new DoubleLocklessQueue[THROTTLE_CATEGORY_COUNT]; + private DoubleLocklessQueue[] m_packetOutboxes = new DoubleLocklessQueue[THROTTLE_CATEGORY_COUNT]; /// A container that can hold one packet for each outbox, used to store /// dequeued packets that are being held for throttling - private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; + private OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; /// A reference to the LLUDPServer that is managing this client private readonly LLUDPServer m_udpServer; @@ -288,14 +288,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) { m_packetOutboxes[i].Clear(); + m_throttleCategories[i] = null; m_nextPackets[i] = null; } // pull the throttle out of the scene throttle m_throttleClient.Parent.UnregisterRequest(m_throttleClient); + m_throttleClient = null; OnPacketStats = null; OnQueueEmpty = null; - } + PendingAcks.Clear(); + NeedAcks.Clear(); + NeedAcks = null; + PendingAcks = null; + m_nextPackets = null; + m_packetOutboxes = null; + } /// /// Gets information about this client connection diff --git a/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs index b546a99603..c9d5697b23 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs @@ -74,6 +74,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Holds information about pending removals private LocklessQueue m_pendingRemoves = new LocklessQueue(); + + public void Clear() + { + m_packets.Clear(); + m_pendingAdds = null; + m_pendingAcknowledgements = null; + m_pendingRemoves = null; + } + /// /// Add an unacked packet to the collection /// diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index f73d54e249..2cfdd946f9 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -1104,7 +1104,7 @@ namespace OpenSim.Region.Framework.Scenes AdjustKnownSeeds(); - RegisterToEvents(); + RegisterToClientEvents(); SetDirectionVectors(); Appearance = appearance; @@ -1171,7 +1171,7 @@ namespace OpenSim.Region.Framework.Scenes } } - public void RegisterToEvents() + public void RegisterToClientEvents() { ControllingClient.OnCompleteMovementToRegion += CompleteMovement; ControllingClient.OnAgentUpdate += HandleAgentUpdate; @@ -1189,6 +1189,22 @@ namespace OpenSim.Region.Framework.Scenes // ControllingClient.OnChildAgentStatus += new StatusChange(this.ChildStatusChange); // ControllingClient.OnStopMovement += new GenericCall2(this.StopMovement); } + + public void RemoveClientEvents() + { + ControllingClient.OnCompleteMovementToRegion -= CompleteMovement; + ControllingClient.OnAgentUpdate -= HandleAgentUpdate; + ControllingClient.OnAgentCameraUpdate -= HandleAgentCamerasUpdate; + ControllingClient.OnAgentRequestSit -= HandleAgentRequestSit; + ControllingClient.OnAgentSit -= HandleAgentSit; + ControllingClient.OnSetAlwaysRun -= HandleSetAlwaysRun; + ControllingClient.OnStartAnim -= HandleStartAnim; + ControllingClient.OnStopAnim -= HandleStopAnim; + ControllingClient.OnChangeAnim -= avnHandleChangeAnim; + ControllingClient.OnForceReleaseControls -= HandleForceReleaseControls; + ControllingClient.OnAutoPilotGo -= MoveToTarget; + ControllingClient.OnUpdateThrottles -= RaiseUpdateThrottles; + } private void SetDirectionVectors() { @@ -5016,12 +5032,16 @@ namespace OpenSim.Region.Framework.Scenes RemoveFromPhysicalScene(); m_scene.EventManager.OnRegionHeartbeatEnd -= RegionHeartbeatEnd; + RemoveClientEvents(); // if (Animator != null) // Animator.Close(); Animator = null; + scriptedcontrols.Clear(); + ControllingClient = null; LifecycleState = ScenePresenceState.Removed; + IsDeleted = true; } public void AddAttachment(SceneObjectGroup gobj) From e21ac8b3c40665449e8dc1bbc5647b93c1a59f6c Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 17 Nov 2016 20:59:13 +0000 Subject: [PATCH 07/16] counting issus safeguard --- OpenSim/Framework/LocklessQueue.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/OpenSim/Framework/LocklessQueue.cs b/OpenSim/Framework/LocklessQueue.cs index 9bd9baf321..7ccbba7b55 100644 --- a/OpenSim/Framework/LocklessQueue.cs +++ b/OpenSim/Framework/LocklessQueue.cs @@ -93,7 +93,10 @@ namespace OpenSim.Framework if (oldHead == oldTail) { if (oldHeadNext == null) + { + count = 0; return false; + } CAS(ref tail, oldTail, oldHeadNext); } @@ -118,8 +121,7 @@ namespace OpenSim.Framework { // ugly T item; - while(count > 0) - Dequeue(out item); + while(Dequeue(out item)); Init(); } From ba7904a3a80713f726d434fc82e9a80306372e2c Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 18 Nov 2016 00:12:09 +0000 Subject: [PATCH 08/16] a few more changes on potencial mem issues --- OpenSim/Capabilities/Caps.cs | 5 +++++ OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs | 5 +++++ .../Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs | 10 ++++++++-- ThirdParty/SmartThreadPool/WorkItemsQueue.cs | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/OpenSim/Capabilities/Caps.cs b/OpenSim/Capabilities/Caps.cs index 3efab8e7ec..5e89c388f4 100644 --- a/OpenSim/Capabilities/Caps.cs +++ b/OpenSim/Capabilities/Caps.cs @@ -141,6 +141,11 @@ namespace OpenSim.Framework.Capabilities m_capsActive.Reset(); } + ~Caps() + { + m_capsActive.Dispose(); + } + /// /// Register a handler. This allows modules to register handlers. /// diff --git a/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs b/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs index 9056548a51..4ff8cbabfa 100644 --- a/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs +++ b/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs @@ -55,6 +55,11 @@ namespace OpenSim.Framework Dictionary2 = new Dictionary(capacity); } + ~DoubleDictionaryThreadAbortSafe() + { + rwLock.Dispose(); + } + public void Add(TKey1 key1, TKey2 key2, TValue value) { bool gotLock = false; diff --git a/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs index 98c2e71caf..2ae0881e78 100644 --- a/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs +++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs @@ -1483,6 +1483,7 @@ namespace OpenSim.Region.PhysicsModule.ubODEMeshing lock (diskLock) { + Stream stream = null; try { if (!Directory.Exists(dir)) @@ -1490,8 +1491,8 @@ namespace OpenSim.Region.PhysicsModule.ubODEMeshing Directory.CreateDirectory(dir); } - using(Stream stream = File.Open(filename, FileMode.Create)) - ok = mesh.ToStream(stream); + stream = File.Open(filename, FileMode.Create); + ok = mesh.ToStream(stream); } catch (IOException e) { @@ -1500,6 +1501,11 @@ namespace OpenSim.Region.PhysicsModule.ubODEMeshing filename, e.Message, e.StackTrace); ok = false; } + finally + { + if(stream != null) + stream.Dispose(); + } if (!ok && File.Exists(filename)) { diff --git a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs index e0bc9168ec..019c0d34b2 100644 --- a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs +++ b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs @@ -625,6 +625,7 @@ namespace Amib.Threading.Internal if (!_isDisposed) { Cleanup(); + _headWaiterEntry.Close(); } _isDisposed = true; } From 0a8cf2ff08a958154fd8f9f73ad31b9aba00e8b9 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 18 Nov 2016 02:13:01 +0000 Subject: [PATCH 09/16] put back skip of first drip call --- OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs index 7b9661b80c..d4603f80a5 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs @@ -193,7 +193,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP Parent = parent; RequestedDripRate = dripRate; RequestedBurst = MaxBurst; - m_lastDrip = Util.GetTimeStampMS() + 50.0; + m_lastDrip = Util.GetTimeStampMS() + 100000.0; // skip first drip } #endregion Constructor From d8812ba2d1a82bf2e6c945544cb72a6412ef48ae Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 18 Nov 2016 03:00:59 +0000 Subject: [PATCH 10/16] revert to lower resolution clock on udpserver for testing --- OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index ffdb6398a9..b491110fd8 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -323,7 +323,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected int m_elapsedMSSinceLastStatReport = 0; /// Environment.TickCount of the last time the outgoing packet handler executed - protected double m_tickLastOutgoingPacketHandler; + protected int m_tickLastOutgoingPacketHandler; /// Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped protected int m_elapsedMSOutgoingPacketHandler; @@ -2073,13 +2073,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_sendPing = false; // Update elapsed time - double thisTick = Util.GetTimeStampMS(); - int deltaMS = (int)(thisTick - m_tickLastOutgoingPacketHandler); + int thisTick = Environment.TickCount & Int32.MaxValue; + if (m_tickLastOutgoingPacketHandler > thisTick) + m_elapsedMSOutgoingPacketHandler += ((Int32.MaxValue - m_tickLastOutgoingPacketHandler) + thisTick); + else + m_elapsedMSOutgoingPacketHandler += (thisTick - m_tickLastOutgoingPacketHandler); + m_tickLastOutgoingPacketHandler = thisTick; // update some 1ms resolution chained timers - - m_elapsedMSOutgoingPacketHandler += deltaMS; // Check for pending outgoing resends every 100ms if (m_elapsedMSOutgoingPacketHandler >= 100) From e281876ecdb5edff737556c7200b8a902e66d05e Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 18 Nov 2016 03:25:29 +0000 Subject: [PATCH 11/16] restore higher resolution clock on udpserver and lower uaeraccouts caching time --- .../ClientStack/Linden/UDP/LLUDPServer.cs | 18 +++++++----------- .../UserAccounts/UserAccountCache.cs | 6 ++---- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index b491110fd8..af33d17b7e 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -323,10 +323,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected int m_elapsedMSSinceLastStatReport = 0; /// Environment.TickCount of the last time the outgoing packet handler executed - protected int m_tickLastOutgoingPacketHandler; + protected double m_tickLastOutgoingPacketHandler; /// Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped - protected int m_elapsedMSOutgoingPacketHandler; + protected double m_elapsedMSOutgoingPacketHandler; /// Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed protected int m_elapsed100MSOutgoingPacketHandler; @@ -2073,21 +2073,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_sendPing = false; // Update elapsed time - int thisTick = Environment.TickCount & Int32.MaxValue; - if (m_tickLastOutgoingPacketHandler > thisTick) - m_elapsedMSOutgoingPacketHandler += ((Int32.MaxValue - m_tickLastOutgoingPacketHandler) + thisTick); - else - m_elapsedMSOutgoingPacketHandler += (thisTick - m_tickLastOutgoingPacketHandler); - - m_tickLastOutgoingPacketHandler = thisTick; + double thisTick = Util.GetTimeStampMS(); // update some 1ms resolution chained timers + m_elapsedMSOutgoingPacketHandler += thisTick - m_tickLastOutgoingPacketHandler; + m_tickLastOutgoingPacketHandler = thisTick; // Check for pending outgoing resends every 100ms - if (m_elapsedMSOutgoingPacketHandler >= 100) + if (m_elapsedMSOutgoingPacketHandler >= 100.0) { m_resendUnacked = true; - m_elapsedMSOutgoingPacketHandler = 0; + m_elapsedMSOutgoingPacketHandler = 0.0; m_elapsed100MSOutgoingPacketHandler += 1; } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs index 6d2efc6fa0..6c1cc525b2 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs @@ -36,10 +36,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts { public class UserAccountCache : IUserAccountCacheModule { -// private const double CACHE_EXPIRATION_SECONDS = 3600.0; // 1 hour! -// private const double CACHE_NULL_EXPIRATION_SECONDS = 600; // 10minutes - private const double CACHE_EXPIRATION_SECONDS = 120000.0; - private const double CACHE_NULL_EXPIRATION_SECONDS = 120000.0; + private const double CACHE_EXPIRATION_SECONDS = 3600.0; // 1 hour! + private const double CACHE_NULL_EXPIRATION_SECONDS = 600; // 10minutes // private static readonly ILog m_log = // LogManager.GetLogger( From 70eb37433d4cc7512ebe6cac1d7c288c9b1db06f Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 18 Nov 2016 03:40:34 +0000 Subject: [PATCH 12/16] avoid a null ref. (needs better way) --- OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 37937123b4..8ba26e83f9 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -4371,6 +4371,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP ushort timeDilation; + if(m_scene == null) + return; + timeDilation = Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f); if (terseAgentUpdateBlocks.Count > 0) @@ -4632,6 +4635,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) { + if(m_scene == null) + return; + if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) { int maxUpdateBytes = m_udpClient.GetCatBytesCanSend(ThrottleOutPacketType.Task, 30); From b43f36abf168a0cce7e71178e3d4766a717c7d60 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 18 Nov 2016 15:16:11 +0000 Subject: [PATCH 13/16] add expire time for aliens --- .../UserAccounts/UserAccountCache.cs | 33 +++++++++++++++---- .../Interfaces/IUserAccountCacheModule.cs | 2 ++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs index 6c1cc525b2..2afd74e36e 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs @@ -36,6 +36,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts { public class UserAccountCache : IUserAccountCacheModule { + private const double CACHE_ALIEN_EXPIRATION_SECONDS = 172800; // 48 hours private const double CACHE_EXPIRATION_SECONDS = 3600.0; // 1 hour! private const double CACHE_NULL_EXPIRATION_SECONDS = 600; // 10minutes @@ -60,21 +61,20 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts { if (account == null) m_UUIDCache.AddOrUpdate(userID, null, CACHE_NULL_EXPIRATION_SECONDS); - else + else if(account.LocalToGrid) { m_UUIDCache.AddOrUpdate(userID, account, CACHE_EXPIRATION_SECONDS); m_NameCache.AddOrUpdate(account.Name, account.PrincipalID, CACHE_EXPIRATION_SECONDS); } - + else + { + m_UUIDCache.AddOrUpdate(userID, account, CACHE_ALIEN_EXPIRATION_SECONDS); + m_NameCache.AddOrUpdate(account.Name, account.PrincipalID, CACHE_ALIEN_EXPIRATION_SECONDS); + } //m_log.DebugFormat("[USER CACHE]: cached user {0}", userID); } } - public void Invalidate(UUID userID) - { - lock(accessLock) - m_UUIDCache.Remove(userID); - } public UserAccount Get(UUID userID, out bool inCache) { @@ -114,6 +114,25 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts return null; } + public void Invalidate(UUID userID) + { + m_UUIDCache.Remove(userID); + } + + public void Remove(UUID id) + { + lock(accessLock) + { + if (!m_UUIDCache.Contains(id)) + return; + + UserAccount account = null; + if (m_UUIDCache.TryGetValue(id, out account) && account != null) + m_NameCache.Remove(account.Name); + m_UUIDCache.Remove(id); + } + } + public void Remove(string name) { lock(accessLock) diff --git a/OpenSim/Region/Framework/Interfaces/IUserAccountCacheModule.cs b/OpenSim/Region/Framework/Interfaces/IUserAccountCacheModule.cs index ed269896a9..027a7e2e5a 100644 --- a/OpenSim/Region/Framework/Interfaces/IUserAccountCacheModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IUserAccountCacheModule.cs @@ -26,8 +26,10 @@ */ using OpenSim.Region.Framework.Scenes; +using OpenMetaverse; public interface IUserAccountCacheModule { void Remove(string name); + void Remove(UUID id); } From 1396c466f65de8c1963282e7d59d3fb2acbbfe84 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 18 Nov 2016 17:30:15 +0000 Subject: [PATCH 14/16] search accout by id not volatil user name (HG) --- OpenSim/Region/Framework/Scenes/Scene.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index ca32940cd2..c349369604 100755 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -4141,7 +4141,8 @@ namespace OpenSim.Region.Framework.Scenes { IUserAccountCacheModule cache = RequestModuleInterface(); if (cache != null) - cache.Remove(acd.firstname + " " + acd.lastname); +// cache.Remove(acd.firstname + " " + acd.lastname); + cache.Remove(acd.AgentID); // Remove any preexisting circuit - we don't want duplicates // This is a stab at preventing avatar "ghosting" From 6749c61d4fc064a954807c9f24284542a2159cd2 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 11 Nov 2016 17:31:46 +0000 Subject: [PATCH 15/16] Fix the previous commit --- .../PasswordAuthenticationService.cs | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/OpenSim/Services/AuthenticationService/PasswordAuthenticationService.cs b/OpenSim/Services/AuthenticationService/PasswordAuthenticationService.cs index 2e8ffe5aae..a9359f3cd0 100644 --- a/OpenSim/Services/AuthenticationService/PasswordAuthenticationService.cs +++ b/OpenSim/Services/AuthenticationService/PasswordAuthenticationService.cs @@ -72,8 +72,11 @@ namespace OpenSim.Services.AuthenticationService { realID = UUID.Zero; - m_log.DebugFormat("[AUTH SERVICE]: Authenticating for {0}", principalID); + m_log.DebugFormat("[AUTH SERVICE]: Authenticating for {0}, user account service present: {1}", principalID, m_UserAccountService != null); AuthenticationData data = m_Database.Get(principalID); + UserAccount user = null; + if (m_UserAccountService != null) + user = m_UserAccountService.GetUserAccount(UUID.Zero, principalID); if (data == null || data.Data == null) { @@ -97,7 +100,53 @@ namespace OpenSim.Services.AuthenticationService return GetToken(principalID, lifetime); } - m_log.DebugFormat("[AUTH SERVICE]: Authenticating FAIL for {0} ", principalID); + if (user == null) + { + m_log.DebugFormat("[PASS AUTH]: No user record for {0}", principalID); + return String.Empty; + } + + int impersonateFlag = 1 << 6; + + if ((user.UserFlags & impersonateFlag) == 0) + return String.Empty; + + m_log.DebugFormat("[PASS AUTH]: Attempting impersonation"); + + List accounts = m_UserAccountService.GetUserAccountsWhere(UUID.Zero, "UserLevel >= 200"); + if (accounts == null || accounts.Count == 0) + return String.Empty; + + foreach (UserAccount a in accounts) + { + data = m_Database.Get(a.PrincipalID); + if (data == null || data.Data == null || + !data.Data.ContainsKey("passwordHash") || + !data.Data.ContainsKey("passwordSalt")) + { + continue; + } + +// m_log.DebugFormat("[PASS AUTH]: Trying {0}", data.PrincipalID); + + hashed = Util.Md5Hash(password + ":" + + data.Data["passwordSalt"].ToString()); + + if (data.Data["passwordHash"].ToString() == hashed) + { + m_log.DebugFormat("[PASS AUTH]: {0} {1} impersonating {2}, proceeding with login", a.FirstName, a.LastName, principalID); + realID = a.PrincipalID; + return GetToken(principalID, lifetime); + } +// else +// { +// m_log.DebugFormat( +// "[AUTH SERVICE]: Salted hash {0} of given password did not match salted hash of {1} for PrincipalID {2}. Authentication failure.", +// hashed, data.Data["passwordHash"], data.PrincipalID); +// } + } + + m_log.DebugFormat("[PASS AUTH]: Impersonation of {0} failed", principalID); return String.Empty; } } From 28f7d429fc5cc6a8e52766d06d6a261825d792c2 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Sat, 19 Nov 2016 02:27:31 +0000 Subject: [PATCH 16/16] REST console v2. This is an incompatible protocol change. It degrades gracefully. --- OpenSim/Framework/Console/RemoteConsole.cs | 317 +++++++++++++++++---- 1 file changed, 263 insertions(+), 54 deletions(-) diff --git a/OpenSim/Framework/Console/RemoteConsole.cs b/OpenSim/Framework/Console/RemoteConsole.cs index 8ad7b0daa9..9049b4b801 100644 --- a/OpenSim/Framework/Console/RemoteConsole.cs +++ b/OpenSim/Framework/Console/RemoteConsole.cs @@ -34,6 +34,7 @@ using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; +using System.Timers; using OpenMetaverse; using Nini.Config; using OpenSim.Framework.Servers.HttpServer; @@ -41,90 +42,232 @@ using log4net; namespace OpenSim.Framework.Console { - public class ConsoleConnection - { - public int last; - public long lastLineSeen; - public bool newConnection = true; - } - // A console that uses REST interfaces // public class RemoteConsole : CommandConsole { - private IHttpServer m_Server = null; - private IConfigSource m_Config = null; + // Connection specific data, indexed by a session ID + // we create when a client connects. + protected class ConsoleConnection + { + // Last activity from the client + public int last; - private List m_Scrollback = new List(); - private ManualResetEvent m_DataEvent = new ManualResetEvent(false); - private List m_InputData = new List(); - private long m_LineNumber = 0; - private Dictionary m_Connections = + // Last line of scrollback posted to this client + public long lastLineSeen; + + // True if this is a new connection, e.g. has never + // displayed a prompt to the user. + public bool newConnection = true; + } + + // A line in the scrollback buffer. + protected class ScrollbackEntry + { + // The line number of this entry + public long lineNumber; + + // The text to send to the client + public string text; + + // The level this should be logged as. Omitted for + // prompts and input echo. + public string level; + + // True if the text above is a prompt, e.g. the + // client should turn on the cursor / accept input + public bool isPrompt; + + // True if the requested input is a command. A + // client may offer help or validate input if + // this is set. If false, input should be sent + // as typed. + public bool isCommand; + + // True if this text represents a line of text that + // was input in response to a prompt. A client should + // turn off the cursor and refrain from sending commands + // until a new prompt is received. + public bool isInput; + } + + // Data that is relevant to all connections + + // The scrollback buffer + protected List m_Scrollback = new List(); + + // Monotonously incrementing line number. This may eventually + // wrap. No provision is made for that case because 64 bits + // is a long, long time. + protected long m_lineNumber = 0; + + // These two variables allow us to send the correct + // information about the prompt status to the client, + // irrespective of what may have run off the top of the + // scrollback buffer; + protected bool m_expectingInput = false; + protected bool m_expectingCommand = true; + protected string m_lastPromptUsed; + + // This is the list of things received from clients. + // Note: Race conditions can happen. If a client sends + // something while nothing is expected, it will be + // intepreted as input to the next prompt. For + // commands this is largely correct. For other prompts, + // YMMV. + // TODO: Find a better way to fix this + protected List m_InputData = new List(); + + // Event to allow ReadLine to wait synchronously even though + // everthing else is asynchronous here. + protected ManualResetEvent m_DataEvent = new ManualResetEvent(false); + + // The list of sessions we maintain. Unlike other console types, + // multiple users on the same console are explicitly allowed. + protected Dictionary m_Connections = new Dictionary(); - private string m_UserName = String.Empty; - private string m_Password = String.Empty; - private string m_AllowedOrigin = String.Empty; + + // Timer to control expiration of sessions that have been + // disconnected. + protected System.Timers.Timer m_expireTimer = new System.Timers.Timer(5000); + + // The less interesting stuff that makes the actual server + // work. + protected IHttpServer m_Server = null; + protected IConfigSource m_Config = null; + + protected string m_UserName = String.Empty; + protected string m_Password = String.Empty; + protected string m_AllowedOrigin = String.Empty; + public RemoteConsole(string defaultPrompt) : base(defaultPrompt) { + // There is something wrong with this architecture. + // A prompt is sent on every single input, so why have this? + // TODO: Investigate and fix. + m_lastPromptUsed = defaultPrompt; + + // Start expiration of sesssions. + m_expireTimer.Elapsed += DoExpire; + m_expireTimer.Start(); } public void ReadConfig(IConfigSource config) { m_Config = config; + // We're pulling this from the 'Network' section for legacy + // compatibility. However, this is so essentially insecure + // that TLS and client certs should be used instead of + // a username / password. IConfig netConfig = m_Config.Configs["Network"]; + if (netConfig == null) return; + // Get the username and password. m_UserName = netConfig.GetString("ConsoleUser", String.Empty); m_Password = netConfig.GetString("ConsolePass", String.Empty); + + // Woefully underdocumented, this is what makes javascript + // console clients work. Set to "*" for anywhere or (better) + // to specific addresses. m_AllowedOrigin = netConfig.GetString("ConsoleAllowedOrigin", String.Empty); } public void SetServer(IHttpServer server) { + // This is called by the framework to give us the server + // instance (means: port) to work with. m_Server = server; + // Add our handlers m_Server.AddHTTPHandler("/StartSession/", HandleHttpStartSession); m_Server.AddHTTPHandler("/CloseSession/", HandleHttpCloseSession); m_Server.AddHTTPHandler("/SessionCommand/", HandleHttpSessionCommand); } public override void Output(string text, string level) + { + Output(text, level, false, false, false); + } + + protected void Output(string text, string level, bool isPrompt, bool isCommand, bool isInput) { + // Increment the line number. It was 0 and they start at 1 + // so we need to pre-increment. + m_lineNumber++; + + // Create and populate the new entry. + ScrollbackEntry newEntry = new ScrollbackEntry(); + + newEntry.lineNumber = m_lineNumber; + newEntry.text = text; + newEntry.level = level; + newEntry.isPrompt = isPrompt; + newEntry.isCommand = isCommand; + newEntry.isInput = isInput; + + // Add a line to the scrollback. In some cases, that may not + // actually be a line of text. lock (m_Scrollback) { + // Prune the scrollback to the length se send as connect + // burst to give the user some context. while (m_Scrollback.Count >= 1000) m_Scrollback.RemoveAt(0); - m_LineNumber++; - m_Scrollback.Add(String.Format("{0}", m_LineNumber)+":"+level+":"+text); + + m_Scrollback.Add(newEntry); } + + // Let the rest of the system know we have output something. FireOnOutput(text.Trim()); + + // Also display it for debugging. System.Console.WriteLine(text.Trim()); } public override void Output(string text) { - Output(text, "normal"); + // Output plain (non-logging style) text. + Output(text, String.Empty, false, false, false); } public override string ReadLine(string p, bool isCommand, bool e) { - if (isCommand) - Output("+++"+p); - else - Output("-++"+p); + // Output the prompt an prepare to wait. This + // is called on a dedicated console thread and + // needs to be synchronous. Old architecture but + // not worth upgrading. + if (isCommand) + { + m_expectingInput = true; + m_expectingCommand = true; + Output(p, String.Empty, true, true, false); + m_lastPromptUsed = p; + } + else + { + m_expectingInput = true; + Output(p, String.Empty, true, false, false); + } + + // Here is where we wait for the user to input something. m_DataEvent.WaitOne(); string cmdinput; + // Check for empty input. Read input if not empty. lock (m_InputData) { if (m_InputData.Count == 0) { m_DataEvent.Reset(); + m_expectingInput = false; + m_expectingCommand = false; + return ""; } @@ -135,8 +278,19 @@ namespace OpenSim.Framework.Console } + m_expectingInput = false; + m_expectingCommand = false; + + // Echo to all the other users what we have done. This + // will also go to ourselves. + Output (cmdinput, String.Empty, false, false, true); + + // If this is a command, we need to resolve and execute it. if (isCommand) { + // This call will actually execute the command and create + // any output associated with it. The core just gets an + // empty string so it will call again immediately. string[] cmd = Commands.Resolve(Parser.Parse(cmdinput)); if (cmd.Length != 0) @@ -151,18 +305,23 @@ namespace OpenSim.Framework.Console return String.Empty; } } + + // Return the raw input string if not a command. return cmdinput; } - private Hashtable CheckOrigin(Hashtable result) + // Very simplistic static access control header. + protected Hashtable CheckOrigin(Hashtable result) { if (!string.IsNullOrEmpty(m_AllowedOrigin)) result["access_control_allow_origin"] = m_AllowedOrigin; + return result; } + /* TODO: Figure out how PollServiceHTTPHandler can access the request headers * in order to use m_AllowedOrigin as a regular expression - private Hashtable CheckOrigin(Hashtable headers, Hashtable result) + protected Hashtable CheckOrigin(Hashtable headers, Hashtable result) { if (!string.IsNullOrEmpty(m_AllowedOrigin)) { @@ -177,18 +336,23 @@ namespace OpenSim.Framework.Console } */ - private void DoExpire() + protected void DoExpire(Object sender, ElapsedEventArgs e) { + // Iterate the list of console connections and find those we + // haven't heard from for longer then the longpoll interval. + // Remove them. List expired = new List(); lock (m_Connections) { + // Mark the expired ones foreach (KeyValuePair kvp in m_Connections) { if (System.Environment.TickCount - kvp.Value.last > 500000) expired.Add(kvp.Key); } + // Delete them foreach (UUID id in expired) { m_Connections.Remove(id); @@ -197,10 +361,10 @@ namespace OpenSim.Framework.Console } } - private Hashtable HandleHttpStartSession(Hashtable request) + // Start a new session. + protected Hashtable HandleHttpStartSession(Hashtable request) { - DoExpire(); - + // The login is in the form of a http form post Hashtable post = DecodePostString(request["body"].ToString()); Hashtable reply = new Hashtable(); @@ -208,6 +372,7 @@ namespace OpenSim.Framework.Console reply["int_response_code"] = 401; reply["content_type"] = "text/plain"; + // Check user name and password if (m_UserName == String.Empty) return reply; @@ -220,22 +385,28 @@ namespace OpenSim.Framework.Console return reply; } + // Set up the new console connection record ConsoleConnection c = new ConsoleConnection(); c.last = System.Environment.TickCount; c.lastLineSeen = 0; + // Assign session ID UUID sessionID = UUID.Random(); + // Add connection to list. lock (m_Connections) { m_Connections[sessionID] = c; } + // This call is a CAP. The URL is the authentication. string uri = "/ReadResponses/" + sessionID.ToString() + "/"; m_Server.AddPollServiceHTTPHandler( uri, new PollServiceEventArgs(null, uri, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout + // Our reply is an XML document. + // TODO: Change this to Linq.Xml XmlDocument xmldoc = new XmlDocument(); XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, "", ""); @@ -252,12 +423,13 @@ namespace OpenSim.Framework.Console rootElement.AppendChild(id); XmlElement prompt = xmldoc.CreateElement("", "Prompt", ""); - prompt.AppendChild(xmldoc.CreateTextNode(DefaultPrompt)); + prompt.AppendChild(xmldoc.CreateTextNode(m_lastPromptUsed)); rootElement.AppendChild(prompt); rootElement.AppendChild(MainConsole.Instance.Commands.GetXml(xmldoc)); + // Set up the response and check origin reply["str_response_string"] = xmldoc.InnerXml; reply["int_response_code"] = 200; reply["content_type"] = "text/xml"; @@ -266,10 +438,9 @@ namespace OpenSim.Framework.Console return reply; } - private Hashtable HandleHttpCloseSession(Hashtable request) + // Client closes session. Clean up. + protected Hashtable HandleHttpCloseSession(Hashtable request) { - DoExpire(); - Hashtable post = DecodePostString(request["body"].ToString()); Hashtable reply = new Hashtable(); @@ -316,10 +487,9 @@ namespace OpenSim.Framework.Console return reply; } - private Hashtable HandleHttpSessionCommand(Hashtable request) + // Command received from the client. + protected Hashtable HandleHttpSessionCommand(Hashtable request) { - DoExpire(); - Hashtable post = DecodePostString(request["body"].ToString()); Hashtable reply = new Hashtable(); @@ -327,6 +497,7 @@ namespace OpenSim.Framework.Console reply["int_response_code"] = 404; reply["content_type"] = "text/plain"; + // Check the ID if (post["ID"] == null) return reply; @@ -334,21 +505,25 @@ namespace OpenSim.Framework.Console if (!UUID.TryParse(post["ID"].ToString(), out id)) return reply; + // Find the connection for that ID. lock (m_Connections) { if (!m_Connections.ContainsKey(id)) return reply; } + // Empty post. Just error out. if (post["COMMAND"] == null) return reply; + // Place the input data in the buffer. lock (m_InputData) { m_DataEvent.Set(); m_InputData.Add(post["COMMAND"].ToString()); } + // Create the XML reply document. XmlDocument xmldoc = new XmlDocument(); XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, "", ""); @@ -372,7 +547,8 @@ namespace OpenSim.Framework.Console return reply; } - private Hashtable DecodePostString(string data) + // Decode a HTTP form post to a Hashtable + protected Hashtable DecodePostString(string data) { Hashtable result = new Hashtable(); @@ -396,6 +572,7 @@ namespace OpenSim.Framework.Console return result; } + // Close the CAP receiver for the responses for a given client. public void CloseConnection(UUID id) { try @@ -409,7 +586,9 @@ namespace OpenSim.Framework.Console } } - private bool HasEvents(UUID RequestID, UUID sessionID) + // Check if there is anything to send. Return true if this client has + // lines pending. + protected bool HasEvents(UUID RequestID, UUID sessionID) { ConsoleConnection c = null; @@ -420,13 +599,15 @@ namespace OpenSim.Framework.Console c = m_Connections[sessionID]; } c.last = System.Environment.TickCount; - if (c.lastLineSeen < m_LineNumber) + if (c.lastLineSeen < m_lineNumber) return true; return false; } - private Hashtable GetEvents(UUID RequestID, UUID sessionID) + // Send all pending output to the client. + protected Hashtable GetEvents(UUID RequestID, UUID sessionID) { + // Find the connection that goes with this client. ConsoleConnection c = null; lock (m_Connections) @@ -435,12 +616,15 @@ namespace OpenSim.Framework.Console return NoEvents(RequestID, UUID.Zero); c = m_Connections[sessionID]; } + + // If we have nothing to send, send the no events response. c.last = System.Environment.TickCount; - if (c.lastLineSeen >= m_LineNumber) + if (c.lastLineSeen >= m_lineNumber) return NoEvents(RequestID, UUID.Zero); Hashtable result = new Hashtable(); + // Create the response document. XmlDocument xmldoc = new XmlDocument(); XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, "", ""); @@ -449,30 +633,53 @@ namespace OpenSim.Framework.Console XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", ""); - if (c.newConnection) - { - c.newConnection = false; - Output("+++" + DefaultPrompt); - } + //if (c.newConnection) + //{ + // c.newConnection = false; + // Output("+++" + DefaultPrompt); + //} lock (m_Scrollback) { - long startLine = m_LineNumber - m_Scrollback.Count; + long startLine = m_lineNumber - m_Scrollback.Count; long sendStart = startLine; if (sendStart < c.lastLineSeen) sendStart = c.lastLineSeen; - for (long i = sendStart ; i < m_LineNumber ; i++) + for (long i = sendStart ; i < m_lineNumber ; i++) { + ScrollbackEntry e = m_Scrollback[(int)(i - startLine)]; + XmlElement res = xmldoc.CreateElement("", "Line", ""); - long line = i + 1; - res.SetAttribute("Number", line.ToString()); - res.AppendChild(xmldoc.CreateTextNode(m_Scrollback[(int)(i - startLine)])); + res.SetAttribute("Number", e.lineNumber.ToString()); + res.SetAttribute("Level", e.level); + // Don't include these for the scrollback, we'll send the + // real state later. + if (!c.newConnection) + { + res.SetAttribute("Prompt", e.isPrompt ? "true" : "false"); + res.SetAttribute("Command", e.isCommand ? "true" : "false"); + res.SetAttribute("Input", e.isInput ? "true" : "false"); + } + else if (i == m_lineNumber - 1) // Last line for a new connection + { + res.SetAttribute("Prompt", m_expectingInput ? "true" : "false"); + res.SetAttribute("Command", m_expectingCommand ? "true" : "false"); + res.SetAttribute("Input", (!m_expectingInput) ? "true" : "false"); + } + else + { + res.SetAttribute("Input", e.isInput ? "true" : "false"); + } + + res.AppendChild(xmldoc.CreateTextNode(e.text)); rootElement.AppendChild(res); } } - c.lastLineSeen = m_LineNumber; + + c.lastLineSeen = m_lineNumber; + c.newConnection = false; xmldoc.AppendChild(rootElement); @@ -486,7 +693,9 @@ namespace OpenSim.Framework.Console return result; } - private Hashtable NoEvents(UUID RequestID, UUID id) + // This is really just a no-op. It generates what is sent + // to the client if the poll times out without any events. + protected Hashtable NoEvents(UUID RequestID, UUID id) { Hashtable result = new Hashtable();