From d44b50ee462978b4899c0b142f6ecbfb553f06b6 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 15 Oct 2009 15:25:02 -0700 Subject: [PATCH 01/61] * Removed some of the redundant broadcast functions in Scene and SceneGraph so it is clear who/what the broadcast is going to each time * Removed two redundant parameters from SceneObjectPart * Changed some code in terse update sending that was meant to work with references to work with value types (since Vector3 and Quaternion are structs) * Committing a preview of a new method for sending object updates efficiently (all commented out for now) --- .../ClientStack/LindenUDP/LLClientView.cs | 229 +++++++++++++++++- .../CoreModules/Avatar/Chat/ChatModule.cs | 11 +- .../World/Estate/EstateManagementModule.cs | 2 +- .../World/Land/LandManagementModule.cs | 6 +- OpenSim/Region/Framework/Scenes/Scene.cs | 31 +-- OpenSim/Region/Framework/Scenes/SceneGraph.cs | 17 -- .../Framework/Scenes/SceneObjectPart.cs | 12 +- .../Region/Framework/Scenes/ScenePresence.cs | 11 +- .../Scripting/Minimodule/MRMModule.cs | 4 +- 9 files changed, 255 insertions(+), 68 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 8487adc7f5..82a2cdd037 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -51,6 +51,44 @@ using Nini.Config; namespace OpenSim.Region.ClientStack.LindenUDP { + #region Enums + + /// + /// Specifies the fields that have been changed when sending a prim or + /// avatar update + /// + [Flags] + public enum PrimUpdateFlags : uint + { + None = 0, + AttachmentPoint = 1 << 0, + Material = 1 << 1, + ClickAction = 1 << 2, + Scale = 1 << 3, + ParentID = 1 << 4, + PrimFlags = 1 << 5, + PrimData = 1 << 6, + MediaURL = 1 << 7, + ScratchPad = 1 << 8, + Textures = 1 << 9, + TextureAnim = 1 << 10, + NameValue = 1 << 11, + Position = 1 << 12, + Rotation = 1 << 13, + Velocity = 1 << 14, + Acceleration = 1 << 15, + AngularVelocity = 1 << 16, + CollisionPlane = 1 << 17, + Text = 1 << 18, + Particles = 1 << 19, + ExtraData = 1 << 20, + Sound = 1 << 21, + Joint = 1 << 22, + FullUpdate = UInt32.MaxValue + } + + #endregion Enums + public delegate bool PacketMethod(IClientAPI simClient, Packet packet); /// @@ -3159,6 +3197,195 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion + #region Prim/Avatar Updates + + /*void SendObjectUpdate(SceneObjectPart obj, PrimFlags creatorFlags, PrimUpdateFlags updateFlags) + { + bool canUseCompressed, canUseImproved; + UpdateFlagsToPacketType(creatorFlags, updateFlags, out canUseCompressed, out canUseImproved); + + if (!canUseImproved && !canUseCompressed) + SendFullObjectUpdate(obj, creatorFlags, updateFlags); + else if (!canUseImproved) + SendObjectUpdateCompressed(obj, creatorFlags, updateFlags); + else + SendImprovedTerseObjectUpdate(obj, creatorFlags, updateFlags); + } + + void SendFullObjectUpdate(SceneObjectPart obj, PrimFlags creatorFlags, PrimUpdateFlags updateFlags) + { + IClientAPI owner; + if (m_scene.ClientManager.TryGetValue(obj.OwnerID, out owner) && owner is LLClientView) + { + LLClientView llOwner = (LLClientView)owner; + + // Send an update out to the owner + ObjectUpdatePacket updateToOwner = new ObjectUpdatePacket(); + updateToOwner.RegionData.RegionHandle = obj.RegionHandle; + //updateToOwner.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue); + updateToOwner.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; + updateToOwner.ObjectData[0] = BuildUpdateBlock(obj, obj.Flags | creatorFlags | PrimFlags.ObjectYouOwner, 0); + + m_udpServer.SendPacket(llOwner.UDPClient, updateToOwner, ThrottleOutPacketType.State, true); + } + + // Send an update out to everyone else + ObjectUpdatePacket updateToOthers = new ObjectUpdatePacket(); + updateToOthers.RegionData.RegionHandle = obj.RegionHandle; + //updateToOthers.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue); + updateToOthers.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; + updateToOthers.ObjectData[0] = BuildUpdateBlock(obj, obj.Flags, 0); + + m_scene.ClientManager.ForEach( + delegate(IClientAPI client) + { + if (client.AgentId != obj.OwnerID && client is LLClientView) + { + LLClientView llClient = (LLClientView)client; + m_udpServer.SendPacket(llClient.UDPClient, updateToOthers, ThrottleOutPacketType.State, true); + } + } + ); + } + + void SendObjectUpdateCompressed(SceneObjectPart obj, PrimFlags creatorFlags, PrimUpdateFlags updateFlags) + { + } + + void SendImprovedTerseObjectUpdate(SceneObjectPart obj, PrimFlags creatorFlags, PrimUpdateFlags updateFlags) + { + } + + void UpdateFlagsToPacketType(PrimFlags creatorFlags, PrimUpdateFlags updateFlags, out bool canUseCompressed, out bool canUseImproved) + { + canUseCompressed = true; + canUseImproved = true; + + if ((updateFlags & PrimUpdateFlags.FullUpdate) == PrimUpdateFlags.FullUpdate || creatorFlags != PrimFlags.None) + { + canUseCompressed = false; + canUseImproved = false; + } + else + { + if ((updateFlags & PrimUpdateFlags.Velocity) != 0 || + (updateFlags & PrimUpdateFlags.Acceleration) != 0 || + (updateFlags & PrimUpdateFlags.CollisionPlane) != 0 || + (updateFlags & PrimUpdateFlags.Joint) != 0) + { + canUseCompressed = false; + } + + if ((updateFlags & PrimUpdateFlags.PrimFlags) != 0 || + (updateFlags & PrimUpdateFlags.ParentID) != 0 || + (updateFlags & PrimUpdateFlags.Scale) != 0 || + (updateFlags & PrimUpdateFlags.PrimData) != 0 || + (updateFlags & PrimUpdateFlags.Text) != 0 || + (updateFlags & PrimUpdateFlags.NameValue) != 0 || + (updateFlags & PrimUpdateFlags.ExtraData) != 0 || + (updateFlags & PrimUpdateFlags.TextureAnim) != 0 || + (updateFlags & PrimUpdateFlags.Sound) != 0 || + (updateFlags & PrimUpdateFlags.Particles) != 0 || + (updateFlags & PrimUpdateFlags.Material) != 0 || + (updateFlags & PrimUpdateFlags.ClickAction) != 0 || + (updateFlags & PrimUpdateFlags.MediaURL) != 0 || + (updateFlags & PrimUpdateFlags.Joint) != 0) + { + canUseImproved = false; + } + } + } + + static ObjectUpdatePacket.ObjectDataBlock BuildUpdateBlockFromPrim(SceneObjectPart prim, UUID assetID, PrimFlags flags, uint crc) + { + byte[] objectData = new byte[60]; + prim.OffsetPosition.ToBytes(objectData, 0); + prim.Velocity.ToBytes(objectData, 12); + prim.Acceleration.ToBytes(objectData, 24); + prim.RotationOffset.ToBytes(objectData, 36); + prim.AngularVelocity.ToBytes(objectData, 48); + + ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); + update.ClickAction = (byte)prim.ClickAction; + update.CRC = crc; + update.ExtraParams = prim.Shape.ExtraParams ?? Utils.EmptyBytes; + update.Flags = (byte)flags; + update.FullID = prim.UUID; + update.ID = prim.LocalId; + //update.JointAxisOrAnchor = Vector3.Zero; // These are deprecated + //update.JointPivot = Vector3.Zero; + //update.JointType = 0; + update.Material = prim.Material; + update.MediaURL = Utils.EmptyBytes; // FIXME: Support this in OpenSim + if (prim.IsAttachment) + update.NameValue = Util.StringToBytes256("AttachItemID STRING RW SV " + assetID); + else + update.NameValue = Utils.EmptyBytes; + update.ObjectData = objectData; + update.ParentID = prim.ParentID; + update.PathBegin = prim.Shape.PathBegin; + update.PathCurve = prim.Shape.PathCurve; + update.PathEnd = prim.Shape.PathEnd; + update.PathRadiusOffset = prim.Shape.PathRadiusOffset; + update.PathRevolutions = prim.Shape.PathRevolutions; + update.PathScaleX = prim.Shape.PathScaleX; + update.PathScaleY = prim.Shape.PathScaleY; + update.PathShearX = prim.Shape.PathShearX; + update.PathShearY = prim.Shape.PathShearY; + update.PathSkew = prim.Shape.PathSkew; + update.PathTaperX = prim.Shape.PathTaperX; + update.PathTaperY = prim.Shape.PathTaperY; + update.PathTwist = prim.Shape.PathTwist; + update.PathTwistBegin = prim.Shape.PathTwistBegin; + update.PCode = prim.Shape.PCode; + update.ProfileBegin = prim.Shape.ProfileBegin; + update.ProfileCurve = prim.Shape.ProfileCurve; + update.ProfileEnd = prim.Shape.ProfileEnd; + update.ProfileHollow = prim.Shape.ProfileHollow; + update.PSBlock = prim.ParticleSystem ?? Utils.EmptyBytes; + update.TextColor = new Color4(prim.Color).GetBytes(true); + update.TextureAnim = prim.TextureAnimation ?? Utils.EmptyBytes; + update.TextureEntry = prim.Shape.TextureEntry ?? Utils.EmptyBytes; + update.Scale = prim.Scale; + update.State = prim.Shape.State; + update.Text = Util.StringToBytes256(prim.Text); + update.UpdateFlags = (uint)flags; + + if (prim.Sound != UUID.Zero) + { + update.Sound = prim.Sound; + update.OwnerID = prim.OwnerID; + update.Gain = (float)prim.SoundGain; + update.Radius = (float)prim.SoundRadius; + } + + switch ((PCode)prim.Shape.PCode) + { + case PCode.Grass: + case PCode.Tree: + case PCode.NewTree: + update.Data = new byte[] { prim.Shape.State }; + break; + default: + // TODO: Support ScratchPad + //if (prim.ScratchPad != null) + //{ + // update.Data = new byte[prim.ScratchPad.Length]; + // Buffer.BlockCopy(prim.ScratchPad, 0, update.Data, 0, update.Data.Length); + //} + //else + //{ + // update.Data = Utils.EmptyBytes; + //} + update.Data = Utils.EmptyBytes; + break; + } + + return update; + }*/ + + #endregion Prim/Avatar Updates + #region Avatar Packet/data sending Methods /// @@ -3365,7 +3592,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return; if (primShape.PCode == 9 && primShape.State != 0 && parentID == 0) return; - + if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) rotation = Quaternion.Identity; diff --git a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs index 66a9b5a540..cd59bdb613 100644 --- a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs @@ -224,11 +224,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat foreach (Scene s in m_scenes) { - s.ForEachScenePresence(delegate(ScenePresence presence) - { - TrySendChatMessage(presence, fromPos, regionPos, fromID, fromName, - c.Type, message, sourceType); - }); + s.ForEachScenePresence( + delegate(ScenePresence presence) + { + TrySendChatMessage(presence, fromPos, regionPos, fromID, fromName, c.Type, message, sourceType); + } + ); } } diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index 4896edfdf6..3bb162e469 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -756,7 +756,7 @@ namespace OpenSim.Region.CoreModules.World.Estate public void sendRegionHandshakeToAll() { - m_scene.Broadcast(sendRegionHandshake); + m_scene.ForEachClient(sendRegionHandshake); } public void handleEstateChangeInfo(IClientAPI remoteClient, UUID invoice, UUID senderID, UInt32 parms1, UInt32 parms2) diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs index d2b5cb19e5..332d3ce59e 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs @@ -1061,7 +1061,7 @@ namespace OpenSim.Region.CoreModules.World.Land { land.LandData.OwnerID = ownerID; - m_scene.Broadcast(SendParcelOverlay); + m_scene.ForEachClient(SendParcelOverlay); land.SendLandUpdateToClient(remote_client); } } @@ -1083,7 +1083,7 @@ namespace OpenSim.Region.CoreModules.World.Land land.LandData.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner; else land.LandData.OwnerID = m_scene.RegionInfo.MasterAvatarAssignedUUID; - m_scene.Broadcast(SendParcelOverlay); + m_scene.ForEachClient(SendParcelOverlay); land.SendLandUpdateToClient(remote_client); } } @@ -1107,7 +1107,7 @@ namespace OpenSim.Region.CoreModules.World.Land land.LandData.OwnerID = m_scene.RegionInfo.MasterAvatarAssignedUUID; land.LandData.ClaimDate = Util.UnixTimeSinceEpoch(); land.LandData.IsGroupOwned = false; - m_scene.Broadcast(SendParcelOverlay); + m_scene.ForEachClient(SendParcelOverlay); land.SendLandUpdateToClient(remote_client); } } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index d3d397dde2..d13d4fb8ec 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1193,15 +1193,6 @@ namespace OpenSim.Region.Framework.Scenes m_eventManager.TriggerOnFrame(); } - /// - /// Perform delegate action on all clients subscribing to updates from this region. - /// - /// - public void Broadcast(Action whatToDo) - { - ForEachScenePresence(delegate(ScenePresence presence) { whatToDo(presence.ControllingClient); }); - } - /// /// Backup the scene. This acts as the main method of the backup thread. /// @@ -3048,17 +3039,13 @@ namespace OpenSim.Region.Framework.Scenes } m_eventManager.TriggerOnRemovePresence(agentID); - Broadcast(delegate(IClientAPI client) - { - try - { - client.SendKillObject(avatar.RegionHandle, avatar.LocalId); - } - catch (NullReferenceException) - { - //We can safely ignore null reference exceptions. It means the avatar are dead and cleaned up anyway. - } - }); + ForEachClient( + delegate(IClientAPI client) + { + //We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway + try { client.SendKillObject(avatar.RegionHandle, avatar.LocalId); } + catch (NullReferenceException) { } + }); ForEachScenePresence( delegate(ScenePresence presence) { presence.CoarseLocationChange(); }); @@ -3143,7 +3130,7 @@ namespace OpenSim.Region.Framework.Scenes return; } } - Broadcast(delegate(IClientAPI client) { client.SendKillObject(m_regionHandle, localID); }); + ForEachClient(delegate(IClientAPI client) { client.SendKillObject(m_regionHandle, localID); }); } #endregion @@ -4211,7 +4198,7 @@ namespace OpenSim.Region.Framework.Scenes public void ForEachClient(Action action) { - m_sceneGraph.ForEachClient(action); + ClientManager.ForEach(action); } public void ForEachSOG(Action action) diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 9cd2247b3a..04397adcec 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -1107,23 +1107,6 @@ namespace OpenSim.Region.Framework.Scenes return UUID.Zero; } - protected internal void ForEachClient(Action action) - { - List splist = GetScenePresences(); - foreach (ScenePresence presence in splist) - { - try - { - action(presence.ControllingClient); - } - catch (Exception e) - { - // Catch it and move on. This includes situations where splist has inconsistent info - m_log.WarnFormat("[SCENE]: Problem processing action in ForEachClient: ", e.Message); - } - } - } - protected internal void ForEachSOG(Action action) { List objlist = new List(SceneObjectGroupsByFullID.Values); diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 801a7db3a5..377cb6e987 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -853,16 +853,6 @@ if (m_shape != null) { return m_offsetPosition + m_groupPosition; } } - public UUID ObjectCreator - { - get { return _creatorID; } - } - - public UUID ObjectOwner - { - get { return _ownerID; } - } - public SceneObjectGroup ParentGroup { get { return m_parentGroup; } @@ -1440,7 +1430,7 @@ if (m_shape != null) { // Move afterwards ResetIDs as it clears the localID dupe.LocalId = localID; // This may be wrong... it might have to be applied in SceneObjectGroup to the object that's being duplicated. - dupe._lastOwnerID = ObjectOwner; + dupe._lastOwnerID = OwnerID; byte[] extraP = new byte[Shape.ExtraParams.Length]; Array.Copy(Shape.ExtraParams, extraP, extraP.Length); diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 2a06f9eb9a..387db4456b 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -2455,11 +2455,10 @@ namespace OpenSim.Region.Framework.Scenes m_perfMonMS = Environment.TickCount; Vector3 pos = m_pos; - Vector3 vel = Velocity; - Quaternion rot = m_bodyRot; pos.Z -= m_appearance.HipOffset; - remoteClient.SendAvatarTerseUpdate(m_regionHandle, (ushort)(m_scene.TimeDilation * ushort.MaxValue), LocalId, new Vector3(pos.X, pos.Y, pos.Z), - new Vector3(vel.X, vel.Y, vel.Z), rot, m_uuid); + + remoteClient.SendAvatarTerseUpdate(m_regionHandle, (ushort)(m_scene.TimeDilation * ushort.MaxValue), + LocalId, pos, Velocity, m_bodyRot, m_uuid); m_scene.StatsReporter.AddAgentTime(Environment.TickCount - m_perfMonMS); m_scene.StatsReporter.AddAgentUpdates(1); @@ -2473,7 +2472,7 @@ namespace OpenSim.Region.Framework.Scenes { m_perfMonMS = Environment.TickCount; - m_scene.Broadcast(SendTerseUpdateToClient); + m_scene.ForEachClient(SendTerseUpdateToClient); m_lastVelocity = m_velocity; lastPhysPos = AbsolutePosition; @@ -2774,7 +2773,7 @@ namespace OpenSim.Region.Framework.Scenes if (m_isChildAgent) return; - m_scene.Broadcast( + m_scene.ForEachClient( delegate(IClientAPI client) { client.SendAnimations(animations, seqs, m_controllingClient.AgentId, objectIDs); }); } diff --git a/OpenSim/Region/OptionalModules/Scripting/Minimodule/MRMModule.cs b/OpenSim/Region/OptionalModules/Scripting/Minimodule/MRMModule.cs index ce50f9e34d..4521f8ef6a 100644 --- a/OpenSim/Region/OptionalModules/Scripting/Minimodule/MRMModule.cs +++ b/OpenSim/Region/OptionalModules/Scripting/Minimodule/MRMModule.cs @@ -259,7 +259,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.Minimodule if (e.InnerException != null) m_log.Error("[MRM] " + e.InnerException); - m_scene.Broadcast(delegate(IClientAPI user) + m_scene.ForEachClient(delegate(IClientAPI user) { user.SendAlertMessage( "MRM UnAuthorizedAccess: " + e); @@ -268,7 +268,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.Minimodule catch (Exception e) { m_log.Info("[MRM] Error: " + e); - m_scene.Broadcast(delegate(IClientAPI user) + m_scene.ForEachClient(delegate(IClientAPI user) { user.SendAlertMessage( "Compile error while building MRM script, check OpenSim console for more information."); From df2d5a460f060129e5c09148b9fa4df2f241d8b1 Mon Sep 17 00:00:00 2001 From: jjgreens Date: Wed, 14 Oct 2009 23:15:03 -0700 Subject: [PATCH 02/61] Replaced the update lists with a priority queue implementation in LLClientView Replaced the update lists with a priority queue implementation in LLClientView. The priority queues are based on the MinHeap implementation also included in this commit within the OpneSim.Framework namespace. Initially setup to exactly mimic the behavior beofre the change which was a first come first serve queue. --- OpenSim/Framework/MinHeap.cs | 375 ++++++++++++++++++ .../ClientStack/LindenUDP/LLClientView.cs | 224 ++++++++--- 2 files changed, 554 insertions(+), 45 deletions(-) create mode 100755 OpenSim/Framework/MinHeap.cs diff --git a/OpenSim/Framework/MinHeap.cs b/OpenSim/Framework/MinHeap.cs new file mode 100755 index 0000000000..ad39bbc51c --- /dev/null +++ b/OpenSim/Framework/MinHeap.cs @@ -0,0 +1,375 @@ +using System; +using System.Threading; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace OpenSim.Framework +{ + public interface IHandle { } + + [Serializable, ComVisible(false)] + public class MinHeap : ICollection, ICollection + { + private class Handle : IHandle + { + internal int index = -1; + internal MinHeap heap = null; + + internal void Clear() + { + this.index = -1; + this.heap = null; + } + } + + private struct HeapItem + { + internal T value; + internal Handle handle; + + internal HeapItem(T value, Handle handle) + { + this.value = value; + this.handle = handle; + } + + internal void Clear() + { + this.value = default(T); + if (this.handle != null) + { + this.handle.Clear(); + this.handle = null; + } + } + } + + public const int DEFAULT_CAPACITY = 4; + + private HeapItem[] items; + private int size; + private object sync_root; + private int version; + + private Comparison comparison; + + public MinHeap() : this(DEFAULT_CAPACITY, Comparer.Default) { } + public MinHeap(int capacity) : this(capacity, Comparer.Default) { } + public MinHeap(IComparer comparer) : this(DEFAULT_CAPACITY, comparer) { } + public MinHeap(int capacity, IComparer comparer) : + this(capacity, new Comparison(comparer.Compare)) { } + public MinHeap(Comparison comparison) : this(DEFAULT_CAPACITY, comparison) { } + public MinHeap(int capacity, Comparison comparison) + { + this.items = new HeapItem[capacity]; + this.comparison = comparison; + this.size = this.version = 0; + } + + public int Count { get { return this.size; } } + + public bool IsReadOnly { get { return false; } } + + public bool IsSynchronized { get { return false; } } + + public T this[IHandle key] + { + get + { + Handle handle = ValidateThisHandle(key); + return this.items[handle.index].value; + } + + set + { + Handle handle = ValidateThisHandle(key); + this.items[handle.index].value = value; + if (!BubbleUp(handle.index)) + BubbleDown(handle.index); + } + } + + public object SyncRoot + { + get + { + if (this.sync_root == null) + Interlocked.CompareExchange(ref this.sync_root, new object(), null); + return this.sync_root; + } + } + + private Handle ValidateHandle(IHandle ihandle) + { + if (ihandle == null) + throw new ArgumentNullException("handle"); + Handle handle = ihandle as Handle; + if (handle == null) + throw new InvalidOperationException("handle is not valid"); + return handle; + } + + private Handle ValidateThisHandle(IHandle ihandle) + { + Handle handle = ValidateHandle(ihandle); + if (!object.ReferenceEquals(handle.heap, this)) + throw new InvalidOperationException("handle is not valid for this heap"); + if (handle.index < 0) + throw new InvalidOperationException("handle is not associated to a value"); + return handle; + } + + private void Set(HeapItem item, int index) + { + this.items[index] = item; + if (item.handle != null) + item.handle.index = index; + } + + private bool BubbleUp(int index) + { + HeapItem item = this.items[index]; + int current, parent; + + for (current = index, parent = (current - 1) / 2; + (current > 0) && (this.comparison(this.items[parent].value, item.value)) > 0; + current = parent, parent = (current - 1) / 2) + { + Set(this.items[parent], current); + } + + if (current != index) + { + Set(item, current); + ++this.version; + return true; + } + return false; + } + + private void BubbleDown(int index) + { + HeapItem item = this.items[index]; + int current, child; + + for (current = index, child = (2 * current) + 1; + current < this.size / 2; + current = child, child = (2 * current) + 1) + { + if ((child < this.size - 1) && this.comparison(this.items[child].value, this.items[child + 1].value) > 0) + ++child; + if (this.comparison(this.items[child].value, item.value) >= 0) + break; + Set(this.items[child], current); + } + + if (current != index) + { + Set(item, current); + ++this.version; + } + } + + public bool TryGetValue(IHandle key, out T value) + { + Handle handle = ValidateHandle(key); + if (handle.index > -1) + { + value = this.items[handle.index].value; + return true; + } + value = default(T); + return false; + } + + public bool ContainsHandle(IHandle ihandle) + { + Handle handle = ValidateHandle(ihandle); + return object.ReferenceEquals(handle.heap, this) && handle.index > -1; + } + + public void Add(T value, ref IHandle handle) + { + if (handle == null) + handle = new Handle(); + Add(value, handle); + } + + public void Add(T value, IHandle ihandle) + { + if (this.size == this.items.Length) + { + int capacity = (int)((this.items.Length * 200L) / 100L); + if (capacity < (this.items.Length + DEFAULT_CAPACITY)) + capacity = this.items.Length + DEFAULT_CAPACITY; + Array.Resize(ref this.items, capacity); + } + + Handle handle = null; + if (ihandle != null) + { + handle = ValidateHandle(ihandle); + handle.heap = this; + } + + HeapItem item = new MinHeap.HeapItem(value, handle); + + Set(item, this.size); + BubbleUp(this.size++); + } + + public void Add(T value) + { + Add(value, null); + } + + public T Min() + { + if (this.size == 0) + throw new InvalidOperationException("Heap is empty"); + + return this.items[0].value; + } + + public void Clear() + { + for (int index = 0; index < this.size; ++index) + this.items[index].Clear(); + this.size = 0; + ++this.version; + } + + public void TrimExcess() + { + int length = (int)(this.items.Length * 0.9); + if (this.size < length) + Array.Resize(ref this.items, Math.Min(this.size, DEFAULT_CAPACITY)); + } + + private void RemoveAt(int index) + { + if (this.size == 0) + throw new InvalidOperationException("Heap is empty"); + if (index >= this.size) + throw new ArgumentOutOfRangeException("index"); + + this.items[index].Clear(); + if (--this.size > 0 && index != this.size) + { + Set(this.items[this.size], index); + if (!BubbleUp(index)) + BubbleDown(index); + } + } + + public T RemoveMin() + { + if (this.size == 0) + throw new InvalidOperationException("Heap is empty"); + + HeapItem item = this.items[0]; + RemoveAt(0); + return item.value; + } + + public T Remove(IHandle ihandle) + { + Handle handle = ValidateThisHandle(ihandle); + HeapItem item = this.items[handle.index]; + RemoveAt(handle.index); + return item.value; + } + + private int GetIndex(T value) + { + EqualityComparer comparer = EqualityComparer.Default; + int index; + + for (index = 0; index < this.size; ++index) + { + if (comparer.Equals(this.items[index].value, value)) + return index; + } + return -1; + } + + public bool Contains(T value) + { + return GetIndex(value) != -1; + } + + public bool Remove(T value) + { + int index = GetIndex(value); + if (index != -1) + { + RemoveAt(index); + return true; + } + return false; + } + + public void CopyTo(T[] array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + if (array.Rank != 1) + throw new ArgumentException("Multidimensional array not supported"); + if (array.GetLowerBound(0) != 0) + throw new ArgumentException("Non-zero lower bound array not supported"); + + int length = array.Length; + if ((index < 0) || (index > length)) + throw new ArgumentOutOfRangeException("index"); + if ((length - index) < this.size) + throw new ArgumentException("Not enough space available in array starting at index"); + + for (int i = 0; i < this.size; ++i) + array[index + i] = this.items[i].value; + } + + public void CopyTo(Array array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + if (array.Rank != 1) + throw new ArgumentException("Multidimensional array not supported"); + if (array.GetLowerBound(0) != 0) + throw new ArgumentException("Non-zero lower bound array not supported"); + + int length = array.Length; + if ((index < 0) || (index > length)) + throw new ArgumentOutOfRangeException("index"); + if ((length - index) < this.size) + throw new ArgumentException("Not enough space available in array starting at index"); + + try + { + for (int i = 0; i < this.size; ++i) + array.SetValue(this.items[i].value, index + i); + } + catch (ArrayTypeMismatchException) + { + throw new ArgumentException("Invalid array type"); + } + } + + public IEnumerator GetEnumerator() + { + int version = this.version; + + for (int index = 0; index < this.size; ++index) + { + if (version != this.version) + throw new InvalidOperationException("Heap was modified while enumerating"); + yield return this.items[index].value; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 82a2cdd037..93fdeefeb4 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -321,11 +321,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_cachedTextureSerial; private Timer m_avatarTerseUpdateTimer; - private List m_avatarTerseUpdates = new List(); + private PriorityQueue m_avatarTerseUpdates_ = + new PriorityQueue(); private Timer m_primTerseUpdateTimer; - private List m_primTerseUpdates = new List(); + private PriorityQueue m_primTerseUpdates_ = + new PriorityQueue(); private Timer m_primFullUpdateTimer; - private List m_primFullUpdates = new List(); + private PriorityQueue m_primFullUpdates_ = + new PriorityQueue(); private int m_moneyBalance; private int m_animationSequenceNumber = 1; private bool m_SendLogoutPacketWhenClosing = true; @@ -3435,16 +3438,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP ImprovedTerseObjectUpdatePacket.ObjectDataBlock terseBlock = CreateAvatarImprovedBlock(localID, position, velocity,rotation); - lock (m_avatarTerseUpdates) + lock (m_avatarTerseUpdates_.SyncRoot) { - m_avatarTerseUpdates.Add(terseBlock); + m_avatarTerseUpdates_.Enqueue(DateTime.Now.ToOADate(), terseBlock, localID); // If packet is full or own movement packet, send it. - if (m_avatarTerseUpdates.Count >= m_avatarTerseUpdatesPerPacket) + if (m_avatarTerseUpdates_.Count >= m_avatarTerseUpdatesPerPacket) { ProcessAvatarTerseUpdates(this, null); } - else if (m_avatarTerseUpdates.Count == 1) + else if (m_avatarTerseUpdates_.Count == 1) { lock (m_avatarTerseUpdateTimer) m_avatarTerseUpdateTimer.Start(); @@ -3454,7 +3457,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void ProcessAvatarTerseUpdates(object sender, ElapsedEventArgs e) { - lock (m_avatarTerseUpdates) + lock (m_avatarTerseUpdates_.SyncRoot) { ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); @@ -3465,8 +3468,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP (ushort)(Scene.TimeDilation * ushort.MaxValue); int max = m_avatarTerseUpdatesPerPacket; - if (max > m_avatarTerseUpdates.Count) - max = m_avatarTerseUpdates.Count; + if (max > m_avatarTerseUpdates_.Count) + max = m_avatarTerseUpdates_.Count; int count = 0; int size = 0; @@ -3474,30 +3477,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte[] zerobuffer = new byte[1024]; byte[] blockbuffer = new byte[1024]; + Queue updates = new Queue(); + for (count = 0 ; count < max ; count++) { int length = 0; - m_avatarTerseUpdates[count].ToBytes(blockbuffer, ref length); + m_avatarTerseUpdates_.Peek().ToBytes(blockbuffer, ref length); length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); if (size + length > Packet.MTU) break; size += length; + updates.Enqueue(m_avatarTerseUpdates_.Dequeue()); } terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; for (int i = 0 ; i < count ; i++) - { - terse.ObjectData[i] = m_avatarTerseUpdates[0]; - m_avatarTerseUpdates.RemoveAt(0); - } + terse.ObjectData[i] = updates.Dequeue(); terse.Header.Reliable = false; terse.Header.Zerocoded = true; // FIXME: Move this to ThrottleOutPacketType.State when the real prioritization code is committed OutPacket(terse, ThrottleOutPacketType.Task); - if (m_avatarTerseUpdates.Count == 0) + if (m_avatarTerseUpdates_.Count == 0) { lock (m_avatarTerseUpdateTimer) m_avatarTerseUpdateTimer.Stop(); @@ -3660,14 +3663,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectData.TextureAnim = textureanim; } - lock (m_primFullUpdates) + lock (m_primFullUpdates_.SyncRoot) { - if (m_primFullUpdates.Count == 0) + if (m_primFullUpdates_.Count == 0) m_primFullUpdateTimer.Start(); - m_primFullUpdates.Add(objectData); + m_primFullUpdates_.Enqueue(DateTime.Now.ToOADate(), objectData, localID); - if (m_primFullUpdates.Count >= m_primFullUpdatesPerPacket) + if (m_primFullUpdates_.Count >= m_primFullUpdatesPerPacket) ProcessPrimFullUpdates(this, null); } } @@ -3690,9 +3693,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e) { - lock (m_primFullUpdates) + lock (m_primFullUpdates_.SyncRoot) { - if (m_primFullUpdates.Count == 0 && m_primFullUpdateTimer.Enabled) + if (m_primFullUpdates_.Count == 0 && m_primFullUpdateTimer.Enabled) { lock (m_primFullUpdateTimer) m_primFullUpdateTimer.Stop(); @@ -3709,7 +3712,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); - int max = m_primFullUpdates.Count; + int max = m_primFullUpdates_.Count; if (max > m_primFullUpdatesPerPacket) max = m_primFullUpdatesPerPacket; @@ -3719,29 +3722,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte[] zerobuffer = new byte[1024]; byte[] blockbuffer = new byte[1024]; + Queue updates = new Queue(); + for (count = 0 ; count < max ; count++) { int length = 0; - m_primFullUpdates[count].ToBytes(blockbuffer, ref length); + m_primFullUpdates_.Peek().ToBytes(blockbuffer, ref length); length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); if (size + length > Packet.MTU) break; size += length; + updates.Enqueue(m_primFullUpdates_.Dequeue()); } outPacket.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[count]; for (int index = 0 ; index < count ; index++) - { - outPacket.ObjectData[index] = m_primFullUpdates[0]; - m_primFullUpdates.RemoveAt(0); - } + outPacket.ObjectData[index] = updates.Dequeue(); outPacket.Header.Zerocoded = true; OutPacket(outPacket, ThrottleOutPacketType.State); - if (m_primFullUpdates.Count == 0 && m_primFullUpdateTimer.Enabled) + if (m_primFullUpdates_.Count == 0 && m_primFullUpdateTimer.Enabled) lock (m_primFullUpdateTimer) m_primFullUpdateTimer.Stop(); } @@ -3763,23 +3766,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP CreatePrimImprovedBlock(localID, position, rotation, velocity, rotationalvelocity, state); - lock (m_primTerseUpdates) + lock (m_primTerseUpdates_.SyncRoot) { - if (m_primTerseUpdates.Count == 0) + if (m_primTerseUpdates_.Count == 0) m_primTerseUpdateTimer.Start(); - m_primTerseUpdates.Add(objectData); + m_primTerseUpdates_.Enqueue(DateTime.Now.ToOADate(), objectData, localID); - if (m_primTerseUpdates.Count >= m_primTerseUpdatesPerPacket) + if (m_primTerseUpdates_.Count >= m_primTerseUpdatesPerPacket) ProcessPrimTerseUpdates(this, null); } } void ProcessPrimTerseUpdates(object sender, ElapsedEventArgs e) { - lock (m_primTerseUpdates) + lock (m_primTerseUpdates_.SyncRoot) { - if (m_primTerseUpdates.Count == 0) + if (m_primTerseUpdates_.Count == 0) { lock (m_primTerseUpdateTimer) m_primTerseUpdateTimer.Stop(); @@ -3797,7 +3800,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); - int max = m_primTerseUpdates.Count; + int max = m_primTerseUpdates_.Count; if (max > m_primTerseUpdatesPerPacket) max = m_primTerseUpdatesPerPacket; @@ -3807,14 +3810,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte[] zerobuffer = new byte[1024]; byte[] blockbuffer = new byte[1024]; + Queue updates = new Queue(); + for (count = 0 ; count < max ; count++) { int length = 0; - m_primTerseUpdates[count].ToBytes(blockbuffer, ref length); + m_primTerseUpdates_.Peek().ToBytes(blockbuffer, ref length); length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); if (size + length > Packet.MTU) break; size += length; + updates.Enqueue(m_primTerseUpdates_.Dequeue()); } outPacket.ObjectData = @@ -3822,16 +3828,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP ObjectDataBlock[count]; for (int index = 0 ; index < count ; index++) - { - outPacket.ObjectData[index] = m_primTerseUpdates[0]; - m_primTerseUpdates.RemoveAt(0); - } + outPacket.ObjectData[index] = updates.Dequeue(); outPacket.Header.Reliable = false; outPacket.Header.Zerocoded = true; OutPacket(outPacket, ThrottleOutPacketType.State); - if (m_primTerseUpdates.Count == 0) + if (m_primTerseUpdates_.Count == 0) lock (m_primTerseUpdateTimer) m_primTerseUpdateTimer.Stop(); } @@ -3839,15 +3842,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void FlushPrimUpdates() { - while (m_primFullUpdates.Count > 0) + while (m_primFullUpdates_.Count > 0) { ProcessPrimFullUpdates(this, null); } - while (m_primTerseUpdates.Count > 0) + while (m_primTerseUpdates_.Count > 0) { ProcessPrimTerseUpdates(this, null); } - while (m_avatarTerseUpdates.Count > 0) + while (m_avatarTerseUpdates_.Count > 0) { ProcessAvatarTerseUpdates(this, null); } @@ -10578,5 +10581,136 @@ namespace OpenSim.Region.ClientStack.LindenUDP pack.TextureData.TextureID = textureID; OutPacket(pack, ThrottleOutPacketType.Task); } + + #region PriorityQueue + private class PriorityQueue + { + private MinHeap[] heaps = new MinHeap[1]; + private Dictionary lookup_table = new Dictionary(); + private Comparison comparison; + private object sync_root = new object(); + + 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) + { + for (int i = 0; i < heaps.Length; ++i) + heaps[i] = new MinHeap(capacity); + this.comparison = comparison; + } + + internal object SyncRoot { get { return this.sync_root; } } + internal int Count + { + get + { + int count = 0; + for (int i = 0; i < heaps.Length; ++i) + count = heaps[i].Count; + return count; + } + } + + internal bool Enqueue(TPriority priority, TValue value, uint local_id) + { + LookupItem item; + + if (lookup_table.TryGetValue(local_id, out item)) + { + item.Heap[item.Handle] = new MinHeapItem(priority, value, local_id, this.comparison); + return false; + } + else + { + item.Heap = heaps[0]; + item.Heap.Add(new MinHeapItem(priority, value, local_id, this.comparison), ref item.Handle); + lookup_table.Add(local_id, item); + return true; + } + } + + internal TValue Peek() + { + for (int i = 0; i < heaps.Length; ++i) + if (heaps[i].Count > 0) + return heaps[i].Min().Value; + throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); + } + + internal TValue Dequeue() + { + for (int i = 0; i < heaps.Length; ++i) + { + if (heaps[i].Count > 0) + { + MinHeapItem item = heaps[i].RemoveMin(); + lookup_table.Remove(item.LocalID); + return item.Value; + } + } + throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); + } + + #region MinHeapItem + private struct MinHeapItem : IComparable + { + private TPriority priority; + private TValue value; + private uint local_id; + private Comparison comparison; + + internal MinHeapItem(TPriority priority, TValue value, uint local_id) : + this(priority, value, local_id, Comparer.Default) { } + internal MinHeapItem(TPriority priority, TValue value, uint local_id, IComparer comparer) : + this(priority, value, local_id, new Comparison(comparer.Compare)) { } + internal MinHeapItem(TPriority priority, TValue value, uint local_id, Comparison comparison) + { + this.priority = priority; + this.value = value; + this.local_id = local_id; + this.comparison = comparison; + } + + internal TPriority Priority { get { return this.priority; } } + internal TValue Value { get { return this.value; } } + internal uint LocalID { get { return this.local_id; } } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("["); + if (this.priority != null) + sb.Append(this.priority.ToString()); + sb.Append(","); + if (this.value != null) + sb.Append(this.value.ToString()); + sb.Append("]"); + return sb.ToString(); + } + + public int CompareTo(MinHeapItem other) + { + return this.comparison(this.priority, other.priority); + } + } + #endregion + + #region LookupItem + private struct LookupItem { + internal MinHeap Heap; + internal IHandle Handle; + } + #endregion + } + #endregion + } } From 4b75353cbf50de3cae4c48ec90b55f30c1612c92 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 15 Oct 2009 16:35:27 -0700 Subject: [PATCH 03/61] Object update prioritization by Jim Greensky of Intel Labs, part one. This implements a simple distance prioritizer based on initial agent positions. Re-prioritizing and more advanced priority algorithms will follow soon --- .../Client/MXP/ClientStack/MXPClientView.cs | 31 +- .../VWoHTTP/ClientStack/VWHClientView.cs | 13 +- OpenSim/Framework/IClientAPI.cs | 254 +++++++++++-- .../ClientStack/LindenUDP/LLClientView.cs | 344 +++++++----------- .../Examples/SimpleModule/MyNpcCharacter.cs | 28 +- .../Framework/Interfaces/ISceneViewer.cs | 1 - OpenSim/Region/Framework/Scenes/Scene.cs | 40 +- OpenSim/Region/Framework/Scenes/SceneGraph.cs | 1 - .../Framework/Scenes/SceneObjectGroup.cs | 74 +++- .../Framework/Scenes/SceneObjectPart.cs | 8 +- .../Region/Framework/Scenes/ScenePresence.cs | 58 ++- .../Region/Framework/Scenes/SceneViewer.cs | 23 +- .../Framework/Scenes/Scripting/IScriptHost.cs | 4 +- .../Scenes/Scripting/NullScriptHost.cs | 4 +- .../Server/IRCClientView.cs | 13 +- .../SceneObjectGroupDiff.cs | 2 +- .../OptionalModules/World/NPC/NPCAvatar.cs | 27 +- .../Shared/Api/Implementation/LSL_Api.cs | 18 +- .../Shared/Api/Implementation/OSSL_Api.cs | 4 +- OpenSim/Tests/Common/Mock/TestClient.cs | 28 +- 20 files changed, 573 insertions(+), 402 deletions(-) diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index bc02bc4186..ea29c41b8f 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -996,19 +996,19 @@ namespace OpenSim.Client.MXP.ClientStack // Need to translate to MXP somehow } - public void SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, uint avatarLocalID, Vector3 position, byte[] textureEntry, uint parentID, Quaternion rotation) + public void SendAvatarData(SendAvatarData data) { //ScenePresence presence=((Scene)this.Scene).GetScenePresence(avatarID); - UUID ownerID = avatarID; - MXPSendAvatarData(firstName + " " + lastName, ownerID, UUID.Zero, avatarID, avatarLocalID, position, rotation); + UUID ownerID = data.avatarID; + MXPSendAvatarData(data.firstName + " " + data.lastName, ownerID, UUID.Zero, data.avatarID, data.avatarLocalID, data.Pos, data.rotation); } - public void SendAvatarTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, Vector3 velocity, Quaternion rotation, UUID uuid) + public void SendAvatarTerseUpdate(SendAvatarTerseData data) { MovementEventMessage me = new MovementEventMessage(); - me.ObjectIndex = localID; - me.Location =ToOmVector(position); - me.Orientation = ToOmQuaternion(rotation); + me.ObjectIndex = data.localID; + me.Location = ToOmVector(data.position); + me.Orientation = ToOmQuaternion(data.rotation); Session.Send(me); } @@ -1028,22 +1028,17 @@ namespace OpenSim.Client.MXP.ClientStack // Need to translate to MXP somehow } - public void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, UUID objectID, UUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, byte clickAction, byte material, byte[] textureanim, bool attachment, uint AttachPoint, UUID AssetId, UUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius) + public void SendPrimitiveToClient(SendPrimitiveData data) { - MXPSendPrimitive(localID, ownerID, acc, rvel, primShape, pos, objectID, vel, rotation, flags,text,color,parentID,particleSystem,clickAction,material,textureanim); + MXPSendPrimitive(data.localID, data.ownerID, data.acc, data.rvel, data.primShape, data.pos, data.objectID, data.vel, data.rotation, data.flags, data.text, data.color, data.parentID, data.particleSystem, data.clickAction, data.material, data.textureanim); } - public void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, UUID objectID, UUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, byte clickAction, byte material) - { - MXPSendPrimitive(localID, ownerID, acc, rvel, primShape, pos, objectID, vel, rotation, flags, text, color, parentID, particleSystem, clickAction, material, new byte[0]); - } - - public void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, Quaternion rotation, Vector3 velocity, Vector3 rotationalvelocity, byte state, UUID AssetId, UUID owner, int attachPoint) + public void SendPrimTerseUpdate(SendPrimitiveTerseData data) { MovementEventMessage me = new MovementEventMessage(); - me.ObjectIndex = localID; - me.Location = ToOmVector(position); - me.Orientation = ToOmQuaternion(rotation); + me.ObjectIndex = data.localID; + me.Location = ToOmVector(data.position); + me.Orientation = ToOmQuaternion(data.rotation); Session.Send(me); } diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index e3abcf5bac..3a48a03db7 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -560,12 +560,12 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } - public void SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, uint avatarLocalID, Vector3 Pos, byte[] textureEntry, uint parentID, Quaternion rotation) + public void SendAvatarData(SendAvatarData data) { throw new System.NotImplementedException(); } - public void SendAvatarTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, Vector3 velocity, Quaternion rotation, UUID uuid) + public void SendAvatarTerseUpdate(SendAvatarTerseData data) { throw new System.NotImplementedException(); } @@ -585,17 +585,12 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } - public void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, UUID objectID, UUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, byte clickAction, byte material, byte[] textureanim, bool attachment, uint AttachPoint, UUID AssetId, UUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius) + public void SendPrimitiveToClient(SendPrimitiveData data) { throw new System.NotImplementedException(); } - public void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, UUID objectID, UUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, byte clickAction, byte material) - { - throw new System.NotImplementedException(); - } - - public void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, Quaternion rotation, Vector3 velocity, Vector3 rotationalvelocity, byte state, UUID AssetId, UUID owner, int attachPoint) + public void SendPrimTerseUpdate(SendPrimitiveTerseData data) { throw new System.NotImplementedException(); } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 29846f5f2f..4b51e158b2 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -517,6 +517,233 @@ namespace OpenSim.Framework public float dwell; } + public struct SendAvatarData + { + private ulong m_regionHandle; + private string m_firstName; + private string m_lastName; + private string m_grouptitle; + private UUID m_avatarID; + private uint m_avatarLocalID; + private Vector3 m_Pos; + private byte[] m_textureEntry; + private uint m_parentID; + private Quaternion m_rotation; + + public SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, + uint avatarLocalID, + Vector3 Pos, byte[] textureEntry, uint parentID, Quaternion rotation) + { + this.m_regionHandle = regionHandle; + this.m_firstName = firstName; + this.m_lastName = lastName; + this.m_grouptitle = grouptitle; + this.m_avatarID = avatarID; + this.m_avatarLocalID = avatarLocalID; + this.m_Pos = Pos; + this.m_textureEntry = textureEntry; + this.m_parentID = parentID; + this.m_rotation = rotation; + } + + public ulong regionHandle { get { return this.m_regionHandle; } } + public string firstName { get { return this.m_firstName; } } + public string lastName { get { return this.m_lastName; } } + public string grouptitle { get { return this.m_grouptitle; } } + public UUID avatarID { get { return this.m_avatarID; } } + public uint avatarLocalID { get { return this.m_avatarLocalID; } } + public Vector3 Pos { get { return this.m_Pos; } } + public byte[] textureEntry { get { return this.m_textureEntry; } } + public uint parentID { get { return this.m_parentID; } } + public Quaternion rotation { get { return this.m_rotation; } } + } + + public struct SendAvatarTerseData + { + private ulong m_regionHandle; + private ushort m_timeDilation; + private uint m_localID; + private Vector3 m_position; + private Vector3 m_velocity; + private Quaternion m_rotation; + private UUID m_agentid; + private double m_priority; + + public SendAvatarTerseData(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, + Vector3 velocity, Quaternion rotation, UUID agentid, double priority) + { + this.m_regionHandle = regionHandle; + this.m_timeDilation = timeDilation; + this.m_localID = localID; + this.m_position = position; + this.m_velocity = velocity; + this.m_rotation = rotation; + this.m_agentid = agentid; + this.m_priority = priority; + } + + public ulong regionHandle { get { return this.m_regionHandle; } } + public ushort timeDilation { get { return this.m_timeDilation; } } + public uint localID { get { return this.m_localID; } } + public Vector3 position { get { return this.m_position; } } + public Vector3 velocity { get { return this.m_velocity; } } + public Quaternion rotation { get { return this.m_rotation; } } + public UUID agentid { get { return this.m_agentid; } } + public double priority { get { return this.m_priority; } } + } + + public struct SendPrimitiveTerseData + { + private ulong m_regionHandle; + private ushort m_timeDilation; + private uint m_localID; + private Vector3 m_position; + private Quaternion m_rotation; + private Vector3 m_velocity; + private Vector3 m_rotationalvelocity; + private byte m_state; + private UUID m_AssetId; + private UUID m_owner; + private int m_attachPoint; + private double m_priority; + + public SendPrimitiveTerseData(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, + Quaternion rotation, Vector3 velocity, Vector3 rotationalvelocity, byte state, + UUID AssetId, UUID owner, int attachPoint, double priority) + { + this.m_regionHandle = regionHandle; + this.m_timeDilation = timeDilation; + this.m_localID = localID; + this.m_position = position; + this.m_rotation = rotation; + this.m_velocity = velocity; + this.m_rotationalvelocity = rotationalvelocity; + this.m_state = state; + this.m_AssetId = AssetId; + this.m_owner = owner; + this.m_attachPoint = attachPoint; + this.m_priority = priority; + } + + public ulong regionHandle { get { return this.m_regionHandle; } } + public ushort timeDilation { get { return this.m_timeDilation; } } + public uint localID { get { return this.m_localID; } } + public Vector3 position { get { return this.m_position; } } + public Quaternion rotation { get { return this.m_rotation; } } + public Vector3 velocity { get { return this.m_velocity; } } + public Vector3 rotationalvelocity { get { return this.m_rotationalvelocity; } } + public byte state { get { return this.m_state; } } + public UUID AssetId { get { return this.m_AssetId; } } + public UUID owner { get { return this.m_owner; } } + public int attachPoint { get { return this.m_attachPoint; } } + public double priority { get { return this.m_priority; } } + } + + public struct SendPrimitiveData + { + private ulong m_regionHandle; + private ushort m_timeDilation; + private uint m_localID; + private PrimitiveBaseShape m_primShape; + private Vector3 m_pos; + private Vector3 m_vel; + private Vector3 m_acc; + private Quaternion m_rotation; + private Vector3 m_rvel; + private uint m_flags; + private UUID m_objectID; + private UUID m_ownerID; + private string m_text; + private byte[] m_color; + private uint m_parentID; + private byte[] m_particleSystem; + private byte m_clickAction; + private byte m_material; + private byte[] m_textureanim; + private bool m_attachment; + private uint m_AttachPoint; + private UUID m_AssetId; + private UUID m_SoundId; + private double m_SoundVolume; + private byte m_SoundFlags; + private double m_SoundRadius; + private double m_priority; + + public SendPrimitiveData(ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, + Vector3 pos, Vector3 vel, Vector3 acc, Quaternion rotation, Vector3 rvel, + uint flags, UUID objectID, UUID ownerID, string text, byte[] color, + uint parentID, byte[] particleSystem, byte clickAction, byte material, double priority) : + this(regionHandle, timeDilation, localID, primShape, pos, vel, acc, rotation, rvel, flags, objectID, + ownerID, text, color, parentID, particleSystem, clickAction, material, new byte[0], false, 0, UUID.Zero, + UUID.Zero, 0, 0, 0, priority) { } + + public SendPrimitiveData(ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, + Vector3 pos, Vector3 vel, Vector3 acc, Quaternion rotation, Vector3 rvel, + uint flags, + UUID objectID, UUID ownerID, string text, byte[] color, uint parentID, + byte[] particleSystem, + byte clickAction, byte material, byte[] textureanim, bool attachment, + uint AttachPoint, UUID AssetId, UUID SoundId, double SoundVolume, byte SoundFlags, + double SoundRadius, double priority) + { + this.m_regionHandle = regionHandle; + this.m_timeDilation = timeDilation; + this.m_localID = localID; + this.m_primShape = primShape; + this.m_pos = pos; + this.m_vel = vel; + this.m_acc = acc; + this.m_rotation = rotation; + this.m_rvel = rvel; + this.m_flags = flags; + this.m_objectID = objectID; + this.m_ownerID = ownerID; + this.m_text = text; + this.m_color = color; + this.m_parentID = parentID; + this.m_particleSystem = particleSystem; + this.m_clickAction = clickAction; + this.m_material = material; + this.m_textureanim = textureanim; + this.m_attachment = attachment; + this.m_AttachPoint = AttachPoint; + this.m_AssetId = AssetId; + this.m_SoundId = SoundId; + this.m_SoundVolume = SoundVolume; + this.m_SoundFlags = SoundFlags; + this.m_SoundRadius = SoundRadius; + this.m_priority = priority; + } + + public ulong regionHandle { get { return this.m_regionHandle; } } + public ushort timeDilation { get { return this.m_timeDilation; } } + public uint localID { get { return this.m_localID; } } + public PrimitiveBaseShape primShape { get { return this.m_primShape; } } + public Vector3 pos { get { return this.m_pos; } } + public Vector3 vel { get { return this.m_vel; } } + public Vector3 acc { get { return this.m_acc; } } + public Quaternion rotation { get { return this.m_rotation; } } + public Vector3 rvel { get { return this.m_rvel; } } + public uint flags { get { return this.m_flags; } } + public UUID objectID { get { return this.m_objectID; } } + public UUID ownerID { get { return this.m_ownerID; } } + public string text { get { return this.m_text; } } + public byte[] color { get { return this.m_color; } } + public uint parentID { get { return this.m_parentID; } } + public byte[] particleSystem { get { return this.m_particleSystem; } } + public byte clickAction { get { return this.m_clickAction; } } + public byte material { get { return this.m_material; } } + public byte[] textureanim { get { return this.m_textureanim; } } + public bool attachment { get { return this.m_attachment; } } + public uint AttachPoint { get { return this.m_AttachPoint; } } + public UUID AssetId { get { return this.m_AssetId; } } + public UUID SoundId { get { return this.m_SoundId; } } + public double SoundVolume { get { return this.m_SoundVolume; } } + public byte SoundFlags { get { return this.m_SoundFlags; } } + public double SoundRadius { get { return this.m_SoundRadius; } } + public double priority { get { return this.m_priority; } } + } + public interface IClientAPI { Vector3 StartPos { get; set; } @@ -877,37 +1104,18 @@ namespace OpenSim.Framework void SendMoneyBalance(UUID transaction, bool success, byte[] description, int balance); void SendPayPrice(UUID objectID, int[] payPrice); - void SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, - uint avatarLocalID, - Vector3 Pos, byte[] textureEntry, uint parentID, Quaternion rotation); + void SendAvatarData(SendAvatarData data); - void SendAvatarTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, - Vector3 velocity, Quaternion rotation, UUID agentid); + void SendAvatarTerseUpdate(SendAvatarTerseData data); void SendCoarseLocationUpdate(List users, List CoarseLocations); void AttachObject(uint localID, Quaternion rotation, byte attachPoint, UUID ownerID); void SetChildAgentThrottle(byte[] throttle); - void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, - Vector3 pos, Vector3 vel, Vector3 acc, Quaternion rotation, Vector3 rvel, - uint flags, - UUID objectID, UUID ownerID, string text, byte[] color, uint parentID, - byte[] particleSystem, - byte clickAction, byte material, byte[] textureanim, bool attachment, - uint AttachPoint, UUID AssetId, UUID SoundId, double SoundVolume, byte SoundFlags, - double SoundRadius); + void SendPrimitiveToClient(SendPrimitiveData data); - - void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, - Vector3 pos, Vector3 vel, Vector3 acc, Quaternion rotation, Vector3 rvel, - uint flags, UUID objectID, UUID ownerID, string text, byte[] color, - uint parentID, byte[] particleSystem, byte clickAction, byte material); - - - void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, - Quaternion rotation, Vector3 velocity, Vector3 rotationalvelocity, byte state, - UUID AssetId, UUID owner, int attachPoint); + void SendPrimTerseUpdate(SendPrimitiveTerseData data); void SendInventoryFolderDetails(UUID ownerID, UUID folderID, List items, List folders, bool fetchFolders, diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 93fdeefeb4..0a7d9239cc 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -320,14 +320,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP private readonly IGroupsModule m_GroupsModule; private int m_cachedTextureSerial; - private Timer m_avatarTerseUpdateTimer; - private PriorityQueue m_avatarTerseUpdates_ = + private PriorityQueue m_avatarTerseUpdates = new PriorityQueue(); - private Timer m_primTerseUpdateTimer; - private PriorityQueue m_primTerseUpdates_ = + private PriorityQueue m_primTerseUpdates = new PriorityQueue(); - private Timer m_primFullUpdateTimer; - private PriorityQueue m_primFullUpdates_ = + private PriorityQueue m_primFullUpdates = new PriorityQueue(); private int m_moneyBalance; private int m_animationSequenceNumber = 1; @@ -353,9 +350,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // LL uses these limits, apparently. Compressed terse would be 23, but we don't have that yet protected int m_primTerseUpdatesPerPacket = 10; protected int m_primFullUpdatesPerPacket = 14; - protected int m_primTerseUpdateRate = 10; - protected int m_primFullUpdateRate = 14; - protected int m_avatarTerseUpdateRate = 50; protected int m_avatarTerseUpdatesPerPacket = 5; /// Number of texture packets to put on the queue each time the /// OnQueueEmpty event is triggered for the texture category @@ -479,25 +473,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Remove ourselves from the scene m_scene.RemoveClient(AgentId); - // Shut down timers. Thread Context of this method is murky. Lock all timers - if (m_avatarTerseUpdateTimer.Enabled) - lock (m_avatarTerseUpdateTimer) - m_avatarTerseUpdateTimer.Stop(); - if (m_primTerseUpdateTimer.Enabled) - lock (m_primTerseUpdateTimer) - m_primTerseUpdateTimer.Stop(); - if (m_primFullUpdateTimer.Enabled) - lock (m_primFullUpdateTimer) - m_primFullUpdateTimer.Stop(); - // We can't reach into other scenes and close the connection // We need to do this over grid communications //m_scene.CloseAllAgents(CircuitCode); - m_avatarTerseUpdateTimer.Dispose(); - m_primTerseUpdateTimer.Dispose(); - m_primFullUpdateTimer.Dispose(); - // Disable UDP handling for this client m_udpClient.Shutdown(); @@ -524,18 +503,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void Stop() { - // Shut down timers. Thread Context is Murky, lock all timers! - if (m_avatarTerseUpdateTimer.Enabled) - lock (m_avatarTerseUpdateTimer) - m_avatarTerseUpdateTimer.Stop(); - if (m_primTerseUpdateTimer.Enabled) - lock (m_primTerseUpdateTimer) - m_primTerseUpdateTimer.Stop(); - - if (m_primFullUpdateTimer.Enabled) - lock (m_primFullUpdateTimer) - m_primFullUpdateTimer.Stop(); } #endregion Client Methods @@ -631,18 +599,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public virtual void Start() { - m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate); - m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); - m_avatarTerseUpdateTimer.AutoReset = false; - - m_primTerseUpdateTimer = new Timer(m_primTerseUpdateRate); - m_primTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimTerseUpdates); - m_primTerseUpdateTimer.AutoReset = false; - - m_primFullUpdateTimer = new Timer(m_primFullUpdateRate); - m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates); - m_primFullUpdateTimer.AutoReset = false; - m_scene.AddNewClient(this); RefreshGroupMembership(); @@ -3394,28 +3350,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// send a objectupdate packet with information about the clients avatar /// - public void SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, - uint avatarLocalID, Vector3 Pos, byte[] textureEntry, uint parentID, Quaternion rotation) + public void SendAvatarData(SendAvatarData data) { ObjectUpdatePacket objupdate = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); // TODO: don't create new blocks if recycling an old packet - objupdate.RegionData.RegionHandle = regionHandle; + objupdate.RegionData.RegionHandle = data.regionHandle; objupdate.RegionData.TimeDilation = ushort.MaxValue; objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; - objupdate.ObjectData[0] = CreateDefaultAvatarPacket(textureEntry); + objupdate.ObjectData[0] = CreateDefaultAvatarPacket(data.textureEntry); //give this avatar object a local id and assign the user a name - objupdate.ObjectData[0].ID = avatarLocalID; - objupdate.ObjectData[0].FullID = avatarID; - objupdate.ObjectData[0].ParentID = parentID; + objupdate.ObjectData[0].ID = data.avatarLocalID; + objupdate.ObjectData[0].FullID = data.avatarID; + objupdate.ObjectData[0].ParentID = data.parentID; objupdate.ObjectData[0].NameValue = - Utils.StringToBytes("FirstName STRING RW SV " + firstName + "\nLastName STRING RW SV " + lastName + "\nTitle STRING RW SV " + grouptitle); + Utils.StringToBytes("FirstName STRING RW SV " + data.firstName + "\nLastName STRING RW SV " + data.lastName + "\nTitle STRING RW SV " + data.grouptitle); - Vector3 pos2 = new Vector3(Pos.X, Pos.Y, Pos.Z); + Vector3 pos2 = new Vector3(data.Pos.X, data.Pos.Y, data.Pos.Z); byte[] pb = pos2.GetBytes(); Array.Copy(pb, 0, objupdate.ObjectData[0].ObjectData, 16, pb.Length); - byte[] rot = rotation.GetBytes(); + byte[] rot = data.rotation.GetBytes(); Array.Copy(rot, 0, objupdate.ObjectData[0].ObjectData, 52, rot.Length); objupdate.Header.Zerocoded = true; @@ -3426,38 +3381,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Send a terse positional/rotation/velocity update about an avatar /// to the client. This avatar can be that of the client itself. /// - public virtual void SendAvatarTerseUpdate(ulong regionHandle, - ushort timeDilation, uint localID, Vector3 position, - Vector3 velocity, Quaternion rotation, UUID agentid) + public virtual void SendAvatarTerseUpdate(SendAvatarTerseData data) { + if (data.priority == double.NaN) + { + m_log.Error("[LLClientView] SendAvatarTerseUpdate received a NaN priority, dropping update"); + return; + } + + Quaternion rotation = data.rotation; + if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) rotation = Quaternion.Identity; ImprovedTerseObjectUpdatePacket.ObjectDataBlock terseBlock = - CreateAvatarImprovedBlock(localID, position, velocity,rotation); + CreateAvatarImprovedBlock(data.localID, data.position, data.velocity, rotation); - lock (m_avatarTerseUpdates_.SyncRoot) - { - m_avatarTerseUpdates_.Enqueue(DateTime.Now.ToOADate(), terseBlock, localID); - - // If packet is full or own movement packet, send it. - if (m_avatarTerseUpdates_.Count >= m_avatarTerseUpdatesPerPacket) - { - ProcessAvatarTerseUpdates(this, null); - } - else if (m_avatarTerseUpdates_.Count == 1) - { - lock (m_avatarTerseUpdateTimer) - m_avatarTerseUpdateTimer.Start(); - } - } + lock (m_avatarTerseUpdates.SyncRoot) + m_avatarTerseUpdates.Enqueue(data.priority, terseBlock, data.localID); } - private void ProcessAvatarTerseUpdates(object sender, ElapsedEventArgs e) + private void ProcessAvatarTerseUpdates() { - lock (m_avatarTerseUpdates_.SyncRoot) + lock (m_avatarTerseUpdates.SyncRoot) { ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); @@ -3468,8 +3416,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP (ushort)(Scene.TimeDilation * ushort.MaxValue); int max = m_avatarTerseUpdatesPerPacket; - if (max > m_avatarTerseUpdates_.Count) - max = m_avatarTerseUpdates_.Count; + if (max > m_avatarTerseUpdates.Count) + max = m_avatarTerseUpdates.Count; int count = 0; int size = 0; @@ -3482,12 +3430,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (count = 0 ; count < max ; count++) { int length = 0; - m_avatarTerseUpdates_.Peek().ToBytes(blockbuffer, ref length); + m_avatarTerseUpdates.Peek().ToBytes(blockbuffer, ref length); length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); if (size + length > Packet.MTU) break; size += length; - updates.Enqueue(m_avatarTerseUpdates_.Dequeue()); + updates.Enqueue(m_avatarTerseUpdates.Dequeue()); } terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; @@ -3497,14 +3445,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP terse.Header.Reliable = false; terse.Header.Zerocoded = true; - // FIXME: Move this to ThrottleOutPacketType.State when the real prioritization code is committed - OutPacket(terse, ThrottleOutPacketType.Task); - if (m_avatarTerseUpdates_.Count == 0) - { - lock (m_avatarTerseUpdateTimer) - m_avatarTerseUpdateTimer.Stop(); - } + OutPacket(terse, ThrottleOutPacketType.State); } } @@ -3569,54 +3511,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(attach, ThrottleOutPacketType.Task); } - public void SendPrimitiveToClient( - ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, - Vector3 pos, Vector3 vel, Vector3 acc, Quaternion rotation, Vector3 rvel, - uint flags, UUID objectID, UUID ownerID, string text, byte[] color, - uint parentID, byte[] particleSystem, byte clickAction, byte material) + public void SendPrimitiveToClient(SendPrimitiveData data) { - byte[] textureanim = new byte[0]; - - SendPrimitiveToClient(regionHandle, timeDilation, localID, primShape, pos, vel, - acc, rotation, rvel, flags, - objectID, ownerID, text, color, parentID, particleSystem, - clickAction, material, textureanim, false, 0, UUID.Zero, UUID.Zero, 0, 0, 0); - } - - public void SendPrimitiveToClient( - ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, - Vector3 pos, Vector3 velocity, Vector3 acceleration, Quaternion rotation, Vector3 rotational_velocity, - uint flags, - UUID objectID, UUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, - byte clickAction, byte material, byte[] textureanim, bool attachment, uint AttachPoint, UUID AssetId, UUID SoundId, double SoundGain, byte SoundFlags, double SoundRadius) - { - - if (AttachPoint > 30 && ownerID != AgentId) // Someone else's HUD + if (data.priority == double.NaN) + { + m_log.Error("[LLClientView] SendPrimitiveToClient received a NaN priority, dropping update"); return; - if (primShape.PCode == 9 && primShape.State != 0 && parentID == 0) + } + + Quaternion rotation = data.rotation; + + if (data.AttachPoint > 30 && data.ownerID != AgentId) // Someone else's HUD + return; + if (data.primShape.PCode == 9 && data.primShape.State != 0 && data.parentID == 0) return; - if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) + if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0.0f) rotation = Quaternion.Identity; - ObjectUpdatePacket.ObjectDataBlock objectData = CreatePrimUpdateBlock(primShape, flags); + ObjectUpdatePacket.ObjectDataBlock objectData = CreatePrimUpdateBlock(data.primShape, data.flags); - objectData.ID = localID; - objectData.FullID = objectID; - objectData.OwnerID = ownerID; + objectData.ID = data.localID; + objectData.FullID = data.objectID; + objectData.OwnerID = data.ownerID; - objectData.Text = Util.StringToBytes256(text); - objectData.TextColor[0] = color[0]; - objectData.TextColor[1] = color[1]; - objectData.TextColor[2] = color[2]; - objectData.TextColor[3] = color[3]; - objectData.ParentID = parentID; - objectData.PSBlock = particleSystem; - objectData.ClickAction = clickAction; - objectData.Material = material; + objectData.Text = Util.StringToBytes256(data.text); + objectData.TextColor[0] = data.color[0]; + objectData.TextColor[1] = data.color[1]; + objectData.TextColor[2] = data.color[2]; + objectData.TextColor[3] = data.color[3]; + objectData.ParentID = data.parentID; + objectData.PSBlock = data.particleSystem; + objectData.ClickAction = data.clickAction; + objectData.Material = data.material; objectData.Flags = 0; - if (attachment) + if (data.attachment) { // Necessary??? objectData.JointAxisOrAnchor = new Vector3(0, 0, 2); @@ -3624,14 +3554,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Item from inventory??? objectData.NameValue = - Utils.StringToBytes("AttachItemID STRING RW SV " + AssetId.Guid); - objectData.State = (byte)((AttachPoint % 16) * 16 + (AttachPoint / 16)); + Utils.StringToBytes("AttachItemID STRING RW SV " + data.AssetId.Guid); + objectData.State = (byte)((data.AttachPoint % 16) * 16 + (data.AttachPoint / 16)); } // Xantor 20080528: Send sound info as well // Xantor 20080530: Zero out everything if there's no SoundId, so zerocompression will work again - objectData.Sound = SoundId; - if (SoundId == UUID.Zero) + objectData.Sound = data.SoundId; + if (data.SoundId == UUID.Zero) { objectData.OwnerID = UUID.Zero; objectData.Gain = 0.0f; @@ -3640,39 +3570,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - objectData.OwnerID = ownerID; - objectData.Gain = (float)SoundGain; - objectData.Radius = (float)SoundRadius; - objectData.Flags = SoundFlags; + objectData.OwnerID = data.ownerID; + objectData.Gain = (float)data.SoundVolume; + objectData.Radius = (float)data.SoundRadius; + objectData.Flags = data.SoundFlags; } - byte[] pb = pos.GetBytes(); - Array.Copy(pb, 0, objectData.ObjectData, 0, pb.Length); + byte[] pb = data.pos.GetBytes(); + Buffer.BlockCopy(pb, 0, objectData.ObjectData, 0, pb.Length); - byte[] vel = velocity.GetBytes(); - Array.Copy(vel, 0, objectData.ObjectData, pb.Length, vel.Length); + byte[] vel = data.vel.GetBytes(); + Buffer.BlockCopy(vel, 0, objectData.ObjectData, pb.Length, vel.Length); byte[] rot = rotation.GetBytes(); - Array.Copy(rot, 0, objectData.ObjectData, 36, rot.Length); + Buffer.BlockCopy(rot, 0, objectData.ObjectData, 36, rot.Length); - byte[] rvel = rotational_velocity.GetBytes(); - Array.Copy(rvel, 0, objectData.ObjectData, 36 + rot.Length, rvel.Length); + byte[] rvel = data.rvel.GetBytes(); + Buffer.BlockCopy(rvel, 0, objectData.ObjectData, 36 + rot.Length, rvel.Length); - if (textureanim.Length > 0) + if (data.textureanim.Length > 0) { - objectData.TextureAnim = textureanim; + objectData.TextureAnim = data.textureanim; } - lock (m_primFullUpdates_.SyncRoot) - { - if (m_primFullUpdates_.Count == 0) - m_primFullUpdateTimer.Start(); - - m_primFullUpdates_.Enqueue(DateTime.Now.ToOADate(), objectData, localID); - - if (m_primFullUpdates_.Count >= m_primFullUpdatesPerPacket) - ProcessPrimFullUpdates(this, null); - } + lock (m_primFullUpdates.SyncRoot) + m_primFullUpdates.Enqueue(data.priority, objectData, data.localID); } void HandleQueueEmpty(ThrottleOutPacketType queue) @@ -3682,6 +3604,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP case ThrottleOutPacketType.Texture: ProcessTextureRequests(); break; + case ThrottleOutPacketType.State: + int count = 0; + + lock (m_avatarTerseUpdates.SyncRoot) + count = m_avatarTerseUpdates.Count; + if (count > 0) + { + ProcessAvatarTerseUpdates(); + return; + } + + lock (m_primFullUpdates.SyncRoot) + count = m_primFullUpdates.Count; + if (count > 0) + { + ProcessPrimFullUpdates(); + return; + } + + lock (m_primTerseUpdates.SyncRoot) + count = m_primTerseUpdates.Count; + if (count > 0) + { + ProcessPrimTerseUpdates(); + return; + } + break; } } @@ -3691,18 +3640,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_imageManager.ProcessImageQueue(m_textureSendLimit); } - void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e) + void ProcessPrimFullUpdates() { - lock (m_primFullUpdates_.SyncRoot) + lock (m_primFullUpdates.SyncRoot) { - if (m_primFullUpdates_.Count == 0 && m_primFullUpdateTimer.Enabled) - { - lock (m_primFullUpdateTimer) - m_primFullUpdateTimer.Stop(); - - return; - } - ObjectUpdatePacket outPacket = (ObjectUpdatePacket)PacketPool.Instance.GetPacket( PacketType.ObjectUpdate); @@ -3712,7 +3653,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); - int max = m_primFullUpdates_.Count; + int max = m_primFullUpdates.Count; if (max > m_primFullUpdatesPerPacket) max = m_primFullUpdatesPerPacket; @@ -3727,12 +3668,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (count = 0 ; count < max ; count++) { int length = 0; - m_primFullUpdates_.Peek().ToBytes(blockbuffer, ref length); + m_primFullUpdates.Peek().ToBytes(blockbuffer, ref length); length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); if (size + length > Packet.MTU) break; size += length; - updates.Enqueue(m_primFullUpdates_.Dequeue()); + updates.Enqueue(m_primFullUpdates.Dequeue()); } outPacket.ObjectData = @@ -3743,53 +3684,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP outPacket.Header.Zerocoded = true; OutPacket(outPacket, ThrottleOutPacketType.State); - - if (m_primFullUpdates_.Count == 0 && m_primFullUpdateTimer.Enabled) - lock (m_primFullUpdateTimer) - m_primFullUpdateTimer.Stop(); } } /// /// /// - public void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, - Quaternion rotation, Vector3 velocity, Vector3 rotationalvelocity, byte state, UUID AssetId, UUID ownerID, int attachPoint) + //public void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, + // Quaternion rotation, Vector3 velocity, Vector3 rotationalvelocity, byte state, UUID AssetId, UUID ownerID, int attachPoint) + public void SendPrimTerseUpdate(SendPrimitiveTerseData data) { - if (attachPoint > 30 && ownerID != AgentId) // Someone else's HUD + if (data.priority == double.NaN) + { + m_log.Error("[LLClientView] SendPrimTerseUpdate received a NaN priority, dropping update"); + return; + } + + Quaternion rotation = data.rotation; + + if (data.attachPoint > 30 && data.owner != AgentId) // Someone else's HUD return; if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) rotation = Quaternion.Identity; ImprovedTerseObjectUpdatePacket.ObjectDataBlock objectData = - CreatePrimImprovedBlock(localID, position, rotation, - velocity, rotationalvelocity, state); + CreatePrimImprovedBlock(data.localID, data.position, rotation, + data.velocity, data.rotationalvelocity, data.state); - lock (m_primTerseUpdates_.SyncRoot) - { - if (m_primTerseUpdates_.Count == 0) - m_primTerseUpdateTimer.Start(); - - m_primTerseUpdates_.Enqueue(DateTime.Now.ToOADate(), objectData, localID); - - if (m_primTerseUpdates_.Count >= m_primTerseUpdatesPerPacket) - ProcessPrimTerseUpdates(this, null); - } + lock (m_primTerseUpdates.SyncRoot) + m_primTerseUpdates.Enqueue(data.priority, objectData, data.localID); } - void ProcessPrimTerseUpdates(object sender, ElapsedEventArgs e) + void ProcessPrimTerseUpdates() { - lock (m_primTerseUpdates_.SyncRoot) + lock (m_primTerseUpdates.SyncRoot) { - if (m_primTerseUpdates_.Count == 0) - { - lock (m_primTerseUpdateTimer) - m_primTerseUpdateTimer.Stop(); - - return; - } - ImprovedTerseObjectUpdatePacket outPacket = (ImprovedTerseObjectUpdatePacket) PacketPool.Instance.GetPacket( @@ -3800,7 +3730,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); - int max = m_primTerseUpdates_.Count; + int max = m_primTerseUpdates.Count; if (max > m_primTerseUpdatesPerPacket) max = m_primTerseUpdatesPerPacket; @@ -3815,12 +3745,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (count = 0 ; count < max ; count++) { int length = 0; - m_primTerseUpdates_.Peek().ToBytes(blockbuffer, ref length); + m_primTerseUpdates.Peek().ToBytes(blockbuffer, ref length); length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); if (size + length > Packet.MTU) break; size += length; - updates.Enqueue(m_primTerseUpdates_.Dequeue()); + updates.Enqueue(m_primTerseUpdates.Dequeue()); } outPacket.ObjectData = @@ -3833,26 +3763,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP outPacket.Header.Reliable = false; outPacket.Header.Zerocoded = true; OutPacket(outPacket, ThrottleOutPacketType.State); - - if (m_primTerseUpdates_.Count == 0) - lock (m_primTerseUpdateTimer) - m_primTerseUpdateTimer.Stop(); } } public void FlushPrimUpdates() { - while (m_primFullUpdates_.Count > 0) + while (m_primFullUpdates.Count > 0) { - ProcessPrimFullUpdates(this, null); + ProcessPrimFullUpdates(); } - while (m_primTerseUpdates_.Count > 0) + while (m_primTerseUpdates.Count > 0) { - ProcessPrimTerseUpdates(this, null); + ProcessPrimTerseUpdates(); } - while (m_avatarTerseUpdates_.Count > 0) + while (m_avatarTerseUpdates.Count > 0) { - ProcessAvatarTerseUpdates(this, null); + ProcessAvatarTerseUpdates(); } } diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index d651fd413f..3799a02f84 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -499,13 +499,11 @@ namespace OpenSim.Region.Examples.SimpleModule { } - public virtual void SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, - uint avatarLocalID, Vector3 Pos, byte[] textureEntry, uint parentID, Quaternion rotation) + public virtual void SendAvatarData(SendAvatarData data) { } - public virtual void SendAvatarTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, - Vector3 position, Vector3 velocity, Quaternion rotation, UUID agentid) + public virtual void SendAvatarTerseUpdate(SendAvatarTerseData data) { } @@ -521,27 +519,11 @@ namespace OpenSim.Region.Examples.SimpleModule { } - public virtual void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, - PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, - Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, - UUID objectID, UUID ownerID, string text, byte[] color, - uint parentID, - byte[] particleSystem, byte clickAction, byte material) + public virtual void SendPrimitiveToClient(SendPrimitiveData data) { } - public virtual void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, - PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, - Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, - UUID objectID, UUID ownerID, string text, byte[] color, - uint parentID, - byte[] particleSystem, byte clickAction, byte material, byte[] textureanimation, - bool attachment, uint AttachmentPoint, UUID AssetId, UUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius) - { - } - public virtual void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, - Vector3 position, Quaternion rotation, Vector3 velocity, - Vector3 rotationalvelocity, byte state, UUID AssetId, - UUID ownerID, int attachPoint) + + public virtual void SendPrimTerseUpdate(SendPrimitiveTerseData data) { } diff --git a/OpenSim/Region/Framework/Interfaces/ISceneViewer.cs b/OpenSim/Region/Framework/Interfaces/ISceneViewer.cs index 8e3f4a0e0d..7251d57f69 100644 --- a/OpenSim/Region/Framework/Interfaces/ISceneViewer.cs +++ b/OpenSim/Region/Framework/Interfaces/ISceneViewer.cs @@ -34,7 +34,6 @@ namespace OpenSim.Region.Framework.Interfaces { void Reset(); void Close(); - int MaxPrimsPerFrame { get; set; } void QueuePartForUpdate(SceneObjectPart part); void SendPrimUpdates(); } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index d13d4fb8ec..c7efc19a49 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -57,6 +57,12 @@ namespace OpenSim.Region.Framework.Scenes public partial class Scene : SceneBase { + public enum UpdatePrioritizationSchemes { + Time = 0, + Distance = 1, + SimpleAngularDistance = 2, + } + public delegate void SynchronizeSceneHandler(Scene scene); public SynchronizeSceneHandler SynchronizeScene = null; @@ -268,9 +274,10 @@ namespace OpenSim.Region.Framework.Scenes private volatile bool shuttingdown = false; private int m_lastUpdate = Environment.TickCount; - private int m_maxPrimsPerFrame = 200; private bool m_firstHeartbeat = true; + private UpdatePrioritizationSchemes m_update_prioritization_scheme = UpdatePrioritizationSchemes.Time; + private object m_deleting_scene_object = new object(); // the minimum time that must elapse before a changed object will be considered for persisted @@ -282,6 +289,8 @@ namespace OpenSim.Region.Framework.Scenes #region Properties + public UpdatePrioritizationSchemes UpdatePrioritizationScheme { get { return this.m_update_prioritization_scheme; } } + public AgentCircuitManager AuthenticateHandler { get { return m_authenticateHandler; } @@ -326,12 +335,6 @@ namespace OpenSim.Region.Framework.Scenes get { return m_sceneGraph.m_syncRoot; } } - public int MaxPrimsPerFrame - { - get { return m_maxPrimsPerFrame; } - set { m_maxPrimsPerFrame = value; } - } - /// /// This is for llGetRegionFPS /// @@ -509,7 +512,6 @@ namespace OpenSim.Region.Framework.Scenes m_defaultScriptEngine = startupConfig.GetString("DefaultScriptEngine", "DotNetEngine"); - m_maxPrimsPerFrame = startupConfig.GetInt("MaxPrimsPerFrame", 200); IConfig packetConfig = m_config.Configs["PacketPool"]; if (packetConfig != null) { @@ -518,6 +520,28 @@ namespace OpenSim.Region.Framework.Scenes } m_strictAccessControl = startupConfig.GetBoolean("StrictAccessControl", m_strictAccessControl); + + IConfig interest_management_config = m_config.Configs["InterestManagement"]; + if (interest_management_config != null) + { + string update_prioritization_scheme = interest_management_config.GetString("UpdatePrioritizationScheme", "Time").Trim().ToLower(); + switch (update_prioritization_scheme) + { + case "time": + m_update_prioritization_scheme = UpdatePrioritizationSchemes.Time; + break; + case "distance": + m_update_prioritization_scheme = UpdatePrioritizationSchemes.Distance; + break; + case "simpleangulardistance": + m_update_prioritization_scheme = UpdatePrioritizationSchemes.SimpleAngularDistance; + break; + default: + m_log.Warn("[SCENE]: UpdatePrioritizationScheme was not recognized, setting to default settomg of Time"); + m_update_prioritization_scheme = UpdatePrioritizationSchemes.Time; + break; + } + } } catch { diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 04397adcec..b9872ca0cf 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -613,7 +613,6 @@ namespace OpenSim.Region.Framework.Scenes newAvatar = new ScenePresence(client, m_parentScene, m_regInfo, appearance); newAvatar.IsChildAgent = true; - newAvatar.MaxPrimsPerFrame = m_parentScene.MaxPrimsPerFrame; AddScenePresence(newAvatar); diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index d4cef7dfaa..2153b9b8cb 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -1817,7 +1817,7 @@ namespace OpenSim.Region.Framework.Scenes public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags) { - remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.ObjectOwner, RootPart.GroupID, RootPart.BaseMask, + 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); @@ -3343,5 +3343,77 @@ namespace OpenSim.Region.Framework.Scenes return true; } + + public double GetUpdatePriority(IClientAPI client) + { + switch (Scene.UpdatePrioritizationScheme) + { + case Scene.UpdatePrioritizationSchemes.Time: + return GetPriorityByTime(); + case Scene.UpdatePrioritizationSchemes.Distance: + return GetPriorityByDistance(client); + case Scene.UpdatePrioritizationSchemes.SimpleAngularDistance: + return GetPriorityBySimpleAngularDistance(client); + default: + throw new InvalidOperationException("UpdatePrioritizationScheme not defined"); + } + } + + private double GetPriorityByTime() + { + return DateTime.Now.ToOADate(); + } + + private double GetPriorityByDistance(IClientAPI client) + { + ScenePresence presence = Scene.GetScenePresence(client.AgentId); + if (presence != null) + { + return GetPriorityByDistance((presence.IsChildAgent) ? + presence.AbsolutePosition : presence.CameraPosition); + } + return double.NaN; + } + + private double GetPriorityBySimpleAngularDistance(IClientAPI client) + { + ScenePresence presence = Scene.GetScenePresence(client.AgentId); + if (presence != null) + { + return GetPriorityBySimpleAngularDistance((presence.IsChildAgent) ? + presence.AbsolutePosition : presence.CameraPosition); + } + return double.NaN; + } + + public double GetPriorityByDistance(Vector3 position) + { + return Vector3.Distance(AbsolutePosition, position); + } + + public double GetPriorityBySimpleAngularDistance(Vector3 position) + { + double distance = Vector3.Distance(position, AbsolutePosition); + if (distance >= double.Epsilon) + { + float height; + Vector3 box = GetAxisAlignedBoundingBox(out height); + + double angle = box.X / distance; + double max = angle; + + angle = box.Y / distance; + if (max < angle) + max = angle; + + angle = box.Z / distance; + if (max < angle) + max = angle; + + return -max; + } + else + return double.MinValue; + } } } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 377cb6e987..79f6366691 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2400,10 +2400,10 @@ if (m_shape != null) { //isattachment = ParentGroup.RootPart.IsAttachment; byte[] color = new byte[] {m_color.R, m_color.G, m_color.B, m_color.A}; - remoteClient.SendPrimitiveToClient(m_regionHandle, (ushort)(m_parentGroup.GetTimeDilation() * (float)ushort.MaxValue), LocalId, m_shape, + remoteClient.SendPrimitiveToClient(new SendPrimitiveData(m_regionHandle, (ushort)(m_parentGroup.GetTimeDilation() * (float)ushort.MaxValue), LocalId, m_shape, lPos, Velocity, Acceleration, RotationOffset, RotationalVelocity, clientFlags, m_uuid, _ownerID, m_text, color, _parentID, m_particleSystem, m_clickAction, (byte)m_material, m_TextureAnimation, IsAttachment, - AttachmentPoint,FromItemID, Sound, SoundGain, SoundFlags, SoundRadius); + AttachmentPoint,FromItemID, Sound, SoundGain, SoundFlags, SoundRadius, ParentGroup.GetUpdatePriority(remoteClient))); } /// @@ -3794,12 +3794,12 @@ if (m_shape != null) { // Causes this thread to dig into the Client Thread Data. // Remember your locking here! - remoteClient.SendPrimTerseUpdate(m_regionHandle, + remoteClient.SendPrimTerseUpdate(new SendPrimitiveTerseData(m_regionHandle, (ushort)(m_parentGroup.GetTimeDilation() * (float)ushort.MaxValue), LocalId, lPos, RotationOffset, Velocity, RotationalVelocity, state, FromItemID, - OwnerID, (int)AttachmentPoint); + OwnerID, (int)AttachmentPoint, ParentGroup.GetUpdatePriority(remoteClient))); } public void AddScriptLPS(int count) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 387db4456b..a5b88c6af1 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -403,12 +403,6 @@ namespace OpenSim.Region.Framework.Scenes set { m_parentPosition = value; } } - public int MaxPrimsPerFrame - { - get { return m_sceneViewer.MaxPrimsPerFrame; } - set { m_sceneViewer.MaxPrimsPerFrame = value; } - } - /// /// Absolute position of this avatar in 'region cordinates' /// @@ -2457,8 +2451,8 @@ namespace OpenSim.Region.Framework.Scenes Vector3 pos = m_pos; pos.Z -= m_appearance.HipOffset; - remoteClient.SendAvatarTerseUpdate(m_regionHandle, (ushort)(m_scene.TimeDilation * ushort.MaxValue), - LocalId, pos, Velocity, m_bodyRot, m_uuid); + remoteClient.SendAvatarTerseUpdate(new SendAvatarTerseData(m_regionHandle, (ushort)(m_scene.TimeDilation * ushort.MaxValue), LocalId, + pos, m_velocity, m_rotation, m_uuid, GetUpdatePriority(remoteClient))); m_scene.StatsReporter.AddAgentTime(Environment.TickCount - m_perfMonMS); m_scene.StatsReporter.AddAgentUpdates(1); @@ -2563,9 +2557,9 @@ namespace OpenSim.Region.Framework.Scenes Vector3 pos = m_pos; pos.Z -= m_appearance.HipOffset; - remoteAvatar.m_controllingClient.SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, + remoteAvatar.m_controllingClient.SendAvatarData(new SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, LocalId, m_pos, m_appearance.Texture.GetBytes(), - m_parentID, rot); + m_parentID, rot)); m_scene.StatsReporter.AddAgentUpdates(1); } @@ -2634,8 +2628,8 @@ namespace OpenSim.Region.Framework.Scenes Vector3 pos = m_pos; pos.Z -= m_appearance.HipOffset; - m_controllingClient.SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, LocalId, - m_pos, m_appearance.Texture.GetBytes(), m_parentID, rot); + m_controllingClient.SendAvatarData(new SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, LocalId, + m_pos, m_appearance.Texture.GetBytes(), m_parentID, rot)); if (!m_isChildAgent) { @@ -2741,8 +2735,8 @@ namespace OpenSim.Region.Framework.Scenes } Quaternion rot = m_bodyRot; - m_controllingClient.SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, LocalId, - m_pos, m_appearance.Texture.GetBytes(), m_parentID, rot); + m_controllingClient.SendAvatarData(new SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, LocalId, + m_pos, m_appearance.Texture.GetBytes(), m_parentID, rot)); } @@ -3870,5 +3864,41 @@ namespace OpenSim.Region.Framework.Scenes } } } + + public double GetUpdatePriority(IClientAPI client) + { + switch (Scene.UpdatePrioritizationScheme) + { + case Scene.UpdatePrioritizationSchemes.Time: + return GetPriorityByTime(); + case Scene.UpdatePrioritizationSchemes.Distance: + return GetPriorityByDistance(client); + case Scene.UpdatePrioritizationSchemes.SimpleAngularDistance: + return GetPriorityByDistance(client); + default: + throw new InvalidOperationException("UpdatePrioritizationScheme not defined."); + } + } + + private double GetPriorityByTime() + { + return DateTime.Now.ToOADate(); + } + + private double GetPriorityByDistance(IClientAPI client) + { + ScenePresence presence = Scene.GetScenePresence(client.AgentId); + if (presence != null) + { + return GetPriorityByDistance((presence.IsChildAgent) ? + presence.AbsolutePosition : presence.CameraPosition); + } + return double.NaN; + } + + private double GetPriorityByDistance(Vector3 position) + { + return Vector3.Distance(AbsolutePosition, position); + } } } diff --git a/OpenSim/Region/Framework/Scenes/SceneViewer.cs b/OpenSim/Region/Framework/Scenes/SceneViewer.cs index 8ab0552887..e4296ef66c 100644 --- a/OpenSim/Region/Framework/Scenes/SceneViewer.cs +++ b/OpenSim/Region/Framework/Scenes/SceneViewer.cs @@ -45,14 +45,6 @@ namespace OpenSim.Region.Framework.Scenes protected Dictionary m_updateTimes = new Dictionary(); - protected int m_maxPrimsPerFrame = 200; - - public int MaxPrimsPerFrame - { - get { return m_maxPrimsPerFrame; } - set { m_maxPrimsPerFrame = value; } - } - public SceneViewer() { } @@ -82,16 +74,7 @@ namespace OpenSim.Region.Framework.Scenes { m_pendingObjects = new Queue(); - List ents = new List(m_presence.Scene.Entities); - if (!m_presence.IsChildAgent) // Proximity sort makes no sense for - { // Child agents - ents.Sort(delegate(EntityBase a, EntityBase b) - { - return Vector3.Distance(m_presence.AbsolutePosition, a.AbsolutePosition).CompareTo(Vector3.Distance(m_presence.AbsolutePosition, b.AbsolutePosition)); - }); - } - - foreach (EntityBase e in ents) + foreach (EntityBase e in m_presence.Scene.Entities) { if (e is SceneObjectGroup) m_pendingObjects.Enqueue((SceneObjectGroup)e); @@ -99,7 +82,7 @@ namespace OpenSim.Region.Framework.Scenes } } - while (m_pendingObjects != null && m_pendingObjects.Count > 0 && m_partsUpdateQueue.Count < m_maxPrimsPerFrame) + while (m_pendingObjects != null && m_pendingObjects.Count > 0) { SceneObjectGroup g = m_pendingObjects.Dequeue(); @@ -183,8 +166,6 @@ namespace OpenSim.Region.Framework.Scenes m_presence.GenerateClientFlags(part.UUID)); } } - - m_presence.ControllingClient.FlushPrimUpdates(); } public void Reset() diff --git a/OpenSim/Region/Framework/Scenes/Scripting/IScriptHost.cs b/OpenSim/Region/Framework/Scenes/Scripting/IScriptHost.cs index 29c467233f..f3be02842a 100644 --- a/OpenSim/Region/Framework/Scenes/Scripting/IScriptHost.cs +++ b/OpenSim/Region/Framework/Scenes/Scripting/IScriptHost.cs @@ -35,8 +35,8 @@ namespace OpenSim.Region.Framework.Scenes.Scripting string Description { get; set; } UUID UUID { get; } - UUID ObjectOwner { get; } - UUID ObjectCreator { get; } + UUID OwnerID { get; } + UUID CreatorID { get; } Vector3 AbsolutePosition { get; } string SitName { get; set; } diff --git a/OpenSim/Region/Framework/Scenes/Scripting/NullScriptHost.cs b/OpenSim/Region/Framework/Scenes/Scripting/NullScriptHost.cs index af18a98335..d7198f0305 100644 --- a/OpenSim/Region/Framework/Scenes/Scripting/NullScriptHost.cs +++ b/OpenSim/Region/Framework/Scenes/Scripting/NullScriptHost.cs @@ -68,12 +68,12 @@ namespace OpenSim.Region.Framework.Scenes.Scripting get { return UUID.Zero; } } - public UUID ObjectOwner + public UUID OwnerID { get { return UUID.Zero; } } - public UUID ObjectCreator + public UUID CreatorID { get { return UUID.Zero; } } diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index a8acf0d6c6..8ad9327540 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -1011,12 +1011,12 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, uint avatarLocalID, Vector3 Pos, byte[] textureEntry, uint parentID, Quaternion rotation) + public void SendAvatarData(SendAvatarData data) { } - public void SendAvatarTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, Vector3 velocity, Quaternion rotation, UUID agentid) + public void SendAvatarTerseUpdate(SendAvatarTerseData data) { } @@ -1036,17 +1036,12 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, UUID objectID, UUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, byte clickAction, byte material, byte[] textureanim, bool attachment, uint AttachPoint, UUID AssetId, UUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius) + public void SendPrimitiveToClient(SendPrimitiveData data) { } - public void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, UUID objectID, UUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, byte clickAction, byte material) - { - - } - - public void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, Quaternion rotation, Vector3 velocity, Vector3 rotationalvelocity, byte state, UUID AssetId, UUID owner, int attachPoint) + public void SendPrimTerseUpdate(SendPrimitiveTerseData data) { } diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/SceneObjectGroupDiff.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/SceneObjectGroupDiff.cs index 0379180066..e185351615 100644 --- a/OpenSim/Region/OptionalModules/ContentManagementSystem/SceneObjectGroupDiff.cs +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/SceneObjectGroupDiff.cs @@ -188,7 +188,7 @@ namespace OpenSim.Region.OptionalModules.ContentManagement // MISC COMPARISONS (UUID, Byte) if (first.ClickAction != second.ClickAction) result |= Diff.CLICKACTION; - if (first.ObjectOwner != second.ObjectOwner) + if (first.OwnerID != second.OwnerID) result |= Diff.OBJECTOWNER; diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index f7c63ace61..6c58f2dcb4 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -588,13 +588,11 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public virtual void SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, - uint avatarLocalID, Vector3 Pos, byte[] textureEntry, uint parentID, Quaternion rotation) + public virtual void SendAvatarData(SendAvatarData data) { } - public virtual void SendAvatarTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, - Vector3 position, Vector3 velocity, Quaternion rotation, UUID agentId) + public virtual void SendAvatarTerseUpdate(SendAvatarTerseData data) { } @@ -610,26 +608,11 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public virtual void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, - PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, - Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, - UUID objectID, UUID ownerID, string text, byte[] color, - uint parentID, - byte[] particleSystem, byte clickAction, byte material) + public virtual void SendPrimitiveToClient(SendPrimitiveData data) { } - public virtual void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, - PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, - Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, - UUID objectID, UUID ownerID, string text, byte[] color, - uint parentID, - byte[] particleSystem, byte clickAction, byte material, byte[] textureanimation, - bool attachment, uint AttachmentPoint, UUID AssetId, UUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius) - { - } - public virtual void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, - Vector3 position, Quaternion rotation, Vector3 velocity, - Vector3 rotationalvelocity, byte state, UUID AssetId, UUID ownerID, int attachPoint) + + public virtual void SendPrimTerseUpdate(SendPrimitiveTerseData data) { } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index e10e612296..57b14f772c 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -2875,7 +2875,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); - return m_host.ObjectOwner.ToString(); + return m_host.OwnerID.ToString(); } public void llInstantMessage(string user, string message) @@ -5634,7 +5634,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ILandObject parcel = World.LandChannel.GetLandObject(av.AbsolutePosition.X, av.AbsolutePosition.Y); if (parcel != null) { - if (m_host.ObjectOwner == parcel.LandData.OwnerID || + if (m_host.OwnerID == parcel.LandData.OwnerID || (m_host.OwnerID == m_host.GroupID && m_host.GroupID == parcel.LandData.GroupID && parcel.LandData.IsGroupOwned) || World.Permissions.IsGod(m_host.OwnerID)) { @@ -7157,7 +7157,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); - if (land.LandData.OwnerID != m_host.ObjectOwner) + if (land.LandData.OwnerID != m_host.OwnerID) return; land.SetMusicUrl(url); @@ -7215,7 +7215,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_String llGetCreator() { m_host.AddScriptLPS(1); - return m_host.ObjectCreator.ToString(); + return m_host.CreatorID.ToString(); } public LSL_String llGetTimestamp() @@ -8396,7 +8396,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api IDialogModule dm = World.RequestModuleInterface(); if (null != dm) dm.SendUrlToUser( - new UUID(avatar_id), m_host.Name, m_host.UUID, m_host.ObjectOwner, false, message, url); + new UUID(avatar_id), m_host.Name, m_host.UUID, m_host.OwnerID, false, message, url); ConditionalScriptSleep(10000); } @@ -8411,7 +8411,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // according to the docs, this command only works if script owner and land owner are the same // lets add estate owners and gods, too, and use the generic permission check. ILandObject landObject = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); - if (!World.Permissions.CanEditParcel(m_host.ObjectOwner, landObject)) return; + if (!World.Permissions.CanEditParcel(m_host.OwnerID, landObject)) return; bool update = false; // send a ParcelMediaUpdate (and possibly change the land's media URL)? byte loop = 0; @@ -9081,9 +9081,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api Vector3 velocity = m_host.Velocity; Quaternion rotation = m_host.RotationOffset; string ownerName = String.Empty; - ScenePresence scenePresence = World.GetScenePresence(m_host.ObjectOwner); + ScenePresence scenePresence = World.GetScenePresence(m_host.OwnerID); if (scenePresence == null) - ownerName = resolveName(m_host.ObjectOwner); + ownerName = resolveName(m_host.OwnerID); else ownerName = scenePresence.Name; @@ -9108,7 +9108,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api httpHeaders["X-SecondLife-Local-Velocity"] = string.Format("({0:0.000000}, {1:0.000000}, {2:0.000000})", velocity.X, velocity.Y, velocity.Z); httpHeaders["X-SecondLife-Local-Rotation"] = string.Format("({0:0.000000}, {1:0.000000}, {2:0.000000}, {3:0.000000})", rotation.X, rotation.Y, rotation.Z, rotation.W); httpHeaders["X-SecondLife-Owner-Name"] = ownerName; - httpHeaders["X-SecondLife-Owner-Key"] = m_host.ObjectOwner.ToString(); + httpHeaders["X-SecondLife-Owner-Key"] = m_host.OwnerID.ToString(); string userAgent = config.Configs["Network"].GetString("user_agent", null); if (userAgent != null) httpHeaders["User-Agent"] = userAgent; diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 4cb4b61f71..52396b6b2d 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -1164,7 +1164,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); - if (land.LandData.OwnerID != m_host.ObjectOwner) + if (land.LandData.OwnerID != m_host.OwnerID) return; land.SetMediaUrl(url); @@ -1182,7 +1182,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); - if (land.LandData.OwnerID != m_host.ObjectOwner) + if (land.LandData.OwnerID != m_host.OwnerID) { OSSLError("osSetParcelSIPAddress: Sorry, you need to own the land to use this function"); return; diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index 5c838c5627..21606e24d0 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -592,13 +592,11 @@ namespace OpenSim.Tests.Common.Mock { } - public virtual void SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, - uint avatarLocalID, Vector3 Pos, byte[] textureEntry, uint parentID, Quaternion rotation) + public virtual void SendAvatarData(SendAvatarData data) { } - public virtual void SendAvatarTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, - Vector3 position, Vector3 velocity, Quaternion rotation, UUID agentid) + public virtual void SendAvatarTerseUpdate(SendAvatarTerseData data) { } @@ -614,27 +612,11 @@ namespace OpenSim.Tests.Common.Mock { } - public virtual void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, - PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, - Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, - UUID objectID, UUID ownerID, string text, byte[] color, - uint parentID, - byte[] particleSystem, byte clickAction, byte material) + public virtual void SendPrimitiveToClient(SendPrimitiveData data) { } - public virtual void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, - PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, - Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, - UUID objectID, UUID ownerID, string text, byte[] color, - uint parentID, - byte[] particleSystem, byte clickAction, byte material, byte[] textureanimation, - bool attachment, uint AttachmentPoint, UUID AssetId, UUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius) - { - } - public virtual void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, - Vector3 position, Quaternion rotation, Vector3 velocity, - Vector3 rotationalvelocity, byte state, UUID AssetId, - UUID ownerID, int attachPoint) + + public virtual void SendPrimTerseUpdate(SendPrimitiveTerseData data) { } From 5a4fda9dc3aca873bcf034877eed1f9c5914493f Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 11:09:18 -0700 Subject: [PATCH 04/61] Updating OpenSim.ini.example with the section required to enable a useful prioritization scheme --- OpenSim/Region/Framework/Scenes/Scene.cs | 2 ++ bin/OpenSim.ini.example | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index c7efc19a49..0d8c241906 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -542,6 +542,8 @@ namespace OpenSim.Region.Framework.Scenes break; } } + + m_log.Info("[SCENE]: Using the " + m_update_prioritization_scheme + " prioritization scheme"); } catch { diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 002745d622..ba797e6948 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -1385,6 +1385,14 @@ ; ;TextureDataLimit = 5 +[InterestManagement] + ; This section controls how state updates are prioritized for each client + UpdatePrioritizationScheme = Distance + ReprioritizeUpdate = true + RootUpdateReprioritizationDistance = 10.0 + ChildUpdateReprioritizationDistance = 20.0 + ReprioritizeUpdatesInterval = 5000.0 + ;; ;; These are defaults that are overwritten below in [Architecture]. ;; These defaults allow OpenSim to work out of the box with From a18489dc9badfccd775145f5a1a800a763d0c554 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 12:20:01 -0700 Subject: [PATCH 05/61] * Change appearance packets from State to Task. This will hopefully fix the cloud issues * Changed the throttling logic to obey the requested client bandwidth limit but also share bandwidth between some of the categories to improve throughput on high prim or heavily trafficked regions --- OpenSim/Data/MySQL/MySQLLegacyRegionData.cs | 5 ++ .../ClientStack/LindenUDP/LLClientView.cs | 2 +- .../ClientStack/LindenUDP/LLUDPClient.cs | 79 +++++++++++-------- .../ClientStack/LindenUDP/LLUDPServer.cs | 2 +- 4 files changed, 51 insertions(+), 37 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs index fe0914b29b..839ac7f073 100644 --- a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs @@ -401,6 +401,7 @@ namespace OpenSim.Data.MySQL Dictionary objects = new Dictionary(); Dictionary prims = new Dictionary(); SceneObjectGroup grp = null; + int count = 0; lock (m_Connection) { @@ -463,6 +464,10 @@ namespace OpenSim.Data.MySQL if (link != 0) prim.LinkNum = link; } + + ++count; + if (count % 5000 == 0) + m_log.Debug("[REGION DB]: Loaded " + count + " prims..."); } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 58fd2e7f9c..630b6e69de 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3124,7 +3124,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP avp.Sender.IsTrial = false; avp.Sender.ID = agentID; - OutPacket(avp, ThrottleOutPacketType.State); + OutPacket(avp, ThrottleOutPacketType.Task); } public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 4eee6b6ba6..8c42ca4358 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -170,7 +170,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP { ThrottleOutPacketType type = (ThrottleOutPacketType)i; + // 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)); } @@ -293,36 +295,54 @@ namespace OpenSim.Region.ClientStack.LindenUDP int state = (int)((float)task * STATE_TASK_PERCENTAGE); task -= state; - int ceiling = Int32.MaxValue; - if (m_defaultThrottleRates.Total != 0) - { - ceiling = m_defaultThrottleRates.Total; - if (ceiling < Packet.MTU) ceiling = Packet.MTU; - } - - resend = Utils.Clamp(resend, Packet.MTU, ceiling); - land = Utils.Clamp(land, Packet.MTU, ceiling); - wind = Utils.Clamp(wind, Packet.MTU, ceiling); - cloud = Utils.Clamp(cloud, Packet.MTU, ceiling); - task = Utils.Clamp(task, Packet.MTU, ceiling); - texture = Utils.Clamp(texture, Packet.MTU, ceiling); - asset = Utils.Clamp(asset, Packet.MTU, ceiling); - state = Utils.Clamp(state, Packet.MTU, ceiling); + // Make sure none of the throttles are set below our packet MTU, + // otherwise a throttle could become permanently clogged + resend = Math.Max(resend, Packet.MTU); + land = Math.Max(land, Packet.MTU); + wind = Math.Max(wind, Packet.MTU); + cloud = Math.Max(cloud, Packet.MTU); + task = Math.Max(task, Packet.MTU); + texture = Math.Max(texture, Packet.MTU); + asset = Math.Max(asset, Packet.MTU); + state = Math.Max(state, Packet.MTU); int total = resend + land + wind + cloud + task + texture + asset + state; - int taskTotal = task + state; m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}", AgentID, resend, land, wind, cloud, task, texture, asset, state, total); - SetThrottle(ThrottleOutPacketType.Resend, resend, resend); - SetThrottle(ThrottleOutPacketType.Land, land, land); - SetThrottle(ThrottleOutPacketType.Wind, wind, wind); - SetThrottle(ThrottleOutPacketType.Cloud, cloud, cloud); - SetThrottle(ThrottleOutPacketType.Task, task, taskTotal); - SetThrottle(ThrottleOutPacketType.Texture, texture, texture); - SetThrottle(ThrottleOutPacketType.Asset, asset, asset); - SetThrottle(ThrottleOutPacketType.State, state, taskTotal); + // Update the token buckets with new throttle values + TokenBucket bucket; + + bucket = m_throttle; + bucket.MaxBurst = total; + + bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; + bucket.DripRate = bucket.MaxBurst = resend; + + bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; + bucket.DripRate = bucket.MaxBurst = land; + + bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; + bucket.DripRate = bucket.MaxBurst = wind; + + bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; + bucket.DripRate = bucket.MaxBurst = cloud; + + bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; + bucket.DripRate = bucket.MaxBurst = asset; + + bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; + bucket.DripRate = task + state + texture; + bucket.MaxBurst = task + state + texture; + + bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; + bucket.DripRate = state + texture; + bucket.MaxBurst = state + texture; + + bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; + bucket.DripRate = texture; + bucket.MaxBurst = texture; } public byte[] GetThrottlesPacked() @@ -342,17 +362,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return data; } - public void SetThrottle(ThrottleOutPacketType category, int rate, int maxBurst) - { - int i = (int)category; - if (i >= 0 && i < m_throttleCategories.Length) - { - TokenBucket bucket = m_throttleCategories[(int)category]; - bucket.DripRate = rate; - bucket.MaxBurst = maxBurst; - } - } - public bool EnqueueOutgoing(OutgoingPacket packet) { int category = (int)packet.Category; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 545a0bcfc7..ee3e754d06 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -513,7 +513,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP IClientAPI client; if (!m_scene.ClientManager.TryGetValue(address, out client) || !(client is LLClientView)) { - m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + + m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_scene.ClientManager.Count + " clients"); return; } From e776dfb1d71ea7f8de37399f84fceb005870e861 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 13:22:45 -0700 Subject: [PATCH 06/61] * Changing the "clean dropped attachments" MySQL command to a using statement inside a try/catch. This statement times out for me very frequently * More verbose logging when zerocoding fails on an outbound packet --- OpenSim/Data/MySQL/MySQLLegacyRegionData.cs | 20 +++++++++++++------ .../ClientStack/LindenUDP/LLUDPServer.cs | 3 ++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs index 839ac7f073..6bc8beceff 100644 --- a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs @@ -68,12 +68,20 @@ namespace OpenSim.Data.MySQL // Clean dropped attachments // - MySqlCommand cmd = m_Connection.CreateCommand(); - cmd.CommandText = "delete from prims, primshapes using prims " + - "left join primshapes on prims.uuid = primshapes.uuid " + - "where PCode = 9 and State <> 0"; - ExecuteNonQuery(cmd); - cmd.Dispose(); + try + { + using (MySqlCommand cmd = m_Connection.CreateCommand()) + { + cmd.CommandText = "delete from prims, primshapes using prims " + + "left join primshapes on prims.uuid = primshapes.uuid " + + "where PCode = 9 and State <> 0"; + ExecuteNonQuery(cmd); + } + } + catch (MySqlException ex) + { + m_log.Error("[REGION DB]: Error cleaning up dropped attachments: " + ex.Message); + } } private IDataReader ExecuteReader(MySqlCommand c) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index ee3e754d06..b11a80dd23 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -285,7 +285,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // The packet grew larger than the bufferSize while zerocoding. // Remove the MSG_ZEROCODED flag and send the unencoded data // instead - m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". Removing MSG_ZEROCODED flag"); + m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength + + " and BufferLength=" + buffer.Data.Length + ". Removing MSG_ZEROCODED flag"); data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } From 1bd9202f2439ac73a70fa2a881f824797f61f589 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 14:17:13 -0700 Subject: [PATCH 07/61] * Simplified the prioritization packet creation code to reduce CPU usage and increase throughput. Apologies to Jim for hacking on your code while it's only halfway done, I'll take responsibility for the manual merge * Changed LLUDP to use its own MTU value of 1400 instead of the 1200 value pulled from the currently shipped libomv --- .../ClientStack/LindenUDP/LLClientView.cs | 101 +++--------------- .../ClientStack/LindenUDP/LLUDPClient.cs | 20 ++-- .../ClientStack/LindenUDP/LLUDPServer.cs | 9 +- 3 files changed, 35 insertions(+), 95 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 630b6e69de..383eac036b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -347,10 +347,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected Dictionary m_groupPowers = new Dictionary(); protected int m_terrainCheckerCount; - // LL uses these limits, apparently. Compressed terse would be 23, but we don't have that yet - protected int m_primTerseUpdatesPerPacket = 10; - protected int m_primFullUpdatesPerPacket = 14; - protected int m_avatarTerseUpdatesPerPacket = 5; + // These numbers are guesses at a decent tradeoff between responsiveness + // of the interest list and throughput. Lower is more responsive, higher + // is better throughput + protected int m_primTerseUpdatesPerPacket = 25; + protected int m_primFullUpdatesPerPacket = 100; + protected int m_avatarTerseUpdatesPerPacket = 10; /// Number of texture packets to put on the queue each time the /// OnQueueEmpty event is triggered for the texture category protected int m_textureSendLimit = 20; @@ -3415,33 +3417,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP terse.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); - int max = m_avatarTerseUpdatesPerPacket; - if (max > m_avatarTerseUpdates.Count) - max = m_avatarTerseUpdates.Count; - - int count = 0; - int size = 0; - - byte[] zerobuffer = new byte[1024]; - byte[] blockbuffer = new byte[1024]; - - Queue updates = new Queue(); - - for (count = 0 ; count < max ; count++) - { - int length = 0; - m_avatarTerseUpdates.Peek().ToBytes(blockbuffer, ref length); - length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); - if (size + length > Packet.MTU) - break; - size += length; - updates.Enqueue(m_avatarTerseUpdates.Dequeue()); - } + int count = Math.Min(m_avatarTerseUpdates.Count, m_avatarTerseUpdatesPerPacket); terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; - - for (int i = 0 ; i < count ; i++) - terse.ObjectData[i] = updates.Dequeue(); + for (int i = 0; i < count; i++) + terse.ObjectData[i] = m_avatarTerseUpdates.Dequeue(); terse.Header.Reliable = false; terse.Header.Zerocoded = true; @@ -3656,34 +3636,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); - int max = m_primFullUpdates.Count; - if (max > m_primFullUpdatesPerPacket) - max = m_primFullUpdatesPerPacket; + int count = Math.Min(m_primFullUpdates.Count, m_primFullUpdatesPerPacket); - int count = 0; - int size = 0; - - byte[] zerobuffer = new byte[1024]; - byte[] blockbuffer = new byte[1024]; - - Queue updates = new Queue(); - - for (count = 0 ; count < max ; count++) - { - int length = 0; - m_primFullUpdates.Peek().ToBytes(blockbuffer, ref length); - length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); - if (size + length > Packet.MTU) - break; - size += length; - updates.Enqueue(m_primFullUpdates.Dequeue()); - } - - outPacket.ObjectData = - new ObjectUpdatePacket.ObjectDataBlock[count]; - - for (int index = 0 ; index < count ; index++) - outPacket.ObjectData[index] = updates.Dequeue(); + outPacket.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[count]; + for (int i = 0; i < count; i++) + outPacket.ObjectData[i] = m_primFullUpdates.Dequeue(); outPacket.Header.Zerocoded = true; OutPacket(outPacket, ThrottleOutPacketType.State); @@ -3733,35 +3690,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); - int max = m_primTerseUpdates.Count; - if (max > m_primTerseUpdatesPerPacket) - max = m_primTerseUpdatesPerPacket; + int count = Math.Min(m_primTerseUpdates.Count, m_primTerseUpdatesPerPacket); - int count = 0; - int size = 0; - - byte[] zerobuffer = new byte[1024]; - byte[] blockbuffer = new byte[1024]; - - Queue updates = new Queue(); - - for (count = 0 ; count < max ; count++) - { - int length = 0; - m_primTerseUpdates.Peek().ToBytes(blockbuffer, ref length); - length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); - if (size + length > Packet.MTU) - break; - size += length; - updates.Enqueue(m_primTerseUpdates.Dequeue()); - } - - outPacket.ObjectData = - new ImprovedTerseObjectUpdatePacket. - ObjectDataBlock[count]; - - for (int index = 0 ; index < count ; index++) - outPacket.ObjectData[index] = updates.Dequeue(); + outPacket.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; + for (int i = 0; i < count; i++) + outPacket.ObjectData[i] = m_primTerseUpdates.Dequeue(); outPacket.Header.Reliable = false; outPacket.Header.Zerocoded = true; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 8c42ca4358..9476eed5dc 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -297,14 +297,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Make sure none of the throttles are set below our packet MTU, // otherwise a throttle could become permanently clogged - resend = Math.Max(resend, Packet.MTU); - land = Math.Max(land, Packet.MTU); - wind = Math.Max(wind, Packet.MTU); - cloud = Math.Max(cloud, Packet.MTU); - task = Math.Max(task, Packet.MTU); - texture = Math.Max(texture, Packet.MTU); - asset = Math.Max(asset, Packet.MTU); - state = Math.Max(state, Packet.MTU); + resend = Math.Max(resend, LLUDPServer.MTU); + land = Math.Max(land, LLUDPServer.MTU); + wind = Math.Max(wind, LLUDPServer.MTU); + cloud = Math.Max(cloud, LLUDPServer.MTU); + task = Math.Max(task, LLUDPServer.MTU); + texture = Math.Max(texture, LLUDPServer.MTU); + asset = Math.Max(asset, LLUDPServer.MTU); + state = Math.Max(state, LLUDPServer.MTU); int total = resend + land + wind + cloud + task + texture + asset + state; @@ -404,9 +404,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP TokenBucket bucket; bool packetSent = false; + //string queueDebugOutput = String.Empty; // Serious debug business + for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) { bucket = m_throttleCategories[i]; + //queueDebugOutput += m_packetOutboxes[i].Count + " "; // Serious debug business if (m_nextPackets[i] != null) { @@ -458,6 +461,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } + //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business return packetSent; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index b11a80dd23..f2b8720f95 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -89,6 +89,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public class LLUDPServer : OpenSimUDPBase { + /// Maximum transmission unit, or UDP packet size, for the LLUDP protocol + public const int MTU = 1400; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); /// Handlers for incoming packets @@ -272,7 +275,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here // to accomodate for both common scenarios and provide ample room for ACK appending in both - int bufferSize = (dataLength > 180) ? Packet.MTU : 200; + int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); @@ -569,9 +572,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP // client.BytesSinceLastACK. Lockless thread safety int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); bytesSinceLastACK += buffer.DataLength; - if (bytesSinceLastACK > Packet.MTU * 2) + if (bytesSinceLastACK > LLUDPServer.MTU * 2) { - bytesSinceLastACK -= Packet.MTU * 2; + bytesSinceLastACK -= LLUDPServer.MTU * 2; SendAcks(udpClient); } Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); From 31dfe87570c952378060179139249af39290473f Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 14:26:58 -0700 Subject: [PATCH 08/61] Prevent oversized packets from crashing the LLUDP server. It will now print a friendly error message and drop the packet --- .../ClientStack/LindenUDP/LLUDPServer.cs | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index f2b8720f95..8bfab6a107 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -270,6 +270,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { int dataLength = data.Length; bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; + bool doCopy = true; // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting @@ -282,7 +283,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Zerocode if needed if (doZerocode) { - try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); } + try + { + dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); + doCopy = false; + } catch (IndexOutOfRangeException) { // The packet grew larger than the bufferSize while zerocoding. @@ -291,18 +296,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength + " and BufferLength=" + buffer.Data.Length + ". Removing MSG_ZEROCODED flag"); data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); - Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } } - else + + // If the packet data wasn't already copied during zerocoding, copy it now + if (doCopy) { - Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); + if (dataLength <= buffer.Data.Length) + { + Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); + } + else + { + m_log.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + + type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length + ". Dropping packet"); + return; + } } + buffer.DataLength = dataLength; #region Queue or Send - // Look up the UDPClient this is going to OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) From 80a8a9c4a7c9f1f0351f70f944a69650e9eca930 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 14:34:42 -0700 Subject: [PATCH 09/61] Converted FireAndForget methods to use a singleton pattern to attempt to work around a Mono bug with nested delegates --- OpenSim/Framework/Util.cs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 38729c6dc8..3203fc1d7d 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -44,6 +44,7 @@ using System.Xml; using log4net; using Nini.Config; using Nwc.XmlRpc; +using BclExtras; using OpenMetaverse; using OpenMetaverse.StructuredData; @@ -1269,14 +1270,32 @@ namespace OpenSim.Framework #region FireAndForget Threading Pattern + /// + /// Created to work around a limitation in Mono with nested delegates + /// + private class FireAndForgetWrapper + { + public void FireAndForget(System.Threading.WaitCallback callback) + { + callback.BeginInvoke(null, EndFireAndForget, callback); + } + + public void FireAndForget(System.Threading.WaitCallback callback, object obj) + { + callback.BeginInvoke(obj, EndFireAndForget, callback); + } + } + public static void FireAndForget(System.Threading.WaitCallback callback) { - callback.BeginInvoke(null, EndFireAndForget, callback); + FireAndForgetWrapper wrapper = Singleton.GetInstance(); + wrapper.FireAndForget(callback); } public static void FireAndForget(System.Threading.WaitCallback callback, object obj) { - callback.BeginInvoke(obj, EndFireAndForget, callback); + FireAndForgetWrapper wrapper = Singleton.GetInstance(); + wrapper.FireAndForget(callback, obj); } private static void EndFireAndForget(IAsyncResult ar) From c21f19741d172801cfb38ab7d4820ec371546b83 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 15:17:37 -0700 Subject: [PATCH 10/61] Adding noisy debug for nebadon --- .../RegionModulesController/RegionModulesControllerPlugin.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs index f30a18b378..fc59b82946 100644 --- a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs +++ b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs @@ -286,6 +286,7 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController // foreach (ISharedRegionModule module in sharedlist) { + m_log.Debug("[REGIONMODULE]: Calling RegionLoaded for " + module); module.RegionLoaded(scene); } From c3712a56f51bccd59958ba4d7806e8205e6f7117 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 15:34:27 -0700 Subject: [PATCH 11/61] A very ugly and temporary hack to disable the RegionCombinerModule RegionLoaded from firing for testing --- .../RegionModulesControllerPlugin.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs index fc59b82946..a8b6682dfa 100644 --- a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs +++ b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs @@ -286,8 +286,15 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController // foreach (ISharedRegionModule module in sharedlist) { - m_log.Debug("[REGIONMODULE]: Calling RegionLoaded for " + module); - module.RegionLoaded(scene); + if (!module.ToString().Contains("RegionCombinerModule")) + { + m_log.Debug("[REGIONMODULE]: Calling RegionLoaded for " + module); + module.RegionLoaded(scene); + } + else + { + m_log.Debug("[REGIONMODULE]: Skipping RegionCombinerModule"); + } } foreach (INonSharedRegionModule module in list) From c04ffe5c94a3929c05329dcaf895c426c506eb65 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 15:44:20 -0700 Subject: [PATCH 12/61] Reverting the previous ugly hack and replacing it with try/catch statements for each RegionLoaded() call. This probably won't fix nebadon's crash, but it seems like a good idea --- .../RegionModulesControllerPlugin.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs index a8b6682dfa..518982ee9e 100644 --- a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs +++ b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs @@ -286,20 +286,14 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController // foreach (ISharedRegionModule module in sharedlist) { - if (!module.ToString().Contains("RegionCombinerModule")) - { - m_log.Debug("[REGIONMODULE]: Calling RegionLoaded for " + module); - module.RegionLoaded(scene); - } - else - { - m_log.Debug("[REGIONMODULE]: Skipping RegionCombinerModule"); - } + try { module.RegionLoaded(scene); } + catch (Exception ex) { m_log.Error("[REGIONMODULE]: Exception while loading shared region module " + module + ": " + ex.Message, ex); } } foreach (INonSharedRegionModule module in list) { - module.RegionLoaded(scene); + try { module.RegionLoaded(scene); } + catch (Exception ex) { m_log.Error("[REGIONMODULE]: Exception while loading non-shared region module " + module + ": " + ex.Message, ex); } } } From f3f93228e02d9fae5baa55a7dd3de4ff7a630db3 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 15:50:22 -0700 Subject: [PATCH 13/61] Changing the region module loading foreach loops to typecast things to the IRegionModuleBase interface where AddRegion(), RegionLoaded(), etc. actually exist. Shot in the dark at fixing the Mono issue --- .../RegionModulesControllerPlugin.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs index 518982ee9e..a868bd0f42 100644 --- a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs +++ b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs @@ -268,7 +268,7 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController deferredlist.Add(module); } - foreach (INonSharedRegionModule module in deferredlist) + foreach (IRegionModuleBase module in deferredlist) { module.AddRegion(scene); scene.AddRegionModule(module.Name, module); @@ -284,13 +284,13 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController // and unneccessary caching logic repeated in all modules. // The extra function stub is just that much cleaner // - foreach (ISharedRegionModule module in sharedlist) + foreach (IRegionModuleBase module in sharedlist) { try { module.RegionLoaded(scene); } catch (Exception ex) { m_log.Error("[REGIONMODULE]: Exception while loading shared region module " + module + ": " + ex.Message, ex); } } - foreach (INonSharedRegionModule module in list) + foreach (IRegionModuleBase module in list) { try { module.RegionLoaded(scene); } catch (Exception ex) { m_log.Error("[REGIONMODULE]: Exception while loading non-shared region module " + module + ": " + ex.Message, ex); } From 7d6d94a7b58c91ce2720d57235768813938efa85 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 16:49:13 -0700 Subject: [PATCH 14/61] More debugging of RegionCombinerModule.RegionLoaded() by making RegionLoaded() a two line function --- .../Region/CoreModules/World/Land/RegionCombinerModule.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/CoreModules/World/Land/RegionCombinerModule.cs b/OpenSim/Region/CoreModules/World/Land/RegionCombinerModule.cs index 649991546a..d8c5ed9fcf 100644 --- a/OpenSim/Region/CoreModules/World/Land/RegionCombinerModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/RegionCombinerModule.cs @@ -81,8 +81,12 @@ namespace OpenSim.Region.CoreModules.World.Land public void RegionLoaded(Scene scene) { - if (!enabledYN) - return; + if (enabledYN) + RegionLoadedDoWork(scene); + } + + private void RegionLoadedDoWork(Scene scene) + { /* // For testing on a single instance if (scene.RegionInfo.RegionLocX == 1004 && scene.RegionInfo.RegionLocY == 1000) From c81378dc22499ccb03f827e4821214ce7eb87dc1 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 17:33:41 -0700 Subject: [PATCH 15/61] Changing avatar movement updates to the Task throttle category until we get finer grained prioritization of avatars vs. prims --- OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 383eac036b..43c3c7c75b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3426,7 +3426,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP terse.Header.Reliable = false; terse.Header.Zerocoded = true; - OutPacket(terse, ThrottleOutPacketType.State); + OutPacket(terse, ThrottleOutPacketType.Task); } } @@ -3582,14 +3582,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP void HandleQueueEmpty(ThrottleOutPacketType queue) { + int count = 0; + switch (queue) { case ThrottleOutPacketType.Texture: ProcessTextureRequests(); break; - case ThrottleOutPacketType.State: - int count = 0; - + case ThrottleOutPacketType.Task: lock (m_avatarTerseUpdates.SyncRoot) count = m_avatarTerseUpdates.Count; if (count > 0) @@ -3597,7 +3597,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP ProcessAvatarTerseUpdates(); return; } - + break; + case ThrottleOutPacketType.State: lock (m_primFullUpdates.SyncRoot) count = m_primFullUpdates.Count; if (count > 0) From 3a63de8d02c4c8fb0f1387b482ee85cf15d05a2a Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sat, 17 Oct 2009 15:55:30 -0700 Subject: [PATCH 16/61] Added a description for RegionType --- OpenSim/Framework/RegionInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs index a7315f51fb..fa747c4c7a 100644 --- a/OpenSim/Framework/RegionInfo.cs +++ b/OpenSim/Framework/RegionInfo.cs @@ -821,7 +821,7 @@ namespace OpenSim.Framework "Scope ID for this region", ScopeID.ToString(), true); configMember.addConfigurationOption("region_type", ConfigurationOption.ConfigurationTypes.TYPE_STRING, - "Region Type", String.Empty, true); + "Free form string describing the type of region", String.Empty, true); } public void loadConfigurationOptions() From e28ac424861deafe6083b497b546cb64352f5512 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sat, 17 Oct 2009 17:19:18 -0700 Subject: [PATCH 17/61] Wrapped the contents of the IncomingPacketHandler loop in a try/catch statement --- OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 8bfab6a107..7a403aae96 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -716,8 +716,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP while (base.IsRunning) { - if (packetInbox.Dequeue(100, ref incomingPacket)) - Util.FireAndForget(ProcessInPacket, incomingPacket); + try + { + if (packetInbox.Dequeue(100, ref incomingPacket)) + Util.FireAndForget(ProcessInPacket, incomingPacket); + } + catch (Exception ex) + { + m_log.Error("[LLUDPSERVER]: Error in the incoming packet handler loop: " + ex.Message, ex); + } } if (packetInbox.Count > 0) From fdb2a75ad357668b860fc5055e0630ef75a3ad20 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sat, 17 Oct 2009 18:01:22 -0700 Subject: [PATCH 18/61] Committing the second part of Jim Greensky @ Intel Lab's patch, re-prioritizing updates --- .../Client/MXP/ClientStack/MXPClientView.cs | 4 + .../VWoHTTP/ClientStack/VWHClientView.cs | 5 + OpenSim/Framework/IClientAPI.cs | 27 +++++ .../ClientStack/LindenUDP/LLClientView.cs | 107 ++++++++++++++--- .../Examples/SimpleModule/MyNpcCharacter.cs | 4 + OpenSim/Region/Framework/Scenes/Scene.cs | 13 ++ OpenSim/Region/Framework/Scenes/SceneGraph.cs | 2 +- .../Region/Framework/Scenes/ScenePresence.cs | 112 +++++++++++++++++- .../Server/IRCClientView.cs | 5 + .../OptionalModules/World/NPC/NPCAvatar.cs | 4 + OpenSim/Tests/Common/Mock/TestClient.cs | 4 + 11 files changed, 267 insertions(+), 20 deletions(-) diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index ea29c41b8f..52110d6ef8 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -1042,6 +1042,10 @@ namespace OpenSim.Client.MXP.ClientStack Session.Send(me); } + public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + } + public void FlushPrimUpdates() { } diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index d6990de17a..4a54c679fd 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -596,6 +596,11 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } + public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + throw new System.NotImplementedException(); + } + public void FlushPrimUpdates() { throw new System.NotImplementedException(); diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 9aa3af1496..03e7a25786 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -448,6 +448,8 @@ namespace OpenSim.Framework public delegate void AvatarInterestUpdate(IClientAPI client, uint wantmask, string wanttext, uint skillsmask, string skillstext, string languages); public delegate void PlacesQuery(UUID QueryID, UUID TransactionID, string QueryText, uint QueryFlags, byte Category, string SimName, IClientAPI client); + public delegate double UpdatePriorityHandler(UpdatePriorityData data); + #endregion public struct DirPlacesReplyData @@ -744,6 +746,29 @@ namespace OpenSim.Framework public double priority { get { return this.m_priority; } } } + public struct UpdatePriorityData { + private double m_priority; + private uint m_localID; + + public UpdatePriorityData(double priority, uint localID) { + this.m_priority = priority; + this.m_localID = localID; + } + + public double priority { get { return this.m_priority; } } + public uint localID { get { return this.m_localID; } } + } + + [Flags] + public enum StateUpdateTypes + { + None = 0, + AvatarTerse = 1, + PrimitiveTerse = AvatarTerse << 1, + PrimitiveFull = PrimitiveTerse << 1, + All = AvatarTerse | PrimitiveTerse | PrimitiveFull, + } + public interface IClientAPI { Vector3 StartPos { get; set; } @@ -1118,6 +1143,8 @@ namespace OpenSim.Framework void SendPrimTerseUpdate(SendPrimitiveTerseData data); + void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler); + void SendInventoryFolderDetails(UUID ownerID, UUID folderID, List items, List folders, bool fetchFolders, bool fetchItems); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 43c3c7c75b..3b2a6042f4 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3582,37 +3582,51 @@ namespace OpenSim.Region.ClientStack.LindenUDP void HandleQueueEmpty(ThrottleOutPacketType queue) { - int count = 0; - switch (queue) { case ThrottleOutPacketType.Texture: ProcessTextureRequests(); break; case ThrottleOutPacketType.Task: - lock (m_avatarTerseUpdates.SyncRoot) - count = m_avatarTerseUpdates.Count; - if (count > 0) + if (Monitor.TryEnter(m_avatarTerseUpdates.SyncRoot, 1)) { - ProcessAvatarTerseUpdates(); - return; + try + { + if (m_avatarTerseUpdates.Count > 0) + { + + ProcessAvatarTerseUpdates(); + return; + } + } + finally { Monitor.Exit(m_avatarTerseUpdates.SyncRoot); } } break; case ThrottleOutPacketType.State: - lock (m_primFullUpdates.SyncRoot) - count = m_primFullUpdates.Count; - if (count > 0) + if (Monitor.TryEnter(m_primFullUpdates.SyncRoot, 1)) { - ProcessPrimFullUpdates(); - return; + try + { + if (m_primFullUpdates.Count > 0) + { + ProcessPrimFullUpdates(); + return; + } + } + finally { Monitor.Exit(m_primFullUpdates.SyncRoot); } } - lock (m_primTerseUpdates.SyncRoot) - count = m_primTerseUpdates.Count; - if (count > 0) + if (Monitor.TryEnter(m_primTerseUpdates.SyncRoot, 1)) { - ProcessPrimTerseUpdates(); - return; + try + { + if (m_primTerseUpdates.Count > 0) + { + ProcessPrimTerseUpdates(); + return; + } + } + finally { Monitor.Exit(m_primTerseUpdates.SyncRoot); } } break; } @@ -3703,6 +3717,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } + public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + PriorityQueue.UpdatePriorityHandler terse_update_priority_handler = + delegate(ref double priority, uint local_id) + { + priority = handler(new UpdatePriorityData(priority, local_id)); + return priority != double.NaN; + }; + PriorityQueue.UpdatePriorityHandler update_priority_handler = + delegate(ref double priority, uint local_id) + { + priority = handler(new UpdatePriorityData(priority, local_id)); + return priority != double.NaN; + }; + + if ((type & StateUpdateTypes.AvatarTerse) != 0) { + lock (m_avatarTerseUpdates.SyncRoot) + m_avatarTerseUpdates.Reprioritize(terse_update_priority_handler); + } + + if ((type & StateUpdateTypes.PrimitiveFull) != 0) { + lock (m_primFullUpdates.SyncRoot) + m_primFullUpdates.Reprioritize(update_priority_handler); + } + + if ((type & StateUpdateTypes.PrimitiveTerse) != 0) { + lock (m_primTerseUpdates.SyncRoot) + m_primTerseUpdates.Reprioritize(terse_update_priority_handler); + } + } + public void FlushPrimUpdates() { while (m_primFullUpdates.Count > 0) @@ -10465,6 +10510,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region PriorityQueue private class PriorityQueue { + internal delegate bool UpdatePriorityHandler(ref TPriority priority, uint local_id); + private MinHeap[] heaps = new MinHeap[1]; private Dictionary lookup_table = new Dictionary(); private Comparison comparison; @@ -10539,6 +10586,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); } + internal void Reprioritize(UpdatePriorityHandler handler) + { + MinHeapItem item; + TPriority priority; + + foreach (LookupItem lookup in new List(this.lookup_table.Values)) + { + if (lookup.Heap.TryGetValue(lookup.Handle, out item)) + { + priority = item.Priority; + if (handler(ref priority, item.LocalID)) + { + if (lookup.Heap.ContainsHandle(lookup.Handle)) + lookup.Heap[lookup.Handle] = + new MinHeapItem(priority, item.Value, item.LocalID); + } + else + { + m_log.Warn("[LLClientView] UpdatePriorityHandle returned false, dropping update"); + lookup.Heap.Remove(lookup.Handle); + this.lookup_table.Remove(item.LocalID); + } + } + } + } + #region MinHeapItem private struct MinHeapItem : IComparable { diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index 3799a02f84..5a5fcfe8c5 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -527,6 +527,10 @@ namespace OpenSim.Region.Examples.SimpleModule { } + public virtual void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + } + public void FlushPrimUpdates() { } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 49c1ebf929..30fe976490 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -278,6 +278,10 @@ namespace OpenSim.Region.Framework.Scenes private bool m_firstHeartbeat = true; private UpdatePrioritizationSchemes m_update_prioritization_scheme = UpdatePrioritizationSchemes.Time; + private bool m_reprioritization_enabled = true; + private double m_reprioritization_interval = 2000.0; + private double m_root_reprioritization_distance = 5.0; + private double m_child_reprioritization_distance = 10.0; private object m_deleting_scene_object = new object(); @@ -291,6 +295,10 @@ namespace OpenSim.Region.Framework.Scenes #region Properties public UpdatePrioritizationSchemes UpdatePrioritizationScheme { get { return this.m_update_prioritization_scheme; } } + public bool IsReprioritizationEnabled { get { return m_reprioritization_enabled; } } + public double ReprioritizationInterval { get { return m_reprioritization_interval; } } + public double RootReprioritizationDistance { get { return m_root_reprioritization_distance; } } + public double ChildReprioritizationDistance { get { return m_child_reprioritization_distance; } } public AgentCircuitManager AuthenticateHandler { @@ -542,6 +550,11 @@ namespace OpenSim.Region.Framework.Scenes m_update_prioritization_scheme = UpdatePrioritizationSchemes.Time; break; } + + m_reprioritization_enabled = interest_management_config.GetBoolean("ReprioritizationEnabled", true); + m_reprioritization_interval = interest_management_config.GetDouble("ReprioritizationInterval", 5000.0); + m_root_reprioritization_distance = interest_management_config.GetDouble("RootReprioritizationDistance", 10.0); + m_child_reprioritization_distance = interest_management_config.GetDouble("ChildReprioritizationDistance", 20.0); } m_log.Info("[SCENE]: Using the " + m_update_prioritization_scheme + " prioritization scheme"); diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index b9872ca0cf..8ee26c3cd7 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -846,7 +846,7 @@ namespace OpenSim.Region.Framework.Scenes /// /// /// null if no scene object group containing that prim is found - private SceneObjectGroup GetGroupByPrim(uint localID) + public SceneObjectGroup GetGroupByPrim(uint localID) { if (Entities.ContainsKey(localID)) return Entities[localID] as SceneObjectGroup; diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 9d13ad4649..f05c3d8183 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Timers; using OpenMetaverse; using log4net; using OpenSim.Framework; @@ -172,6 +173,11 @@ namespace OpenSim.Region.Framework.Scenes // Position of agent's camera in world (region cordinates) protected Vector3 m_CameraCenter = Vector3.Zero; + protected Vector3 m_lastCameraCenter = Vector3.Zero; + + protected Timer m_reprioritization_timer; + protected bool m_reprioritizing = false; + protected bool m_reprioritization_called = false; // Use these three vectors to figure out what the agent is looking at // Convert it to a Matrix and/or Quaternion @@ -639,7 +645,14 @@ namespace OpenSim.Region.Framework.Scenes m_scriptEngines = m_scene.RequestModuleInterfaces(); - AbsolutePosition = m_controllingClient.StartPos; + AbsolutePosition = posLastSignificantMove = m_CameraCenter = + m_lastCameraCenter = m_controllingClient.StartPos; + + m_reprioritization_timer = new Timer(world.ReprioritizationInterval); + m_reprioritization_timer.Elapsed += new ElapsedEventHandler(Reprioritize); + m_reprioritization_timer.AutoReset = false; + + AdjustKnownSeeds(); TrySetMovementAnimation("STAND"); // TODO: I think, this won't send anything, as we are still a child here... @@ -1219,6 +1232,11 @@ namespace OpenSim.Region.Framework.Scenes // Camera location in world. We'll need to raytrace // from this location from time to time. m_CameraCenter = agentData.CameraCenter; + if (Vector3.Distance(m_lastCameraCenter, m_CameraCenter) >= Scene.RootReprioritizationDistance) + { + ReprioritizeUpdates(); + m_lastCameraCenter = m_CameraCenter; + } // Use these three vectors to figure out what the agent is looking at // Convert it to a Matrix and/or Quaternion @@ -2823,7 +2841,7 @@ namespace OpenSim.Region.Framework.Scenes } // Minimum Draw distance is 64 meters, the Radius of the draw distance sphere is 32m - if (Util.GetDistanceTo(AbsolutePosition,m_LastChildAgentUpdatePosition) > 32) + if (Util.GetDistanceTo(AbsolutePosition, m_LastChildAgentUpdatePosition) >= Scene.ChildReprioritizationDistance) { ChildAgentDataUpdate cadu = new ChildAgentDataUpdate(); cadu.ActiveGroupID = UUID.Zero.Guid; @@ -3118,6 +3136,12 @@ namespace OpenSim.Region.Framework.Scenes if (cAgentData.Position != new Vector3(-1, -1, -1)) // UGH!! m_pos = new Vector3(cAgentData.Position.X + shiftx, cAgentData.Position.Y + shifty, cAgentData.Position.Z); + if (Vector3.Distance(AbsolutePosition, posLastSignificantMove) >= Scene.ChildReprioritizationDistance) + { + posLastSignificantMove = AbsolutePosition; + ReprioritizeUpdates(); + } + // It's hard to say here.. We can't really tell where the camera position is unless it's in world cordinates from the sending region m_CameraCenter = cAgentData.Center; @@ -3498,6 +3522,16 @@ namespace OpenSim.Region.Framework.Scenes { m_knownChildRegions.Clear(); } + + lock (m_reprioritization_timer) + { + m_reprioritization_timer.Enabled = false; + m_reprioritization_timer.Elapsed -= new ElapsedEventHandler(Reprioritize); + } + // I don't get it but mono crashes when you try to dispose of this timer, + // unsetting the elapsed callback should be enough to allow for cleanup however. + //m_reprioritizationTimer.Dispose(); + m_sceneViewer.Close(); RemoveFromPhysicalScene(); @@ -3913,5 +3947,79 @@ namespace OpenSim.Region.Framework.Scenes { return Vector3.Distance(AbsolutePosition, position); } + + private double GetSOGUpdatePriority(SceneObjectGroup sog) + { + switch (Scene.UpdatePrioritizationScheme) + { + case Scene.UpdatePrioritizationSchemes.Time: + throw new InvalidOperationException("UpdatePrioritizationScheme for time not supported for reprioritization"); + case Scene.UpdatePrioritizationSchemes.Distance: + return sog.GetPriorityByDistance((IsChildAgent) ? AbsolutePosition : CameraPosition); + case Scene.UpdatePrioritizationSchemes.SimpleAngularDistance: + return sog.GetPriorityBySimpleAngularDistance((IsChildAgent) ? AbsolutePosition : CameraPosition); + default: + throw new InvalidOperationException("UpdatePrioritizationScheme not defined"); + } + } + + private double UpdatePriority(UpdatePriorityData data) + { + EntityBase entity; + SceneObjectGroup group; + + if (Scene.Entities.TryGetValue(data.localID, out entity)) + { + group = entity as SceneObjectGroup; + if (group != null) + return GetSOGUpdatePriority(group); + + ScenePresence presence = entity as ScenePresence; + if (presence == null) + throw new InvalidOperationException("entity found is neither SceneObjectGroup nor ScenePresence"); + switch (Scene.UpdatePrioritizationScheme) + { + case Scene.UpdatePrioritizationSchemes.Time: + throw new InvalidOperationException("UpdatePrioritization for time not supported for reprioritization"); + case Scene.UpdatePrioritizationSchemes.Distance: + case Scene.UpdatePrioritizationSchemes.SimpleAngularDistance: + return GetPriorityByDistance((IsChildAgent) ? AbsolutePosition : CameraPosition); + default: + throw new InvalidOperationException("UpdatePrioritizationScheme not defined"); + } + } + else + { + group = Scene.SceneGraph.GetGroupByPrim(data.localID); + if (group != null) + return GetSOGUpdatePriority(group); + } + return double.NaN; + } + + private void ReprioritizeUpdates() + { + if (Scene.IsReprioritizationEnabled && Scene.UpdatePrioritizationScheme != Scene.UpdatePrioritizationSchemes.Time) + { + lock (m_reprioritization_timer) + { + if (!m_reprioritizing) + m_reprioritization_timer.Enabled = m_reprioritizing = true; + else + m_reprioritization_called = true; + } + } + } + + private void Reprioritize(object sender, ElapsedEventArgs e) + { + m_controllingClient.ReprioritizeUpdates(StateUpdateTypes.All, UpdatePriority); + + lock (m_reprioritization_timer) + { + m_reprioritization_timer.Enabled = m_reprioritizing = m_reprioritization_called; + m_reprioritization_called = false; + } + } } } diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 1a24decb21..df03b8d934 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -1048,6 +1048,11 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } + public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + + } + public void SendInventoryFolderDetails(UUID ownerID, UUID folderID, List items, List folders, bool fetchFolders, bool fetchItems) { diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 6c58f2dcb4..f7cadaabd2 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -616,6 +616,10 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } + public virtual void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + } + public void FlushPrimUpdates() { } diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index 21606e24d0..0f642b945f 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -620,6 +620,10 @@ namespace OpenSim.Tests.Common.Mock { } + public virtual void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + } + public void FlushPrimUpdates() { } From a3f93cffb4fb9cb9865fe5a1815c547fa02d092c Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sat, 17 Oct 2009 22:06:36 -0700 Subject: [PATCH 19/61] * Committing Nini.dll with the patch from #3773 applied * Fixing a log message typo --- .../ClientStack/LindenUDP/LLClientView.cs | 2 +- bin/Nini.dll | Bin 69632 -> 61440 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 3b2a6042f4..2773a5e8e9 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -10604,7 +10604,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - m_log.Warn("[LLClientView] UpdatePriorityHandle returned false, dropping update"); + m_log.Warn("[LLCLIENTVIEW]: UpdatePriorityHandler returned false, dropping update"); lookup.Heap.Remove(lookup.Handle); this.lookup_table.Remove(item.LocalID); } diff --git a/bin/Nini.dll b/bin/Nini.dll index cbe10501cbf7b0b20e17f1e987411da0a5d962ed..745057c3f64795a9d2709faafd6a0a942ca717f1 100644 GIT binary patch literal 61440 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 E1)?t;O8@`> literal 69632 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

=mkBZOS_k__*z{czP7Lc+eHO62ulrJ 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 tAiy~PBvF_2k8Ru{0uh0TKtv!S5D|z7L Date: Sun, 18 Oct 2009 02:00:42 -0700 Subject: [PATCH 20/61] * Big performance increase in loading prims from the region database with MySQL * Handle the AgentFOV packet * Bypass queuing and throttles for ping checks to make ping times more closely match network latency * Only track reliable bytes in LLUDPCLient.BytesSinceLastACK --- OpenSim/Data/MySQL/MySQLLegacyRegionData.cs | 277 +++++++++--------- OpenSim/Framework/IClientAPI.cs | 2 + .../ClientStack/LindenUDP/LLClientView.cs | 26 +- .../ClientStack/LindenUDP/LLUDPServer.cs | 51 +++- 4 files changed, 194 insertions(+), 162 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs index 6bc8beceff..801d6b9eed 100644 --- a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs @@ -403,26 +403,23 @@ namespace OpenSim.Data.MySQL } } - public List LoadObjects(UUID regionUUID) + public List LoadObjects(UUID regionID) { - UUID lastGroupID = UUID.Zero; + const int ROWS_PER_QUERY = 5000; + + Dictionary prims = new Dictionary(ROWS_PER_QUERY); Dictionary objects = new Dictionary(); - Dictionary prims = new Dictionary(); - SceneObjectGroup grp = null; int count = 0; + #region Prim Loading + lock (m_Connection) { using (MySqlCommand cmd = m_Connection.CreateCommand()) { - cmd.CommandText = "select *, " + - "case when prims.UUID = SceneGroupID " + - "then 0 else 1 end as sort from prims " + - "left join primshapes on prims.UUID = primshapes.UUID " + - "where RegionUUID = ?RegionUUID " + - "order by SceneGroupID asc, sort asc, LinkNumber asc"; - - cmd.Parameters.AddWithValue("RegionUUID", regionUUID.ToString()); + cmd.CommandText = + "SELECT * FROM prims LEFT JOIN primshapes ON prims.UUID = primshapes.UUID WHERE RegionUUID = ?RegionUUID"; + cmd.Parameters.AddWithValue("RegionUUID", regionID.ToString()); using (IDataReader reader = ExecuteReader(cmd)) { @@ -434,56 +431,61 @@ namespace OpenSim.Data.MySQL else prim.Shape = BuildShape(reader); + UUID parentID = new UUID(reader["SceneGroupID"].ToString()); + if (parentID != prim.UUID) + prim.ParentUUID = parentID; + prims[prim.UUID] = prim; - UUID groupID = new UUID(reader["SceneGroupID"].ToString()); - - if (groupID != lastGroupID) // New SOG - { - if (grp != null) - objects[grp.UUID] = grp; - - lastGroupID = groupID; - - // There sometimes exist OpenSim bugs that 'orphan groups' so that none of the prims are - // recorded as the root prim (for which the UUID must equal the persisted group UUID). In - // this case, force the UUID to be the same as the group UUID so that at least these can be - // deleted (we need to change the UUID so that any other prims in the linkset can also be - // deleted). - if (prim.UUID != groupID && groupID != UUID.Zero) - { - m_log.WarnFormat( - "[REGION DB]: Found root prim {0} {1} at {2} where group was actually {3}. Forcing UUID to group UUID", - prim.Name, prim.UUID, prim.GroupPosition, groupID); - - prim.UUID = groupID; - } - - grp = new SceneObjectGroup(prim); - } - else - { - // Black magic to preserve link numbers - // - int link = prim.LinkNum; - - grp.AddPart(prim); - - if (link != 0) - prim.LinkNum = link; - } - ++count; - if (count % 5000 == 0) + if (count % ROWS_PER_QUERY == 0) m_log.Debug("[REGION DB]: Loaded " + count + " prims..."); } } - - if (grp != null) - objects[grp.UUID] = grp; } } + #endregion Prim Loading + + #region SceneObjectGroup Creation + + // Create all of the SOGs from the root prims first + foreach (SceneObjectPart prim in prims.Values) + { + if (prim.ParentUUID == UUID.Zero) + objects[prim.UUID] = new SceneObjectGroup(prim); + } + + // Add all of the children objects to the SOGs + foreach (SceneObjectPart prim in prims.Values) + { + SceneObjectGroup sog; + if (prim.UUID != prim.ParentUUID) + { + if (objects.TryGetValue(prim.ParentUUID, out sog)) + { + int originalLinkNum = prim.LinkNum; + + sog.AddPart(prim); + + // SceneObjectGroup.AddPart() tries to be smart and automatically set the LinkNum. + // We override that here + if (originalLinkNum != 0) + prim.LinkNum = originalLinkNum; + } + else + { + m_log.Warn("[REGION DB]: Database contains an orphan child prim " + prim.UUID + " pointing to missing parent " + prim.ParentUUID); + } + } + } + + #endregion SceneObjectGroup Creation + + m_log.DebugFormat("[REGION DB]: Loaded {0} objects using {1} prims", objects.Count, prims.Count); + + #region Prim Inventory Loading + // Instead of attempting to LoadItems on every prim, // most of which probably have no items... get a // list from DB of all prims which have items and @@ -493,7 +495,7 @@ namespace OpenSim.Data.MySQL { using (MySqlCommand itemCmd = m_Connection.CreateCommand()) { - itemCmd.CommandText = "select distinct primID from primitems"; + itemCmd.CommandText = "SELECT DISTINCT primID FROM primitems"; using (IDataReader itemReader = ExecuteReader(itemCmd)) { while (itemReader.Read()) @@ -502,9 +504,7 @@ namespace OpenSim.Data.MySQL { UUID primID = new UUID(itemReader["primID"].ToString()); if (prims.ContainsKey(primID)) - { primsWithInventory.Add(prims[primID]); - } } } } @@ -512,9 +512,14 @@ namespace OpenSim.Data.MySQL } foreach (SceneObjectPart prim in primsWithInventory) + { LoadItems(prim); + } + + #endregion Prim Inventory Loading + + m_log.DebugFormat("[REGION DB]: Loaded inventory from {0} objects", primsWithInventory.Count); - m_log.DebugFormat("[REGION DB]: Loaded {0} objects using {1} prims", objects.Count, prims.Count); return new List(objects.Values); } @@ -811,137 +816,137 @@ namespace OpenSim.Data.MySQL private SceneObjectPart BuildPrim(IDataReader row) { SceneObjectPart prim = new SceneObjectPart(); - prim.UUID = new UUID((String) row["UUID"]); + prim.UUID = new UUID((string)row["UUID"]); // explicit conversion of integers is required, which sort // of sucks. No idea if there is a shortcut here or not. - prim.CreationDate = Convert.ToInt32(row["CreationDate"]); + prim.CreationDate = (int)row["CreationDate"]; if (row["Name"] != DBNull.Value) - prim.Name = (String)row["Name"]; + prim.Name = (string)row["Name"]; else - prim.Name = string.Empty; - // various text fields - prim.Text = (String) row["Text"]; - prim.Color = Color.FromArgb(Convert.ToInt32(row["ColorA"]), - Convert.ToInt32(row["ColorR"]), - Convert.ToInt32(row["ColorG"]), - Convert.ToInt32(row["ColorB"])); - prim.Description = (String) row["Description"]; - prim.SitName = (String) row["SitName"]; - prim.TouchName = (String) row["TouchName"]; - // permissions - prim.ObjectFlags = Convert.ToUInt32(row["ObjectFlags"]); - prim.CreatorID = new UUID((String) row["CreatorID"]); - prim.OwnerID = new UUID((String) row["OwnerID"]); - prim.GroupID = new UUID((String) row["GroupID"]); - prim.LastOwnerID = new UUID((String) row["LastOwnerID"]); - prim.OwnerMask = Convert.ToUInt32(row["OwnerMask"]); - prim.NextOwnerMask = Convert.ToUInt32(row["NextOwnerMask"]); - prim.GroupMask = Convert.ToUInt32(row["GroupMask"]); - prim.EveryoneMask = Convert.ToUInt32(row["EveryoneMask"]); - prim.BaseMask = Convert.ToUInt32(row["BaseMask"]); - // vectors + prim.Name = String.Empty; + // Various text fields + prim.Text = (string)row["Text"]; + prim.Color = Color.FromArgb((int)row["ColorA"], + (int)row["ColorR"], + (int)row["ColorG"], + (int)row["ColorB"]); + prim.Description = (string)row["Description"]; + prim.SitName = (string)row["SitName"]; + prim.TouchName = (string)row["TouchName"]; + // Permissions + prim.ObjectFlags = (uint)(int)row["ObjectFlags"]; + prim.CreatorID = new UUID((string)row["CreatorID"]); + prim.OwnerID = new UUID((string)row["OwnerID"]); + prim.GroupID = new UUID((string)row["GroupID"]); + prim.LastOwnerID = new UUID((string)row["LastOwnerID"]); + prim.OwnerMask = (uint)(int)row["OwnerMask"]; + prim.NextOwnerMask = (uint)(int)row["NextOwnerMask"]; + prim.GroupMask = (uint)(int)row["GroupMask"]; + prim.EveryoneMask = (uint)(int)row["EveryoneMask"]; + prim.BaseMask = (uint)(int)row["BaseMask"]; + // Vectors prim.OffsetPosition = new Vector3( - Convert.ToSingle(row["PositionX"]), - Convert.ToSingle(row["PositionY"]), - Convert.ToSingle(row["PositionZ"]) + (float)(double)row["PositionX"], + (float)(double)row["PositionY"], + (float)(double)row["PositionZ"] ); prim.GroupPosition = new Vector3( - Convert.ToSingle(row["GroupPositionX"]), - Convert.ToSingle(row["GroupPositionY"]), - Convert.ToSingle(row["GroupPositionZ"]) + (float)(double)row["GroupPositionX"], + (float)(double)row["GroupPositionY"], + (float)(double)row["GroupPositionZ"] ); prim.Velocity = new Vector3( - Convert.ToSingle(row["VelocityX"]), - Convert.ToSingle(row["VelocityY"]), - Convert.ToSingle(row["VelocityZ"]) + (float)(double)row["VelocityX"], + (float)(double)row["VelocityY"], + (float)(double)row["VelocityZ"] ); prim.AngularVelocity = new Vector3( - Convert.ToSingle(row["AngularVelocityX"]), - Convert.ToSingle(row["AngularVelocityY"]), - Convert.ToSingle(row["AngularVelocityZ"]) + (float)(double)row["AngularVelocityX"], + (float)(double)row["AngularVelocityY"], + (float)(double)row["AngularVelocityZ"] ); prim.Acceleration = new Vector3( - Convert.ToSingle(row["AccelerationX"]), - Convert.ToSingle(row["AccelerationY"]), - Convert.ToSingle(row["AccelerationZ"]) + (float)(double)row["AccelerationX"], + (float)(double)row["AccelerationY"], + (float)(double)row["AccelerationZ"] ); // quaternions prim.RotationOffset = new Quaternion( - Convert.ToSingle(row["RotationX"]), - Convert.ToSingle(row["RotationY"]), - Convert.ToSingle(row["RotationZ"]), - Convert.ToSingle(row["RotationW"]) + (float)(double)row["RotationX"], + (float)(double)row["RotationY"], + (float)(double)row["RotationZ"], + (float)(double)row["RotationW"] ); prim.SitTargetPositionLL = new Vector3( - Convert.ToSingle(row["SitTargetOffsetX"]), - Convert.ToSingle(row["SitTargetOffsetY"]), - Convert.ToSingle(row["SitTargetOffsetZ"]) + (float)(double)row["SitTargetOffsetX"], + (float)(double)row["SitTargetOffsetY"], + (float)(double)row["SitTargetOffsetZ"] ); prim.SitTargetOrientationLL = new Quaternion( - Convert.ToSingle(row["SitTargetOrientX"]), - Convert.ToSingle(row["SitTargetOrientY"]), - Convert.ToSingle(row["SitTargetOrientZ"]), - Convert.ToSingle(row["SitTargetOrientW"]) + (float)(double)row["SitTargetOrientX"], + (float)(double)row["SitTargetOrientY"], + (float)(double)row["SitTargetOrientZ"], + (float)(double)row["SitTargetOrientW"] ); - prim.PayPrice[0] = Convert.ToInt32(row["PayPrice"]); - prim.PayPrice[1] = Convert.ToInt32(row["PayButton1"]); - prim.PayPrice[2] = Convert.ToInt32(row["PayButton2"]); - prim.PayPrice[3] = Convert.ToInt32(row["PayButton3"]); - prim.PayPrice[4] = Convert.ToInt32(row["PayButton4"]); + prim.PayPrice[0] = (int)row["PayPrice"]; + prim.PayPrice[1] = (int)row["PayButton1"]; + prim.PayPrice[2] = (int)row["PayButton2"]; + prim.PayPrice[3] = (int)row["PayButton3"]; + prim.PayPrice[4] = (int)row["PayButton4"]; prim.Sound = new UUID(row["LoopedSound"].ToString()); - prim.SoundGain = Convert.ToSingle(row["LoopedSoundGain"]); + prim.SoundGain = (float)(double)row["LoopedSoundGain"]; prim.SoundFlags = 1; // If it's persisted at all, it's looped if (!(row["TextureAnimation"] is DBNull)) - prim.TextureAnimation = (Byte[])row["TextureAnimation"]; + prim.TextureAnimation = (byte[])row["TextureAnimation"]; if (!(row["ParticleSystem"] is DBNull)) - prim.ParticleSystem = (Byte[])row["ParticleSystem"]; + prim.ParticleSystem = (byte[])row["ParticleSystem"]; prim.RotationalVelocity = new Vector3( - Convert.ToSingle(row["OmegaX"]), - Convert.ToSingle(row["OmegaY"]), - Convert.ToSingle(row["OmegaZ"]) + (float)(double)row["OmegaX"], + (float)(double)row["OmegaY"], + (float)(double)row["OmegaZ"] ); prim.SetCameraEyeOffset(new Vector3( - Convert.ToSingle(row["CameraEyeOffsetX"]), - Convert.ToSingle(row["CameraEyeOffsetY"]), - Convert.ToSingle(row["CameraEyeOffsetZ"]) + (float)(double)row["CameraEyeOffsetX"], + (float)(double)row["CameraEyeOffsetY"], + (float)(double)row["CameraEyeOffsetZ"] )); prim.SetCameraAtOffset(new Vector3( - Convert.ToSingle(row["CameraAtOffsetX"]), - Convert.ToSingle(row["CameraAtOffsetY"]), - Convert.ToSingle(row["CameraAtOffsetZ"]) + (float)(double)row["CameraAtOffsetX"], + (float)(double)row["CameraAtOffsetY"], + (float)(double)row["CameraAtOffsetZ"] )); - if (Convert.ToInt16(row["ForceMouselook"]) != 0) + if ((sbyte)row["ForceMouselook"] != 0) prim.SetForceMouselook(true); - prim.ScriptAccessPin = Convert.ToInt32(row["ScriptAccessPin"]); + prim.ScriptAccessPin = (int)row["ScriptAccessPin"]; - if (Convert.ToInt16(row["AllowedDrop"]) != 0) + if ((sbyte)row["AllowedDrop"] != 0) prim.AllowedDrop = true; - if (Convert.ToInt16(row["DieAtEdge"]) != 0) + if ((sbyte)row["DieAtEdge"] != 0) prim.DIE_AT_EDGE = true; - prim.SalePrice = Convert.ToInt32(row["SalePrice"]); - prim.ObjectSaleType = unchecked((byte)Convert.ToSByte(row["SaleType"])); + prim.SalePrice = (int)row["SalePrice"]; + prim.ObjectSaleType = unchecked((byte)(sbyte)row["SaleType"]); - prim.Material = unchecked((byte)Convert.ToSByte(row["Material"])); + prim.Material = unchecked((byte)(sbyte)row["Material"]); if (!(row["ClickAction"] is DBNull)) - prim.ClickAction = unchecked((byte)Convert.ToSByte(row["ClickAction"])); + prim.ClickAction = unchecked((byte)(sbyte)row["ClickAction"]); prim.CollisionSound = new UUID(row["CollisionSound"].ToString()); - prim.CollisionSoundVolume = Convert.ToSingle(row["CollisionSoundVolume"]); + prim.CollisionSoundVolume = (float)(double)row["CollisionSoundVolume"]; - if (Convert.ToInt16(row["PassTouches"]) != 0) + if ((sbyte)row["PassTouches"] != 0) prim.PassTouches = true; - prim.LinkNum = Convert.ToInt32(row["LinkNumber"]); + prim.LinkNum = (int)row["LinkNumber"]; return prim; } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 03e7a25786..1e03d4c012 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -448,6 +448,8 @@ namespace OpenSim.Framework public delegate void AvatarInterestUpdate(IClientAPI client, uint wantmask, string wanttext, uint skillsmask, string skillstext, string languages); public delegate void PlacesQuery(UUID QueryID, UUID TransactionID, string QueryText, uint QueryFlags, byte Category, string SimName, IClientAPI client); + public delegate void AgentFOV(IClientAPI client, float verticalAngle); + public delegate double UpdatePriorityHandler(UpdatePriorityData data); #endregion diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 2773a5e8e9..b93e905c18 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -295,6 +295,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public event MuteListRequest OnMuteListRequest; public event AvatarInterestUpdate OnAvatarInterestUpdate; public event PlacesQuery OnPlacesQuery; + public event AgentFOV OnAgentFOV; #endregion Events @@ -346,6 +347,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected ulong m_activeGroupPowers; protected Dictionary m_groupPowers = new Dictionary(); protected int m_terrainCheckerCount; + protected uint m_agentFOVCounter; // These numbers are guesses at a decent tradeoff between responsiveness // of the interest list and throughput. Lower is more responsive, higher @@ -8871,20 +8873,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion + case PacketType.AgentFOV: + AgentFOVPacket fovPacket = (AgentFOVPacket)Pack; + + if (fovPacket.FOVBlock.GenCounter > m_agentFOVCounter) + { + m_agentFOVCounter = fovPacket.FOVBlock.GenCounter; + AgentFOV handlerAgentFOV = OnAgentFOV; + if (handlerAgentFOV != null) + { + handlerAgentFOV(this, fovPacket.FOVBlock.VerticalAngle); + } + } + break; #region unimplemented handlers - case PacketType.StartPingCheck: - StartPingCheckPacket pingStart = (StartPingCheckPacket)Pack; - CompletePingCheckPacket pingComplete = new CompletePingCheckPacket(); - pingComplete.PingID.PingID = pingStart.PingID.PingID; - m_udpServer.SendPacket(m_udpClient, pingComplete, ThrottleOutPacketType.Unknown, false); - break; - - case PacketType.CompletePingCheck: - // TODO: Do stats tracking or something with these? - break; - case PacketType.ViewerStats: // TODO: handle this packet //m_log.Warn("[CLIENT]: unhandled ViewerStats packet"); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 7a403aae96..4f3478b64c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -572,6 +572,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < ackPacket.Packets.Length; i++) AcknowledgePacket(udpClient, ackPacket.Packets[i].ID, now, packet.Header.Resent); } + + // We don't need to do anything else with PacketAck packets + return; } #endregion ACK Receiving @@ -579,20 +582,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region ACK Sending if (packet.Header.Reliable) + { udpClient.PendingAcks.Enqueue(packet.Header.Sequence); - // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, - // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove - // 2*MTU bytes from the value and send ACKs, and finally add the local value back to - // client.BytesSinceLastACK. Lockless thread safety - int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); - bytesSinceLastACK += buffer.DataLength; - if (bytesSinceLastACK > LLUDPServer.MTU * 2) - { - bytesSinceLastACK -= LLUDPServer.MTU * 2; - SendAcks(udpClient); + // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, + // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove + // 2*MTU bytes from the value and send ACKs, and finally add the local value back to + // client.BytesSinceLastACK. Lockless thread safety + int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); + bytesSinceLastACK += buffer.DataLength; + if (bytesSinceLastACK > LLUDPServer.MTU * 2) + { + bytesSinceLastACK -= LLUDPServer.MTU * 2; + SendAcks(udpClient); + } + Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); } - Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); #endregion ACK Sending @@ -612,12 +617,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Incoming Packet Accounting - // Don't bother clogging up the queue with PacketAck packets that are already handled here - if (packet.Type != PacketType.PacketAck) + #region Ping Check Handling + + if (packet.Type == PacketType.StartPingCheck) { - // Inbox insertion - packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); + // We don't need to do anything else with ping checks + StartPingCheckPacket startPing = (StartPingCheckPacket)packet; + + CompletePingCheckPacket completePing = new CompletePingCheckPacket(); + completePing.PingID.PingID = startPing.PingID.PingID; + SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false); + return; } + else if (packet.Type == PacketType.CompletePingCheck) + { + // We don't currently track client ping times + return; + } + + #endregion Ping Check Handling + + // Inbox insertion + packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); } protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent) From 2f2eeb6731f4b692dafd17afa88019c24e361a36 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sun, 18 Oct 2009 02:53:36 -0700 Subject: [PATCH 21/61] Zero out PrimitiveBaseShape.SculptData after the JPEG2000 data has been decoded to allow garbage collection on it --- OpenSim/Region/Physics/Meshing/Meshmerizer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs index 1ea08e250c..f609e738ec 100644 --- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs +++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs @@ -289,6 +289,9 @@ namespace OpenSim.Region.Physics.Meshing ManagedImage managedImage; // we never use this OpenJPEG.DecodeToImage(primShape.SculptData, out managedImage, out idata); + // Remove the reference to the encoded JPEG2000 data so it can be GCed + primShape.SculptData = Utils.EmptyBytes; + if (cacheSculptMaps) { try { idata.Save(decodedSculptFileName, ImageFormat.MemoryBmp); } From 1dbbf6edb6c913697ac3248ef5b74c943a7a9c23 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sun, 18 Oct 2009 03:15:36 -0700 Subject: [PATCH 22/61] * Process the avatar terse update priority queue as soon as an update for our own avatar is ready to send * Reduce the scope of the locks when processing the update queues * Reuse the ImprovedTerseObjectUpdate.RegionData block --- .../ClientStack/LindenUDP/LLClientView.cs | 79 +++++++++---------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index b93e905c18..83a71844b4 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3402,34 +3402,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP ImprovedTerseObjectUpdatePacket.ObjectDataBlock terseBlock = CreateAvatarImprovedBlock(data.localID, data.position, data.velocity, rotation); - + lock (m_avatarTerseUpdates.SyncRoot) m_avatarTerseUpdates.Enqueue(data.priority, terseBlock, data.localID); + + // If we received an update about our own avatar, process the avatar update priority queue immediately + if (data.agentid == m_agentId) + ProcessAvatarTerseUpdates(); } private void ProcessAvatarTerseUpdates() { + ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); + terse.Header.Reliable = false; + terse.Header.Zerocoded = true; + + //terse.RegionData = new ImprovedTerseObjectUpdatePacket.RegionDataBlock(); + terse.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; + terse.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); + lock (m_avatarTerseUpdates.SyncRoot) { - ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); - - terse.RegionData = new ImprovedTerseObjectUpdatePacket.RegionDataBlock(); - - terse.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; - terse.RegionData.TimeDilation = - (ushort)(Scene.TimeDilation * ushort.MaxValue); - int count = Math.Min(m_avatarTerseUpdates.Count, m_avatarTerseUpdatesPerPacket); + if (count == 0) + return; terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; for (int i = 0; i < count; i++) terse.ObjectData[i] = m_avatarTerseUpdates.Dequeue(); - - terse.Header.Reliable = false; - terse.Header.Zerocoded = true; - - OutPacket(terse, ThrottleOutPacketType.Task); } + + OutPacket(terse, ThrottleOutPacketType.Task); } public void SendCoarseLocationUpdate(List users, List CoarseLocations) @@ -3642,26 +3645,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP void ProcessPrimFullUpdates() { + ObjectUpdatePacket outPacket = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); + outPacket.Header.Zerocoded = true; + + //outPacket.RegionData = new ObjectUpdatePacket.RegionDataBlock(); + outPacket.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; + outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); + lock (m_primFullUpdates.SyncRoot) { - ObjectUpdatePacket outPacket = - (ObjectUpdatePacket)PacketPool.Instance.GetPacket( - PacketType.ObjectUpdate); - - outPacket.RegionData.RegionHandle = - Scene.RegionInfo.RegionHandle; - outPacket.RegionData.TimeDilation = - (ushort)(Scene.TimeDilation * ushort.MaxValue); - int count = Math.Min(m_primFullUpdates.Count, m_primFullUpdatesPerPacket); + if (count == 0) + return; outPacket.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[count]; for (int i = 0; i < count; i++) outPacket.ObjectData[i] = m_primFullUpdates.Dequeue(); - - outPacket.Header.Zerocoded = true; - OutPacket(outPacket, ThrottleOutPacketType.State); } + + OutPacket(outPacket, ThrottleOutPacketType.State); } ///

@@ -3695,28 +3697,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP void ProcessPrimTerseUpdates() { + ImprovedTerseObjectUpdatePacket outPacket = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); + outPacket.Header.Reliable = false; + outPacket.Header.Zerocoded = true; + + outPacket.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; + outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); + lock (m_primTerseUpdates.SyncRoot) { - ImprovedTerseObjectUpdatePacket outPacket = - (ImprovedTerseObjectUpdatePacket) - PacketPool.Instance.GetPacket( - PacketType.ImprovedTerseObjectUpdate); - - outPacket.RegionData.RegionHandle = - Scene.RegionInfo.RegionHandle; - outPacket.RegionData.TimeDilation = - (ushort)(Scene.TimeDilation * ushort.MaxValue); - int count = Math.Min(m_primTerseUpdates.Count, m_primTerseUpdatesPerPacket); + if (count == 0) + return; outPacket.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; for (int i = 0; i < count; i++) outPacket.ObjectData[i] = m_primTerseUpdates.Dequeue(); - - outPacket.Header.Reliable = false; - outPacket.Header.Zerocoded = true; - OutPacket(outPacket, ThrottleOutPacketType.State); } + + OutPacket(outPacket, ThrottleOutPacketType.State); } public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) From baed19d0688fe03fe8fba233bc6bd8306c71779f Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 18 Oct 2009 16:48:44 -0700 Subject: [PATCH 23/61] A bit of instrumentation to figure out what's going on with physics actors. --- OpenSim/Region/Physics/OdePlugin/ODECharacter.cs | 9 ++++++++- OpenSim/Region/Physics/OdePlugin/OdePlugin.cs | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs index 1fff8469ac..ef0e56e05f 100644 --- a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs +++ b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs @@ -139,8 +139,14 @@ namespace OpenSim.Region.Physics.OdePlugin public int m_eventsubscription = 0; private CollisionEventUpdate CollisionEventsThisFrame = new CollisionEventUpdate(); + // unique UUID of this character object + public UUID m_uuid; + public bool bad = false; + public OdeCharacter(String avName, OdeScene parent_scene, PhysicsVector pos, CollisionLocker dode, PhysicsVector size, float pid_d, float pid_p, float capsule_radius, float tensor, float density, float height_fudge_factor, float walk_divisor, float rundivisor) { + m_uuid = UUID.Random(); + // ode = dode; _velocity = new PhysicsVector(); _target_velocity = new PhysicsVector(); @@ -1112,10 +1118,11 @@ namespace OpenSim.Region.Physics.OdePlugin } catch (NullReferenceException) { + bad = true; _parent_scene.BadCharacter(this); vec = new d.Vector3(_position.X, _position.Y, _position.Z); base.RaiseOutOfBounds(_position); // Tells ScenePresence that there's a problem! - m_log.WarnFormat("[ODEPLUGIN]: Avatar Null reference for Avatar: {0}", m_name); + m_log.WarnFormat("[ODEPLUGIN]: Avatar Null reference for Avatar {0}, physical actor {1}", m_name, m_uuid); } diff --git a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs index 0e03e81eac..7187fbecef 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs @@ -1664,6 +1664,8 @@ namespace OpenSim.Region.Physics.OdePlugin if (!_characters.Contains(chr)) { _characters.Add(chr); + if (chr.bad) + m_log.DebugFormat("[PHYSICS] Added BAD actor {0} to characters list", chr.m_uuid); } } } @@ -2581,7 +2583,11 @@ namespace OpenSim.Region.Physics.OdePlugin lock (_taintedActors) { if (!(_taintedActors.Contains(taintedchar))) + { _taintedActors.Add(taintedchar); + if (taintedchar.bad) + m_log.DebugFormat("[PHYSICS]: Added BAD actor {0} to tainted actors", taintedchar.m_uuid); + } } } } From 233e16b99cc80190d41143ecdfe01308eb39932a Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sun, 18 Oct 2009 20:24:20 -0700 Subject: [PATCH 24/61] * Rewrote the methods that build ObjectUpdate and ImprovedTerseObjectUpdate packets to fill in the data more accurately and avoid allocating memory that is immediately thrown away * Changed the Send*Data structs in IClientAPI to use public readonly members instead of private members and getters * Made Parallel.ProcessorCount public * Started switching over packet building methods in LLClientView to use Util.StringToBytes[256/1024]() instead of Utils.StringToBytes() * More cleanup of the ScenePresences vs. ClientManager nightmare * ScenePresence.HandleAgentUpdate() will now time out and drop incoming AgentUpdate packets after three seconds. This fixes a deadlock on m_AgentUpdates that was blocking up the LLUDP server --- .../Client/MXP/ClientStack/MXPClientView.cs | 20 +- OpenSim/Framework/IClientAPI.cs | 182 ++- OpenSim/Framework/Parallel.cs | 8 +- .../ClientStack/LindenUDP/LLClientView.cs | 1107 ++++++----------- .../ClientStack/LindenUDP/LLUDPServer.cs | 4 +- OpenSim/Region/Framework/Scenes/Scene.cs | 35 +- .../Framework/Scenes/SceneObjectPart.cs | 6 +- .../Region/Framework/Scenes/ScenePresence.cs | 22 +- 8 files changed, 538 insertions(+), 846 deletions(-) diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index 52110d6ef8..204603d3b0 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -999,16 +999,16 @@ namespace OpenSim.Client.MXP.ClientStack public void SendAvatarData(SendAvatarData data) { //ScenePresence presence=((Scene)this.Scene).GetScenePresence(avatarID); - UUID ownerID = data.avatarID; - MXPSendAvatarData(data.firstName + " " + data.lastName, ownerID, UUID.Zero, data.avatarID, data.avatarLocalID, data.Pos, data.rotation); + UUID ownerID = data.AvatarID; + MXPSendAvatarData(data.FirstName + " " + data.LastName, ownerID, UUID.Zero, data.AvatarID, data.AvatarLocalID, data.Position, data.Rotation); } public void SendAvatarTerseUpdate(SendAvatarTerseData data) { MovementEventMessage me = new MovementEventMessage(); - me.ObjectIndex = data.localID; - me.Location = ToOmVector(data.position); - me.Orientation = ToOmQuaternion(data.rotation); + me.ObjectIndex = data.LocalID; + me.Location = ToOmVector(data.Position); + me.Orientation = ToOmQuaternion(data.Rotation); Session.Send(me); } @@ -1030,15 +1030,17 @@ namespace OpenSim.Client.MXP.ClientStack public void SendPrimitiveToClient(SendPrimitiveData data) { - MXPSendPrimitive(data.localID, data.ownerID, data.acc, data.rvel, data.primShape, data.pos, data.objectID, data.vel, data.rotation, data.flags, data.text, data.color, data.parentID, data.particleSystem, data.clickAction, data.material, data.textureanim); + MXPSendPrimitive(data.localID, data.ownerID, data.acc, data.rvel, data.primShape, data.pos, data.objectID, data.vel, + data.rotation, (uint)data.flags, data.text, data.color, data.parentID, data.particleSystem, data.clickAction, + data.material, data.textureanim); } public void SendPrimTerseUpdate(SendPrimitiveTerseData data) { MovementEventMessage me = new MovementEventMessage(); - me.ObjectIndex = data.localID; - me.Location = ToOmVector(data.position); - me.Orientation = ToOmQuaternion(data.rotation); + me.ObjectIndex = data.LocalID; + me.Location = ToOmVector(data.Position); + me.Orientation = ToOmQuaternion(data.Rotation); Session.Send(me); } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 1e03d4c012..d304345b83 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -523,124 +523,100 @@ namespace OpenSim.Framework public struct SendAvatarData { - private ulong m_regionHandle; - private string m_firstName; - private string m_lastName; - private string m_grouptitle; - private UUID m_avatarID; - private uint m_avatarLocalID; - private Vector3 m_Pos; - private byte[] m_textureEntry; - private uint m_parentID; - private Quaternion m_rotation; + public readonly ulong RegionHandle; + public readonly string FirstName; + public readonly string LastName; + public readonly string GroupTitle; + public readonly UUID AvatarID; + public readonly uint AvatarLocalID; + public readonly Vector3 Position; + public readonly byte[] TextureEntry; + public readonly uint ParentID; + public readonly Quaternion Rotation; - public SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, - uint avatarLocalID, - Vector3 Pos, byte[] textureEntry, uint parentID, Quaternion rotation) + public SendAvatarData(ulong regionHandle, string firstName, string lastName, string groupTitle, UUID avatarID, + uint avatarLocalID, Vector3 position, byte[] textureEntry, uint parentID, Quaternion rotation) { - this.m_regionHandle = regionHandle; - this.m_firstName = firstName; - this.m_lastName = lastName; - this.m_grouptitle = grouptitle; - this.m_avatarID = avatarID; - this.m_avatarLocalID = avatarLocalID; - this.m_Pos = Pos; - this.m_textureEntry = textureEntry; - this.m_parentID = parentID; - this.m_rotation = rotation; + RegionHandle = regionHandle; + FirstName = firstName; + LastName = lastName; + GroupTitle = groupTitle; + AvatarID = avatarID; + AvatarLocalID = avatarLocalID; + Position = position; + TextureEntry = textureEntry; + ParentID = parentID; + Rotation = rotation; } - - public ulong regionHandle { get { return this.m_regionHandle; } } - public string firstName { get { return this.m_firstName; } } - public string lastName { get { return this.m_lastName; } } - public string grouptitle { get { return this.m_grouptitle; } } - public UUID avatarID { get { return this.m_avatarID; } } - public uint avatarLocalID { get { return this.m_avatarLocalID; } } - public Vector3 Pos { get { return this.m_Pos; } } - public byte[] textureEntry { get { return this.m_textureEntry; } } - public uint parentID { get { return this.m_parentID; } } - public Quaternion rotation { get { return this.m_rotation; } } } public struct SendAvatarTerseData { - private ulong m_regionHandle; - private ushort m_timeDilation; - private uint m_localID; - private Vector3 m_position; - private Vector3 m_velocity; - private Quaternion m_rotation; - private UUID m_agentid; - private double m_priority; + public readonly ulong RegionHandle; + public readonly ushort TimeDilation; + public readonly uint LocalID; + public readonly Vector3 Position; + public readonly Vector3 Velocity; + public readonly Vector3 Acceleration; + public readonly Quaternion Rotation; + public readonly Vector4 CollisionPlane; + public readonly UUID AgentID; + public readonly byte[] TextureEntry; + public readonly double Priority; - public SendAvatarTerseData(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, - Vector3 velocity, Quaternion rotation, UUID agentid, double priority) + public SendAvatarTerseData(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, Vector3 velocity, + Vector3 acceleration, Quaternion rotation, Vector4 collisionPlane, UUID agentid, byte[] textureEntry, double priority) { - this.m_regionHandle = regionHandle; - this.m_timeDilation = timeDilation; - this.m_localID = localID; - this.m_position = position; - this.m_velocity = velocity; - this.m_rotation = rotation; - this.m_agentid = agentid; - this.m_priority = priority; + RegionHandle = regionHandle; + TimeDilation = timeDilation; + LocalID = localID; + Position = position; + Velocity = velocity; + Acceleration = acceleration; + Rotation = rotation; + CollisionPlane = collisionPlane; + AgentID = agentid; + TextureEntry = textureEntry; + Priority = priority; } - - public ulong regionHandle { get { return this.m_regionHandle; } } - public ushort timeDilation { get { return this.m_timeDilation; } } - public uint localID { get { return this.m_localID; } } - public Vector3 position { get { return this.m_position; } } - public Vector3 velocity { get { return this.m_velocity; } } - public Quaternion rotation { get { return this.m_rotation; } } - public UUID agentid { get { return this.m_agentid; } } - public double priority { get { return this.m_priority; } } } public struct SendPrimitiveTerseData { - private ulong m_regionHandle; - private ushort m_timeDilation; - private uint m_localID; - private Vector3 m_position; - private Quaternion m_rotation; - private Vector3 m_velocity; - private Vector3 m_rotationalvelocity; - private byte m_state; - private UUID m_AssetId; - private UUID m_owner; - private int m_attachPoint; - private double m_priority; + public readonly ulong RegionHandle; + public readonly ushort TimeDilation; + public readonly uint LocalID; + public readonly Vector3 Position; + public readonly Quaternion Rotation; + public readonly Vector3 Velocity; + public readonly Vector3 Acceleration; + public readonly Vector3 AngularVelocity; + public readonly byte State; + public readonly UUID AssetID; + public readonly UUID OwnerID; + public readonly int AttachPoint; + public readonly byte[] TextureEntry; + public readonly double Priority; public SendPrimitiveTerseData(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, - Quaternion rotation, Vector3 velocity, Vector3 rotationalvelocity, byte state, - UUID AssetId, UUID owner, int attachPoint, double priority) + Quaternion rotation, Vector3 velocity, Vector3 acceleration, Vector3 rotationalvelocity, byte state, + UUID assetID, UUID ownerID, int attachPoint, byte[] textureEntry, double priority) { - this.m_regionHandle = regionHandle; - this.m_timeDilation = timeDilation; - this.m_localID = localID; - this.m_position = position; - this.m_rotation = rotation; - this.m_velocity = velocity; - this.m_rotationalvelocity = rotationalvelocity; - this.m_state = state; - this.m_AssetId = AssetId; - this.m_owner = owner; - this.m_attachPoint = attachPoint; - this.m_priority = priority; + RegionHandle = regionHandle; + TimeDilation = timeDilation; + LocalID = localID; + Position = position; + Rotation = rotation; + Velocity = velocity; + Acceleration = acceleration; + AngularVelocity = rotationalvelocity; + State = state; + AssetID = assetID; + OwnerID = ownerID; + AttachPoint = attachPoint; + TextureEntry = textureEntry; + Priority = priority; } - - public ulong regionHandle { get { return this.m_regionHandle; } } - public ushort timeDilation { get { return this.m_timeDilation; } } - public uint localID { get { return this.m_localID; } } - public Vector3 position { get { return this.m_position; } } - public Quaternion rotation { get { return this.m_rotation; } } - public Vector3 velocity { get { return this.m_velocity; } } - public Vector3 rotationalvelocity { get { return this.m_rotationalvelocity; } } - public byte state { get { return this.m_state; } } - public UUID AssetId { get { return this.m_AssetId; } } - public UUID owner { get { return this.m_owner; } } - public int attachPoint { get { return this.m_attachPoint; } } - public double priority { get { return this.m_priority; } } } public struct SendPrimitiveData @@ -654,7 +630,7 @@ namespace OpenSim.Framework private Vector3 m_acc; private Quaternion m_rotation; private Vector3 m_rvel; - private uint m_flags; + private PrimFlags m_flags; private UUID m_objectID; private UUID m_ownerID; private string m_text; @@ -699,7 +675,7 @@ namespace OpenSim.Framework this.m_acc = acc; this.m_rotation = rotation; this.m_rvel = rvel; - this.m_flags = flags; + this.m_flags = (PrimFlags)flags; this.m_objectID = objectID; this.m_ownerID = ownerID; this.m_text = text; @@ -728,7 +704,7 @@ namespace OpenSim.Framework public Vector3 acc { get { return this.m_acc; } } public Quaternion rotation { get { return this.m_rotation; } } public Vector3 rvel { get { return this.m_rvel; } } - public uint flags { get { return this.m_flags; } } + public PrimFlags flags { get { return this.m_flags; } } public UUID objectID { get { return this.m_objectID; } } public UUID ownerID { get { return this.m_ownerID; } } public string text { get { return this.m_text; } } diff --git a/OpenSim/Framework/Parallel.cs b/OpenSim/Framework/Parallel.cs index 6efdad022c..cf4f773147 100644 --- a/OpenSim/Framework/Parallel.cs +++ b/OpenSim/Framework/Parallel.cs @@ -36,7 +36,7 @@ namespace OpenSim.Framework /// public static class Parallel { - private static readonly int processorCount = System.Environment.ProcessorCount; + public static readonly int ProcessorCount = System.Environment.ProcessorCount; /// /// Executes a for loop in which iterations may run in parallel @@ -46,7 +46,7 @@ namespace OpenSim.Framework /// Method body to run for each iteration of the loop public static void For(int fromInclusive, int toExclusive, Action body) { - For(processorCount, fromInclusive, toExclusive, body); + For(ProcessorCount, fromInclusive, toExclusive, body); } /// @@ -103,7 +103,7 @@ namespace OpenSim.Framework /// Method body to run for each object in the collection public static void ForEach(IEnumerable enumerable, Action body) { - ForEach(processorCount, enumerable, body); + ForEach(ProcessorCount, enumerable, body); } /// @@ -161,7 +161,7 @@ namespace OpenSim.Framework /// A series of method bodies to execute public static void Invoke(params Action[] actions) { - Invoke(processorCount, actions); + Invoke(ProcessorCount, actions); } /// diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 83a71844b4..1c463ea9ac 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -342,7 +342,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected Thread m_clientThread; protected Vector3 m_startpos; protected EndPoint m_userEndPoint; - protected UUID m_activeGroupID = UUID.Zero; + protected UUID m_activeGroupID; protected string m_activeGroupName = String.Empty; protected ulong m_activeGroupPowers; protected Dictionary m_groupPowers = new Dictionary(); @@ -422,7 +422,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_hyperAssets = m_scene.RequestModuleInterface(); m_GroupsModule = scene.RequestModuleInterface(); m_imageManager = new LLImageManager(this, m_assetService, Scene.RequestModuleInterface()); - m_channelVersion = Utils.StringToBytes(scene.GetSimulatorVersion()); + m_channelVersion = Util.StringToBytes256(scene.GetSimulatorVersion()); m_agentId = agentId; m_sessionId = sessionId; m_secureSessionId = sessionInfo.LoginInfo.SecureSession; @@ -498,7 +498,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP kupack.UserInfo.SessionID = SessionId; kupack.TargetBlock.TargetIP = 0; kupack.TargetBlock.TargetPort = 0; - kupack.UserInfo.Reason = Utils.StringToBytes(message); + kupack.UserInfo.Reason = Util.StringToBytes256(message); OutPacket(kupack, ThrottleOutPacketType.Task); // You must sleep here or users get no message! Thread.Sleep(500); @@ -643,7 +643,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP handshake.RegionInfo.WaterHeight = args.waterHeight; handshake.RegionInfo.RegionFlags = args.regionFlags; - handshake.RegionInfo.SimName = Utils.StringToBytes(args.regionName); + handshake.RegionInfo.SimName = Util.StringToBytes256(args.regionName); handshake.RegionInfo.SimOwner = args.SimOwner; handshake.RegionInfo.TerrainBase0 = args.terrainBase0; handshake.RegionInfo.TerrainBase1 = args.terrainBase1; @@ -699,11 +699,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP { ChatFromSimulatorPacket reply = (ChatFromSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.ChatFromSimulator); reply.ChatData.Audible = audible; - reply.ChatData.Message = Utils.StringToBytes(message); + reply.ChatData.Message = Util.StringToBytes1024(message); reply.ChatData.ChatType = type; reply.ChatData.SourceType = source; reply.ChatData.Position = fromPos; - reply.ChatData.FromName = Utils.StringToBytes(fromName); + reply.ChatData.FromName = Util.StringToBytes256(fromName); reply.ChatData.OwnerID = fromAgentID; reply.ChatData.SourceID = fromAgentID; @@ -724,7 +724,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP msg.AgentData.AgentID = new UUID(im.fromAgentID); msg.AgentData.SessionID = UUID.Zero; - msg.MessageBlock.FromAgentName = Utils.StringToBytes(im.fromAgentName); + msg.MessageBlock.FromAgentName = Util.StringToBytes256(im.fromAgentName); msg.MessageBlock.Dialog = im.dialog; msg.MessageBlock.FromGroup = im.fromGroup; if (im.imSessionID == UUID.Zero.Guid) @@ -737,12 +737,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP msg.MessageBlock.RegionID = new UUID(im.RegionID); msg.MessageBlock.Timestamp = im.timestamp; msg.MessageBlock.ToAgentID = new UUID(im.toAgentID); - // Cap the message length at 1099. There is a limit in ImprovedInstantMessagePacket - // the limit is 1100 but a 0 byte gets added to mark the end of the string - if (im.message != null && im.message.Length > 1099) - msg.MessageBlock.Message = Utils.StringToBytes(im.message.Substring(0, 1099)); - else - msg.MessageBlock.Message = Utils.StringToBytes(im.message); + msg.MessageBlock.Message = Util.StringToBytes1024(im.message); msg.MessageBlock.BinaryBucket = im.binaryBucket; if (im.message.StartsWith("[grouptest]")) @@ -760,7 +755,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP eq.ChatterboxInvitation( new UUID("00000000-68f9-1111-024e-222222111123"), "OpenSimulator Testing", new UUID(im.fromAgentID), im.message, new UUID(im.toAgentID), im.fromAgentName, im.dialog, 0, - false, 0, new Vector3(), 1, new UUID(im.imSessionID), im.fromGroup, Utils.StringToBytes("OpenSimulator Testing")); + false, 0, new Vector3(), 1, new UUID(im.imSessionID), im.fromGroup, Util.StringToBytes256("OpenSimulator Testing")); eq.ChatterBoxSessionAgentListUpdates( new UUID("00000000-68f9-1111-024e-222222111123"), @@ -777,13 +772,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendGenericMessage(string method, List message) { GenericMessagePacket gmp = new GenericMessagePacket(); - gmp.MethodData.Method = Utils.StringToBytes(method); + gmp.MethodData.Method = Util.StringToBytes256(method); gmp.ParamList = new GenericMessagePacket.ParamListBlock[message.Count]; int i = 0; foreach (string val in message) { gmp.ParamList[i] = new GenericMessagePacket.ParamListBlock(); - gmp.ParamList[i++].Parameter = Utils.StringToBytes(val); + gmp.ParamList[i++].Parameter = Util.StringToBytes256(val); } OutPacket(gmp, ThrottleOutPacketType.Task); } @@ -1047,7 +1042,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP newSimPack.RegionData.SimIP += (uint)byteIP[1] << 8; newSimPack.RegionData.SimIP += (uint)byteIP[0]; newSimPack.RegionData.SimPort = (ushort)externalIPEndPoint.Port; - newSimPack.RegionData.SeedCapability = Utils.StringToBytes(capsURL); + newSimPack.RegionData.SeedCapability = Util.StringToBytes256(capsURL); // Hack to get this out immediately and skip throttles OutPacket(newSimPack, ThrottleOutPacketType.Unknown); @@ -1125,7 +1120,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP teleport.Info.RegionHandle = regionHandle; teleport.Info.SimAccess = simAccess; - teleport.Info.SeedCapability = Utils.StringToBytes(capsURL); + teleport.Info.SeedCapability = Util.StringToBytes256(capsURL); IPAddress oIP = newRegionEndPoint.Address; byte[] byteIP = oIP.GetAddressBytes(); @@ -1150,7 +1145,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { TeleportFailedPacket tpFailed = (TeleportFailedPacket)PacketPool.Instance.GetPacket(PacketType.TeleportFailed); tpFailed.Info.AgentID = AgentId; - tpFailed.Info.Reason = Utils.StringToBytes(reason); + tpFailed.Info.Reason = Util.StringToBytes256(reason); tpFailed.AlertInfo = new TeleportFailedPacket.AlertInfoBlock[0]; // Hack to get this out immediately and skip throttles @@ -1882,11 +1877,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP AgentDataUpdatePacket sendAgentDataUpdate = (AgentDataUpdatePacket)PacketPool.Instance.GetPacket(PacketType.AgentDataUpdate); sendAgentDataUpdate.AgentData.ActiveGroupID = activegroupid; sendAgentDataUpdate.AgentData.AgentID = agentid; - sendAgentDataUpdate.AgentData.FirstName = Utils.StringToBytes(firstname); - sendAgentDataUpdate.AgentData.GroupName = Utils.StringToBytes(groupname); + sendAgentDataUpdate.AgentData.FirstName = Util.StringToBytes256(firstname); + sendAgentDataUpdate.AgentData.GroupName = Util.StringToBytes256(groupname); sendAgentDataUpdate.AgentData.GroupPowers = grouppowers; - sendAgentDataUpdate.AgentData.GroupTitle = Utils.StringToBytes(grouptitle); - sendAgentDataUpdate.AgentData.LastName = Utils.StringToBytes(lastname); + sendAgentDataUpdate.AgentData.GroupTitle = Util.StringToBytes256(grouptitle); + sendAgentDataUpdate.AgentData.LastName = Util.StringToBytes256(lastname); OutPacket(sendAgentDataUpdate, ThrottleOutPacketType.Task); } @@ -1899,7 +1894,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { AlertMessagePacket alertPack = (AlertMessagePacket)PacketPool.Instance.GetPacket(PacketType.AlertMessage); alertPack.AlertData = new AlertMessagePacket.AlertDataBlock(); - alertPack.AlertData.Message = Utils.StringToBytes(message); + alertPack.AlertData.Message = Util.StringToBytes256(message); alertPack.AlertInfo = new AlertMessagePacket.AlertInfoBlock[0]; OutPacket(alertPack, ThrottleOutPacketType.Task); } @@ -1926,7 +1921,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { AgentAlertMessagePacket alertPack = (AgentAlertMessagePacket)PacketPool.Instance.GetPacket(PacketType.AgentAlertMessage); alertPack.AgentData.AgentID = AgentId; - alertPack.AlertData.Message = Utils.StringToBytes(message); + alertPack.AlertData.Message = Util.StringToBytes256(message); alertPack.AlertData.Modal = modal; return alertPack; @@ -1936,12 +1931,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP string url) { LoadURLPacket loadURL = (LoadURLPacket)PacketPool.Instance.GetPacket(PacketType.LoadURL); - loadURL.Data.ObjectName = Utils.StringToBytes(objectname); + loadURL.Data.ObjectName = Util.StringToBytes256(objectname); loadURL.Data.ObjectID = objectID; loadURL.Data.OwnerID = ownerID; loadURL.Data.OwnerIsGroup = groupOwned; - loadURL.Data.Message = Utils.StringToBytes(message); - loadURL.Data.URL = Utils.StringToBytes(url); + loadURL.Data.Message = Util.StringToBytes256(message); + loadURL.Data.URL = Util.StringToBytes256(url); OutPacket(loadURL, ThrottleOutPacketType.Task); } @@ -1949,18 +1944,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP { ScriptDialogPacket dialog = (ScriptDialogPacket)PacketPool.Instance.GetPacket(PacketType.ScriptDialog); dialog.Data.ObjectID = objectID; - dialog.Data.ObjectName = Utils.StringToBytes(objectname); + dialog.Data.ObjectName = Util.StringToBytes256(objectname); // this is the username of the *owner* - dialog.Data.FirstName = Utils.StringToBytes(ownerFirstName); - dialog.Data.LastName = Utils.StringToBytes(ownerLastName); - dialog.Data.Message = Utils.StringToBytes(msg); + dialog.Data.FirstName = Util.StringToBytes256(ownerFirstName); + dialog.Data.LastName = Util.StringToBytes256(ownerLastName); + dialog.Data.Message = Util.StringToBytes1024(msg); dialog.Data.ImageID = textureID; dialog.Data.ChatChannel = ch; ScriptDialogPacket.ButtonsBlock[] buttons = new ScriptDialogPacket.ButtonsBlock[buttonlabels.Length]; for (int i = 0; i < buttonlabels.Length; i++) { buttons[i] = new ScriptDialogPacket.ButtonsBlock(); - buttons[i].ButtonLabel = Utils.StringToBytes(buttonlabels[i]); + buttons[i].ButtonLabel = Util.StringToBytes256(buttonlabels[i]); } dialog.Buttons = buttons; OutPacket(dialog, ThrottleOutPacketType.Task); @@ -2116,19 +2111,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP avatarReply.AgentData.AgentID = AgentId; avatarReply.AgentData.AvatarID = avatarID; if (aboutText != null) - avatarReply.PropertiesData.AboutText = Utils.StringToBytes(aboutText); + avatarReply.PropertiesData.AboutText = Util.StringToBytes1024(aboutText); else - avatarReply.PropertiesData.AboutText = Utils.StringToBytes(""); - avatarReply.PropertiesData.BornOn = Utils.StringToBytes(bornOn); + avatarReply.PropertiesData.AboutText = Utils.EmptyBytes; + avatarReply.PropertiesData.BornOn = Util.StringToBytes256(bornOn); avatarReply.PropertiesData.CharterMember = charterMember; if (flAbout != null) - avatarReply.PropertiesData.FLAboutText = Utils.StringToBytes(flAbout); + avatarReply.PropertiesData.FLAboutText = Util.StringToBytes256(flAbout); else - avatarReply.PropertiesData.FLAboutText = Utils.StringToBytes(""); + avatarReply.PropertiesData.FLAboutText = Utils.EmptyBytes; avatarReply.PropertiesData.Flags = flags; avatarReply.PropertiesData.FLImageID = flImageID; avatarReply.PropertiesData.ImageID = imageID; - avatarReply.PropertiesData.ProfileURL = Utils.StringToBytes(profileURL); + avatarReply.PropertiesData.ProfileURL = Util.StringToBytes256(profileURL); avatarReply.PropertiesData.PartnerID = partnerID; OutPacket(avatarReply, ThrottleOutPacketType.Task); } @@ -2255,7 +2250,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP Group.Contribution = GroupMembership[i].Contribution; Group.GroupID = GroupMembership[i].GroupID; Group.GroupInsigniaID = GroupMembership[i].GroupPicture; - Group.GroupName = Utils.StringToBytes(GroupMembership[i].GroupName); + Group.GroupName = Util.StringToBytes256(GroupMembership[i].GroupName); Group.GroupPowers = GroupMembership[i].GroupPowers; Groups[i] = Group; @@ -2289,7 +2284,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP UUIDGroupNameReplyPacket.UUIDNameBlockBlock[] uidnameblock = new UUIDGroupNameReplyPacket.UUIDNameBlockBlock[1]; UUIDGroupNameReplyPacket.UUIDNameBlockBlock uidnamebloc = new UUIDGroupNameReplyPacket.UUIDNameBlockBlock(); uidnamebloc.ID = groupLLUID; - uidnamebloc.GroupName = Utils.StringToBytes(GroupName); + uidnamebloc.GroupName = Util.StringToBytes256(GroupName); uidnameblock[0] = uidnamebloc; pack.UUIDNameBlock = uidnameblock; OutPacket(pack, ThrottleOutPacketType.Task); @@ -2314,8 +2309,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP lsrepdb.Score = lsrpia[i].Score; lsrepdb.TaskID = lsrpia[i].TaskID; lsrepdb.TaskLocalID = lsrpia[i].TaskLocalID; - lsrepdb.TaskName = Utils.StringToBytes(lsrpia[i].TaskName); - lsrepdb.OwnerName = Utils.StringToBytes(lsrpia[i].OwnerName); + lsrepdb.TaskName = Util.StringToBytes256(lsrpia[i].TaskName); + lsrepdb.OwnerName = Util.StringToBytes256(lsrpia[i].OwnerName); lsrepdba[i] = lsrepdb; } lsrp.ReportData = lsrepdba; @@ -3257,127 +3252,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP canUseImproved = false; } } - } - - static ObjectUpdatePacket.ObjectDataBlock BuildUpdateBlockFromPrim(SceneObjectPart prim, UUID assetID, PrimFlags flags, uint crc) - { - byte[] objectData = new byte[60]; - prim.OffsetPosition.ToBytes(objectData, 0); - prim.Velocity.ToBytes(objectData, 12); - prim.Acceleration.ToBytes(objectData, 24); - prim.RotationOffset.ToBytes(objectData, 36); - prim.AngularVelocity.ToBytes(objectData, 48); - - ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); - update.ClickAction = (byte)prim.ClickAction; - update.CRC = crc; - update.ExtraParams = prim.Shape.ExtraParams ?? Utils.EmptyBytes; - update.Flags = (byte)flags; - update.FullID = prim.UUID; - update.ID = prim.LocalId; - //update.JointAxisOrAnchor = Vector3.Zero; // These are deprecated - //update.JointPivot = Vector3.Zero; - //update.JointType = 0; - update.Material = prim.Material; - update.MediaURL = Utils.EmptyBytes; // FIXME: Support this in OpenSim - if (prim.IsAttachment) - update.NameValue = Util.StringToBytes256("AttachItemID STRING RW SV " + assetID); - else - update.NameValue = Utils.EmptyBytes; - update.ObjectData = objectData; - update.ParentID = prim.ParentID; - update.PathBegin = prim.Shape.PathBegin; - update.PathCurve = prim.Shape.PathCurve; - update.PathEnd = prim.Shape.PathEnd; - update.PathRadiusOffset = prim.Shape.PathRadiusOffset; - update.PathRevolutions = prim.Shape.PathRevolutions; - update.PathScaleX = prim.Shape.PathScaleX; - update.PathScaleY = prim.Shape.PathScaleY; - update.PathShearX = prim.Shape.PathShearX; - update.PathShearY = prim.Shape.PathShearY; - update.PathSkew = prim.Shape.PathSkew; - update.PathTaperX = prim.Shape.PathTaperX; - update.PathTaperY = prim.Shape.PathTaperY; - update.PathTwist = prim.Shape.PathTwist; - update.PathTwistBegin = prim.Shape.PathTwistBegin; - update.PCode = prim.Shape.PCode; - update.ProfileBegin = prim.Shape.ProfileBegin; - update.ProfileCurve = prim.Shape.ProfileCurve; - update.ProfileEnd = prim.Shape.ProfileEnd; - update.ProfileHollow = prim.Shape.ProfileHollow; - update.PSBlock = prim.ParticleSystem ?? Utils.EmptyBytes; - update.TextColor = new Color4(prim.Color).GetBytes(true); - update.TextureAnim = prim.TextureAnimation ?? Utils.EmptyBytes; - update.TextureEntry = prim.Shape.TextureEntry ?? Utils.EmptyBytes; - update.Scale = prim.Scale; - update.State = prim.Shape.State; - update.Text = Util.StringToBytes256(prim.Text); - update.UpdateFlags = (uint)flags; - - if (prim.Sound != UUID.Zero) - { - update.Sound = prim.Sound; - update.OwnerID = prim.OwnerID; - update.Gain = (float)prim.SoundGain; - update.Radius = (float)prim.SoundRadius; - } - - switch ((PCode)prim.Shape.PCode) - { - case PCode.Grass: - case PCode.Tree: - case PCode.NewTree: - update.Data = new byte[] { prim.Shape.State }; - break; - default: - // TODO: Support ScratchPad - //if (prim.ScratchPad != null) - //{ - // update.Data = new byte[prim.ScratchPad.Length]; - // Buffer.BlockCopy(prim.ScratchPad, 0, update.Data, 0, update.Data.Length); - //} - //else - //{ - // update.Data = Utils.EmptyBytes; - //} - update.Data = Utils.EmptyBytes; - break; - } - - return update; }*/ #endregion Prim/Avatar Updates - #region Avatar Packet/data sending Methods + #region Avatar Packet/Data Sending Methods /// - /// send a objectupdate packet with information about the clients avatar + /// Send an ObjectUpdate packet with information about an avatar /// public void SendAvatarData(SendAvatarData data) { ObjectUpdatePacket objupdate = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); - // TODO: don't create new blocks if recycling an old packet - objupdate.RegionData.RegionHandle = data.regionHandle; - objupdate.RegionData.TimeDilation = ushort.MaxValue; - objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; - objupdate.ObjectData[0] = CreateDefaultAvatarPacket(data.textureEntry); - - //give this avatar object a local id and assign the user a name - objupdate.ObjectData[0].ID = data.avatarLocalID; - objupdate.ObjectData[0].FullID = data.avatarID; - objupdate.ObjectData[0].ParentID = data.parentID; - objupdate.ObjectData[0].NameValue = - Utils.StringToBytes("FirstName STRING RW SV " + data.firstName + "\nLastName STRING RW SV " + data.lastName + "\nTitle STRING RW SV " + data.grouptitle); - - Vector3 pos2 = new Vector3(data.Pos.X, data.Pos.Y, data.Pos.Z); - byte[] pb = pos2.GetBytes(); - Array.Copy(pb, 0, objupdate.ObjectData[0].ObjectData, 16, pb.Length); - - byte[] rot = data.rotation.GetBytes(); - Array.Copy(rot, 0, objupdate.ObjectData[0].ObjectData, 52, rot.Length); - objupdate.Header.Zerocoded = true; + + objupdate.RegionData.RegionHandle = data.RegionHandle; + objupdate.RegionData.TimeDilation = ushort.MaxValue; + + objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; + objupdate.ObjectData[0] = CreateAvatarUpdateBlock(data); + OutPacket(objupdate, ThrottleOutPacketType.Task); } @@ -3387,27 +3281,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public virtual void SendAvatarTerseUpdate(SendAvatarTerseData data) { - if (data.priority == double.NaN) + if (data.Priority == double.NaN) { m_log.Error("[LLClientView] SendAvatarTerseUpdate received a NaN priority, dropping update"); return; } - Quaternion rotation = data.rotation; + Quaternion rotation = data.Rotation; if (rotation.X == rotation.Y && rotation.Y == rotation.Z && - rotation.Z == rotation.W && rotation.W == 0) + rotation.Z == rotation.W && rotation.W == 0.0f) rotation = Quaternion.Identity; - ImprovedTerseObjectUpdatePacket.ObjectDataBlock terseBlock = - CreateAvatarImprovedBlock(data.localID, data.position, data.velocity, rotation); + ImprovedTerseObjectUpdatePacket.ObjectDataBlock terseBlock = CreateImprovedTerseBlock(data); lock (m_avatarTerseUpdates.SyncRoot) - m_avatarTerseUpdates.Enqueue(data.priority, terseBlock, data.localID); + m_avatarTerseUpdates.Enqueue(data.Priority, terseBlock, data.LocalID); // If we received an update about our own avatar, process the avatar update priority queue immediately - if (data.agentid == m_agentId) + if (data.AgentID == m_agentId) ProcessAvatarTerseUpdates(); } @@ -3471,9 +3364,153 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(loc, ThrottleOutPacketType.Task); } - #endregion + #endregion Avatar Packet/Data Sending Methods - #region Primitive Packet/data Sending Methods + #region Primitive Packet/Data Sending Methods + + public void SendPrimitiveToClient(SendPrimitiveData data) + { + if (data.priority == double.NaN) + { + m_log.Error("[LLClientView] SendPrimitiveToClient received a NaN priority, dropping update"); + return; + } + + Quaternion rotation = data.rotation; + + if (data.AttachPoint > 30 && data.ownerID != AgentId) // Someone else's HUD + return; + if (data.primShape.PCode == 9 && data.primShape.State != 0 && data.parentID == 0) + return; + + if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0.0f) + rotation = Quaternion.Identity; + + ObjectUpdatePacket.ObjectDataBlock objectData = CreatePrimUpdateBlock(data); + + lock (m_primFullUpdates.SyncRoot) + m_primFullUpdates.Enqueue(data.priority, objectData, data.localID); + } + + void ProcessPrimFullUpdates() + { + ObjectUpdatePacket outPacket = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); + outPacket.Header.Zerocoded = true; + + //outPacket.RegionData = new ObjectUpdatePacket.RegionDataBlock(); + outPacket.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; + outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); + + lock (m_primFullUpdates.SyncRoot) + { + int count = Math.Min(m_primFullUpdates.Count, m_primFullUpdatesPerPacket); + if (count == 0) + return; + + outPacket.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[count]; + for (int i = 0; i < count; i++) + outPacket.ObjectData[i] = m_primFullUpdates.Dequeue(); + } + + OutPacket(outPacket, ThrottleOutPacketType.State); + } + + public void SendPrimTerseUpdate(SendPrimitiveTerseData data) + { + if (data.Priority == double.NaN) + { + m_log.Error("[LLClientView] SendPrimTerseUpdate received a NaN priority, dropping update"); + return; + } + + Quaternion rotation = data.Rotation; + + if (data.AttachPoint > 30 && data.OwnerID != AgentId) // Someone else's HUD + return; + + if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) + rotation = Quaternion.Identity; + + ImprovedTerseObjectUpdatePacket.ObjectDataBlock objectData = CreateImprovedTerseBlock(data); + + lock (m_primTerseUpdates.SyncRoot) + m_primTerseUpdates.Enqueue(data.Priority, objectData, data.LocalID); + } + + void ProcessPrimTerseUpdates() + { + ImprovedTerseObjectUpdatePacket outPacket = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); + outPacket.Header.Reliable = false; + outPacket.Header.Zerocoded = true; + + outPacket.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; + outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); + + lock (m_primTerseUpdates.SyncRoot) + { + int count = Math.Min(m_primTerseUpdates.Count, m_primTerseUpdatesPerPacket); + if (count == 0) + return; + + outPacket.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; + for (int i = 0; i < count; i++) + outPacket.ObjectData[i] = m_primTerseUpdates.Dequeue(); + } + + OutPacket(outPacket, ThrottleOutPacketType.State); + } + + public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + PriorityQueue.UpdatePriorityHandler terse_update_priority_handler = + delegate(ref double priority, uint local_id) + { + priority = handler(new UpdatePriorityData(priority, local_id)); + return priority != double.NaN; + }; + PriorityQueue.UpdatePriorityHandler update_priority_handler = + delegate(ref double priority, uint local_id) + { + priority = handler(new UpdatePriorityData(priority, local_id)); + return priority != double.NaN; + }; + + if ((type & StateUpdateTypes.AvatarTerse) != 0) + { + lock (m_avatarTerseUpdates.SyncRoot) + m_avatarTerseUpdates.Reprioritize(terse_update_priority_handler); + } + + if ((type & StateUpdateTypes.PrimitiveFull) != 0) + { + lock (m_primFullUpdates.SyncRoot) + m_primFullUpdates.Reprioritize(update_priority_handler); + } + + if ((type & StateUpdateTypes.PrimitiveTerse) != 0) + { + lock (m_primTerseUpdates.SyncRoot) + m_primTerseUpdates.Reprioritize(terse_update_priority_handler); + } + } + + public void FlushPrimUpdates() + { + while (m_primFullUpdates.Count > 0) + { + ProcessPrimFullUpdates(); + } + while (m_primTerseUpdates.Count > 0) + { + ProcessPrimTerseUpdates(); + } + while (m_avatarTerseUpdates.Count > 0) + { + ProcessAvatarTerseUpdates(); + } + } + + #endregion Primitive Packet/Data Sending Methods /// /// @@ -3499,92 +3536,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(attach, ThrottleOutPacketType.Task); } - public void SendPrimitiveToClient(SendPrimitiveData data) - { - if (data.priority == double.NaN) - { - m_log.Error("[LLClientView] SendPrimitiveToClient received a NaN priority, dropping update"); - return; - } - - Quaternion rotation = data.rotation; - - if (data.AttachPoint > 30 && data.ownerID != AgentId) // Someone else's HUD - return; - if (data.primShape.PCode == 9 && data.primShape.State != 0 && data.parentID == 0) - return; - - if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0.0f) - rotation = Quaternion.Identity; - - ObjectUpdatePacket.ObjectDataBlock objectData = CreatePrimUpdateBlock(data.primShape, data.flags); - - objectData.ID = data.localID; - objectData.FullID = data.objectID; - objectData.OwnerID = data.ownerID; - - objectData.Text = Util.StringToBytes256(data.text); - objectData.TextColor[0] = data.color[0]; - objectData.TextColor[1] = data.color[1]; - objectData.TextColor[2] = data.color[2]; - objectData.TextColor[3] = data.color[3]; - objectData.ParentID = data.parentID; - objectData.PSBlock = data.particleSystem; - objectData.ClickAction = data.clickAction; - objectData.Material = data.material; - objectData.Flags = 0; - - if (data.attachment) - { - // Necessary??? - objectData.JointAxisOrAnchor = new Vector3(0, 0, 2); - objectData.JointPivot = new Vector3(0, 0, 0); - - // Item from inventory??? - objectData.NameValue = - Utils.StringToBytes("AttachItemID STRING RW SV " + data.AssetId.Guid); - objectData.State = (byte)((data.AttachPoint % 16) * 16 + (data.AttachPoint / 16)); - } - - // Xantor 20080528: Send sound info as well - // Xantor 20080530: Zero out everything if there's no SoundId, so zerocompression will work again - objectData.Sound = data.SoundId; - if (data.SoundId == UUID.Zero) - { - objectData.OwnerID = UUID.Zero; - objectData.Gain = 0.0f; - objectData.Radius = 0.0f; - objectData.Flags = 0; - } - else - { - objectData.OwnerID = data.ownerID; - objectData.Gain = (float)data.SoundVolume; - objectData.Radius = (float)data.SoundRadius; - objectData.Flags = data.SoundFlags; - } - - byte[] pb = data.pos.GetBytes(); - Buffer.BlockCopy(pb, 0, objectData.ObjectData, 0, pb.Length); - - byte[] vel = data.vel.GetBytes(); - Buffer.BlockCopy(vel, 0, objectData.ObjectData, pb.Length, vel.Length); - - byte[] rot = rotation.GetBytes(); - Buffer.BlockCopy(rot, 0, objectData.ObjectData, 36, rot.Length); - - byte[] rvel = data.rvel.GetBytes(); - Buffer.BlockCopy(rvel, 0, objectData.ObjectData, 36 + rot.Length, rvel.Length); - - if (data.textureanim.Length > 0) - { - objectData.TextureAnim = data.textureanim; - } - - lock (m_primFullUpdates.SyncRoot) - m_primFullUpdates.Enqueue(data.priority, objectData, data.localID); - } - void HandleQueueEmpty(ThrottleOutPacketType queue) { switch (queue) @@ -3643,128 +3594,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_imageManager.ProcessImageQueue(m_textureSendLimit); } - void ProcessPrimFullUpdates() - { - ObjectUpdatePacket outPacket = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); - outPacket.Header.Zerocoded = true; - - //outPacket.RegionData = new ObjectUpdatePacket.RegionDataBlock(); - outPacket.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; - outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); - - lock (m_primFullUpdates.SyncRoot) - { - int count = Math.Min(m_primFullUpdates.Count, m_primFullUpdatesPerPacket); - if (count == 0) - return; - - outPacket.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[count]; - for (int i = 0; i < count; i++) - outPacket.ObjectData[i] = m_primFullUpdates.Dequeue(); - } - - OutPacket(outPacket, ThrottleOutPacketType.State); - } - - /// - /// - /// - //public void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, - // Quaternion rotation, Vector3 velocity, Vector3 rotationalvelocity, byte state, UUID AssetId, UUID ownerID, int attachPoint) - public void SendPrimTerseUpdate(SendPrimitiveTerseData data) - { - if (data.priority == double.NaN) - { - m_log.Error("[LLClientView] SendPrimTerseUpdate received a NaN priority, dropping update"); - return; - } - - Quaternion rotation = data.rotation; - - if (data.attachPoint > 30 && data.owner != AgentId) // Someone else's HUD - return; - - if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) - rotation = Quaternion.Identity; - - ImprovedTerseObjectUpdatePacket.ObjectDataBlock objectData = - CreatePrimImprovedBlock(data.localID, data.position, rotation, - data.velocity, data.rotationalvelocity, data.state); - - lock (m_primTerseUpdates.SyncRoot) - m_primTerseUpdates.Enqueue(data.priority, objectData, data.localID); - } - - void ProcessPrimTerseUpdates() - { - ImprovedTerseObjectUpdatePacket outPacket = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); - outPacket.Header.Reliable = false; - outPacket.Header.Zerocoded = true; - - outPacket.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; - outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); - - lock (m_primTerseUpdates.SyncRoot) - { - int count = Math.Min(m_primTerseUpdates.Count, m_primTerseUpdatesPerPacket); - if (count == 0) - return; - - outPacket.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; - for (int i = 0; i < count; i++) - outPacket.ObjectData[i] = m_primTerseUpdates.Dequeue(); - } - - OutPacket(outPacket, ThrottleOutPacketType.State); - } - - public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) - { - PriorityQueue.UpdatePriorityHandler terse_update_priority_handler = - delegate(ref double priority, uint local_id) - { - priority = handler(new UpdatePriorityData(priority, local_id)); - return priority != double.NaN; - }; - PriorityQueue.UpdatePriorityHandler update_priority_handler = - delegate(ref double priority, uint local_id) - { - priority = handler(new UpdatePriorityData(priority, local_id)); - return priority != double.NaN; - }; - - if ((type & StateUpdateTypes.AvatarTerse) != 0) { - lock (m_avatarTerseUpdates.SyncRoot) - m_avatarTerseUpdates.Reprioritize(terse_update_priority_handler); - } - - if ((type & StateUpdateTypes.PrimitiveFull) != 0) { - lock (m_primFullUpdates.SyncRoot) - m_primFullUpdates.Reprioritize(update_priority_handler); - } - - if ((type & StateUpdateTypes.PrimitiveTerse) != 0) { - lock (m_primTerseUpdates.SyncRoot) - m_primTerseUpdates.Reprioritize(terse_update_priority_handler); - } - } - - public void FlushPrimUpdates() - { - while (m_primFullUpdates.Count > 0) - { - ProcessPrimFullUpdates(); - } - while (m_primTerseUpdates.Count > 0) - { - ProcessPrimTerseUpdates(); - } - while (m_avatarTerseUpdates.Count > 0) - { - ProcessAvatarTerseUpdates(); - } - } - public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) { AssetUploadCompletePacket newPack = new AssetUploadCompletePacket(); @@ -3946,8 +3775,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(proper, ThrottleOutPacketType.Task); } - #endregion - #region Estate Data Sending Methods private static bool convertParamStringToBool(byte[] field) @@ -4354,325 +4181,221 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Helper Methods - protected ImprovedTerseObjectUpdatePacket.ObjectDataBlock CreateAvatarImprovedBlock(uint localID, Vector3 pos, - Vector3 velocity, - Quaternion rotation) + protected ImprovedTerseObjectUpdatePacket.ObjectDataBlock CreateImprovedTerseBlock(SendAvatarTerseData data) { - byte[] bytes = new byte[60]; - int i = 0; - ImprovedTerseObjectUpdatePacket.ObjectDataBlock dat = PacketPool.GetDataBlock(); - - dat.TextureEntry = new byte[0]; // AvatarTemplate.TextureEntry; - - uint ID = localID; - - bytes[i++] = (byte)(ID % 256); - bytes[i++] = (byte)((ID >> 8) % 256); - bytes[i++] = (byte)((ID >> 16) % 256); - bytes[i++] = (byte)((ID >> 24) % 256); - bytes[i++] = 0; - bytes[i++] = 1; - i += 14; - bytes[i++] = 128; - bytes[i++] = 63; - - byte[] pb = pos.GetBytes(); - Array.Copy(pb, 0, bytes, i, pb.Length); - i += 12; - - Vector3 internDirec = new Vector3(velocity.X, velocity.Y, velocity.Z); - - internDirec = internDirec / 128.0f; - internDirec.X += 1; - internDirec.Y += 1; - internDirec.Z += 1; - - ushort InternVelocityX = (ushort)(32768 * internDirec.X); - ushort InternVelocityY = (ushort)(32768 * internDirec.Y); - ushort InternVelocityZ = (ushort)(32768 * internDirec.Z); - - ushort ac = 32767; - bytes[i++] = (byte)(InternVelocityX % 256); - bytes[i++] = (byte)((InternVelocityX >> 8) % 256); - bytes[i++] = (byte)(InternVelocityY % 256); - bytes[i++] = (byte)((InternVelocityY >> 8) % 256); - bytes[i++] = (byte)(InternVelocityZ % 256); - bytes[i++] = (byte)((InternVelocityZ >> 8) % 256); - - //accel - bytes[i++] = (byte)(ac % 256); - bytes[i++] = (byte)((ac >> 8) % 256); - bytes[i++] = (byte)(ac % 256); - bytes[i++] = (byte)((ac >> 8) % 256); - bytes[i++] = (byte)(ac % 256); - bytes[i++] = (byte)((ac >> 8) % 256); - - //rotation - ushort rw, rx, ry, rz; - rw = (ushort)(32768 * (rotation.W + 1)); - rx = (ushort)(32768 * (rotation.X + 1)); - ry = (ushort)(32768 * (rotation.Y + 1)); - rz = (ushort)(32768 * (rotation.Z + 1)); - - //rot - bytes[i++] = (byte)(rx % 256); - bytes[i++] = (byte)((rx >> 8) % 256); - bytes[i++] = (byte)(ry % 256); - bytes[i++] = (byte)((ry >> 8) % 256); - bytes[i++] = (byte)(rz % 256); - bytes[i++] = (byte)((rz >> 8) % 256); - bytes[i++] = (byte)(rw % 256); - bytes[i++] = (byte)((rw >> 8) % 256); - - //rotation vel - bytes[i++] = (byte)(ac % 256); - bytes[i++] = (byte)((ac >> 8) % 256); - bytes[i++] = (byte)(ac % 256); - bytes[i++] = (byte)((ac >> 8) % 256); - bytes[i++] = (byte)(ac % 256); - bytes[i++] = (byte)((ac >> 8) % 256); - - dat.Data = bytes; - - return (dat); + return CreateImprovedTerseBlock(true, data.LocalID, 0, data.CollisionPlane, data.Position, data.Velocity, + data.Acceleration, data.Rotation, Vector3.Zero, data.TextureEntry); } - /// - /// - /// - /// - /// - /// - /// - protected ImprovedTerseObjectUpdatePacket.ObjectDataBlock CreatePrimImprovedBlock(uint localID, - Vector3 position, - Quaternion rotation, - Vector3 velocity, - Vector3 rotationalvelocity, - byte state) + protected ImprovedTerseObjectUpdatePacket.ObjectDataBlock CreateImprovedTerseBlock(SendPrimitiveTerseData data) { - uint ID = localID; - byte[] bytes = new byte[60]; - - int i = 0; - ImprovedTerseObjectUpdatePacket.ObjectDataBlock dat = PacketPool.GetDataBlock(); - dat.TextureEntry = new byte[0]; - bytes[i++] = (byte)(ID % 256); - bytes[i++] = (byte)((ID >> 8) % 256); - bytes[i++] = (byte)((ID >> 16) % 256); - bytes[i++] = (byte)((ID >> 24) % 256); - bytes[i++] = (byte)(((state & 0xf0) >> 4) | ((state & 0x0f) << 4)); - bytes[i++] = 0; - - byte[] pb = position.GetBytes(); - Array.Copy(pb, 0, bytes, i, pb.Length); - i += 12; - ushort ac = 32767; - - ushort velx, vely, velz; - Vector3 vel = new Vector3(velocity.X, velocity.Y, velocity.Z); - - vel = vel / 128.0f; - vel.X += 1; - vel.Y += 1; - vel.Z += 1; - //vel - velx = (ushort)(32768 * (vel.X)); - vely = (ushort)(32768 * (vel.Y)); - velz = (ushort)(32768 * (vel.Z)); - - bytes[i++] = (byte)(velx % 256); - bytes[i++] = (byte)((velx >> 8) % 256); - bytes[i++] = (byte)(vely % 256); - bytes[i++] = (byte)((vely >> 8) % 256); - bytes[i++] = (byte)(velz % 256); - bytes[i++] = (byte)((velz >> 8) % 256); - - //accel - bytes[i++] = (byte)(ac % 256); - bytes[i++] = (byte)((ac >> 8) % 256); - bytes[i++] = (byte)(ac % 256); - bytes[i++] = (byte)((ac >> 8) % 256); - bytes[i++] = (byte)(ac % 256); - bytes[i++] = (byte)((ac >> 8) % 256); - - ushort rw, rx, ry, rz; - rw = (ushort)(32768 * (rotation.W + 1)); - rx = (ushort)(32768 * (rotation.X + 1)); - ry = (ushort)(32768 * (rotation.Y + 1)); - rz = (ushort)(32768 * (rotation.Z + 1)); - - //rot - bytes[i++] = (byte)(rx % 256); - bytes[i++] = (byte)((rx >> 8) % 256); - bytes[i++] = (byte)(ry % 256); - bytes[i++] = (byte)((ry >> 8) % 256); - bytes[i++] = (byte)(rz % 256); - bytes[i++] = (byte)((rz >> 8) % 256); - bytes[i++] = (byte)(rw % 256); - bytes[i++] = (byte)((rw >> 8) % 256); - - //rotation vel - Vector3 rvel = new Vector3(rotationalvelocity.X, rotationalvelocity.Y, rotationalvelocity.Z); - - rvel = rvel / 128.0f; - rvel.X += 1; - rvel.Y += 1; - rvel.Z += 1; - //vel - ushort rvelx = (ushort)(32768 * (rvel.X)); - ushort rvely = (ushort)(32768 * (rvel.Y)); - ushort rvelz = (ushort)(32768 * (rvel.Z)); - - bytes[i++] = (byte)(rvelx % 256); - bytes[i++] = (byte)((rvelx >> 8) % 256); - bytes[i++] = (byte)(rvely % 256); - bytes[i++] = (byte)((rvely >> 8) % 256); - bytes[i++] = (byte)(rvelz % 256); - bytes[i++] = (byte)((rvelz >> 8) % 256); - dat.Data = bytes; - - return dat; + return CreateImprovedTerseBlock(false, data.LocalID, data.State, Vector4.Zero, data.Position, data.Velocity, + data.Acceleration, data.Rotation, data.AngularVelocity, data.TextureEntry); } - /// - /// Create the ObjectDataBlock for a ObjectUpdatePacket (for a Primitive) - /// - /// - /// - protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(PrimitiveBaseShape primShape, uint flags) + protected ImprovedTerseObjectUpdatePacket.ObjectDataBlock CreateImprovedTerseBlock(bool avatar, uint localID, byte state, + Vector4 collisionPlane, Vector3 position, Vector3 velocity, Vector3 acceleration, Quaternion rotation, + Vector3 angularVelocity, byte[] textureEntry) { - ObjectUpdatePacket.ObjectDataBlock objupdate = PacketPool.GetDataBlock(); - SetDefaultPrimPacketValues(objupdate); - objupdate.UpdateFlags = flags; - SetPrimPacketShapeData(objupdate, primShape); + int pos = 0; + byte[] data = new byte[(avatar ? 60 : 44)]; - if ((primShape.PCode == (byte)PCode.NewTree) || (primShape.PCode == (byte)PCode.Tree) || (primShape.PCode == (byte)PCode.Grass)) + // LocalID + Utils.UIntToBytes(localID, data, pos); + pos += 4; + + // Avatar/CollisionPlane + data[pos++] = state; + if (avatar) { - objupdate.Data = new byte[1]; - objupdate.Data[0] = primShape.State; + data[pos++] = 1; + + if (collisionPlane == Vector4.Zero) + collisionPlane = Vector4.UnitW; + + collisionPlane.ToBytes(data, pos); + pos += 16; } - return objupdate; - } - - protected void SetPrimPacketShapeData(ObjectUpdatePacket.ObjectDataBlock objectData, PrimitiveBaseShape primData) - { - objectData.TextureEntry = primData.TextureEntry; - objectData.PCode = primData.PCode; - objectData.State = primData.State; - objectData.PathBegin = primData.PathBegin; - objectData.PathEnd = primData.PathEnd; - objectData.PathScaleX = primData.PathScaleX; - objectData.PathScaleY = primData.PathScaleY; - objectData.PathShearX = primData.PathShearX; - objectData.PathShearY = primData.PathShearY; - objectData.PathSkew = primData.PathSkew; - objectData.ProfileBegin = primData.ProfileBegin; - objectData.ProfileEnd = primData.ProfileEnd; - objectData.Scale = primData.Scale; - objectData.PathCurve = primData.PathCurve; - objectData.ProfileCurve = primData.ProfileCurve; - objectData.ProfileHollow = primData.ProfileHollow; - objectData.PathRadiusOffset = primData.PathRadiusOffset; - objectData.PathRevolutions = primData.PathRevolutions; - objectData.PathTaperX = primData.PathTaperX; - objectData.PathTaperY = primData.PathTaperY; - objectData.PathTwist = primData.PathTwist; - objectData.PathTwistBegin = primData.PathTwistBegin; - objectData.ExtraParams = primData.ExtraParams; - } - - /// - /// Set some default values in a ObjectUpdatePacket - /// - /// - protected void SetDefaultPrimPacketValues(ObjectUpdatePacket.ObjectDataBlock objdata) - { - objdata.PSBlock = new byte[0]; - objdata.ExtraParams = new byte[1]; - objdata.MediaURL = new byte[0]; - objdata.NameValue = new byte[0]; - objdata.Text = new byte[0]; - objdata.TextColor = new byte[4]; - objdata.JointAxisOrAnchor = new Vector3(0, 0, 0); - objdata.JointPivot = new Vector3(0, 0, 0); - objdata.Material = 3; - objdata.TextureAnim = new byte[0]; - objdata.Sound = UUID.Zero; - objdata.State = 0; - objdata.Data = new byte[0]; - - objdata.ObjectData = new byte[60]; - objdata.ObjectData[46] = 128; - objdata.ObjectData[47] = 63; - } - - /// - /// - /// - /// - public ObjectUpdatePacket.ObjectDataBlock CreateDefaultAvatarPacket(byte[] textureEntry) - { - ObjectUpdatePacket.ObjectDataBlock objdata = PacketPool.GetDataBlock(); - // new OpenMetaverse.Packets.ObjectUpdatePacket.ObjectDataBlock(data1, ref i); - - SetDefaultAvatarPacketValues(ref objdata); - objdata.UpdateFlags = 61 + (9 << 8) + (130 << 16) + (16 << 24); - objdata.PathCurve = 16; - objdata.ProfileCurve = 1; - objdata.PathScaleX = 100; - objdata.PathScaleY = 100; - objdata.ParentID = 0; - objdata.OwnerID = UUID.Zero; - objdata.Scale = new Vector3(1, 1, 1); - objdata.PCode = (byte)PCode.Avatar; - if (textureEntry != null) + else { - objdata.TextureEntry = textureEntry; + ++pos; } - Vector3 pos = new Vector3(objdata.ObjectData, 16); - pos.X = 100f; - objdata.ID = 8880000; - objdata.NameValue = Utils.StringToBytes("FirstName STRING RW SV Test \nLastName STRING RW SV User "); - //Vector3 pos2 = new Vector3(100f, 100f, 23f); - //objdata.FullID=user.AgentId; - byte[] pb = pos.GetBytes(); - Array.Copy(pb, 0, objdata.ObjectData, 16, pb.Length); - return objdata; + // Position + position.ToBytes(data, pos); + pos += 12; + + // Velocity + Utils.UInt16ToBytes(Utils.FloatToUInt16(velocity.X, -128.0f, 128.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(velocity.Y, -128.0f, 128.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(velocity.Z, -128.0f, 128.0f), data, pos); pos += 2; + + // Acceleration + Utils.UInt16ToBytes(Utils.FloatToUInt16(acceleration.X, -64.0f, 64.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(acceleration.Y, -64.0f, 64.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(acceleration.Z, -64.0f, 64.0f), data, pos); pos += 2; + + // Rotation + Utils.UInt16ToBytes(Utils.FloatToUInt16(rotation.X, -1.0f, 1.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(rotation.Y, -1.0f, 1.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(rotation.Z, -1.0f, 1.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(rotation.W, -1.0f, 1.0f), data, pos); pos += 2; + + // Angular Velocity + Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.X, -64.0f, 64.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Y, -64.0f, 64.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Z, -64.0f, 64.0f), data, pos); pos += 2; + + ImprovedTerseObjectUpdatePacket.ObjectDataBlock block = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock(); + block.Data = data; + + if (textureEntry != null && textureEntry.Length > 0) + { + byte[] teBytesFinal = new byte[textureEntry.Length + 4]; + + // Texture Length + Utils.IntToBytes(textureEntry.Length, textureEntry, 0); + // Texture + Buffer.BlockCopy(textureEntry, 0, teBytesFinal, 4, textureEntry.Length); + + block.TextureEntry = teBytesFinal; + } + else + { + block.TextureEntry = Utils.EmptyBytes; + } + + return block; } - /// - /// - /// - /// - protected void SetDefaultAvatarPacketValues(ref ObjectUpdatePacket.ObjectDataBlock objdata) + protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(SendAvatarData data) { - objdata.PSBlock = new byte[0]; - objdata.ExtraParams = new byte[1]; - objdata.MediaURL = new byte[0]; - objdata.NameValue = new byte[0]; - objdata.Text = new byte[0]; - objdata.TextColor = new byte[4]; - objdata.JointAxisOrAnchor = new Vector3(0, 0, 0); - objdata.JointPivot = new Vector3(0, 0, 0); - objdata.Material = 4; - objdata.TextureAnim = new byte[0]; - objdata.Sound = UUID.Zero; - Primitive.TextureEntry ntex = new Primitive.TextureEntry(new UUID("00000000-0000-0000-5005-000000000005")); - objdata.TextureEntry = ntex.GetBytes(); + byte[] objectData = new byte[60]; + data.Position.ToBytes(objectData, 0); + //data.Velocity.ToBytes(objectData, 12); + //data.Acceleration.ToBytes(objectData, 24); + data.Rotation.ToBytes(objectData, 36); + //data.AngularVelocity.ToBytes(objectData, 48); - objdata.State = 0; - objdata.Data = new byte[0]; + ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); - objdata.ObjectData = new byte[76]; - objdata.ObjectData[15] = 128; - objdata.ObjectData[16] = 63; - objdata.ObjectData[56] = 128; - objdata.ObjectData[61] = 102; - objdata.ObjectData[62] = 40; - objdata.ObjectData[63] = 61; - objdata.ObjectData[64] = 189; + update.Data = Utils.EmptyBytes; + update.ExtraParams = new byte[1]; + update.FullID = data.AvatarID; + update.ID = data.AvatarLocalID; + update.Material = (byte)Material.Flesh; + update.MediaURL = Utils.EmptyBytes; + update.NameValue = Utils.StringToBytes("FirstName STRING RW SV " + data.FirstName + "\nLastName STRING RW SV " + + data.LastName + "\nTitle STRING RW SV " + data.GroupTitle); + update.ObjectData = objectData; + update.ParentID = data.ParentID; + update.PathCurve = 16; + update.PathScaleX = 100; + update.PathScaleY = 100; + update.PCode = (byte)PCode.Avatar; + update.ProfileCurve = 1; + update.PSBlock = Utils.EmptyBytes; + update.Scale = Vector3.One; + update.Text = Utils.EmptyBytes; + update.TextColor = new byte[4]; + update.TextureAnim = Utils.EmptyBytes; + update.TextureEntry = data.TextureEntry ?? Utils.EmptyBytes; + update.UpdateFlags = 61 + (9 << 8) + (130 << 16) + (16 << 24); // TODO: Replace these numbers with PrimFlags + + return update; + } + + protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SendPrimitiveData data) + { + byte[] objectData = new byte[60]; + data.pos.ToBytes(objectData, 0); + data.vel.ToBytes(objectData, 12); + data.acc.ToBytes(objectData, 24); + data.rotation.ToBytes(objectData, 36); + data.rvel.ToBytes(objectData, 48); + + ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); + update.ClickAction = (byte)data.clickAction; + update.CRC = 0; + update.ExtraParams = data.primShape.ExtraParams ?? Utils.EmptyBytes; + update.FullID = data.objectID; + update.ID = data.localID; + //update.JointAxisOrAnchor = Vector3.Zero; // These are deprecated + //update.JointPivot = Vector3.Zero; + //update.JointType = 0; + update.Material = data.material; + update.MediaURL = Utils.EmptyBytes; // FIXME: Support this in OpenSim + if (data.attachment) + { + update.NameValue = Util.StringToBytes256("AttachItemID STRING RW SV " + data.AssetId); + update.State = (byte)((data.AttachPoint % 16) * 16 + (data.AttachPoint / 16)); + } + else + { + update.NameValue = Utils.EmptyBytes; + update.State = data.primShape.State; + } + update.ObjectData = objectData; + update.ParentID = data.parentID; + update.PathBegin = data.primShape.PathBegin; + update.PathCurve = data.primShape.PathCurve; + update.PathEnd = data.primShape.PathEnd; + update.PathRadiusOffset = data.primShape.PathRadiusOffset; + update.PathRevolutions = data.primShape.PathRevolutions; + update.PathScaleX = data.primShape.PathScaleX; + update.PathScaleY = data.primShape.PathScaleY; + update.PathShearX = data.primShape.PathShearX; + update.PathShearY = data.primShape.PathShearY; + update.PathSkew = data.primShape.PathSkew; + update.PathTaperX = data.primShape.PathTaperX; + update.PathTaperY = data.primShape.PathTaperY; + update.PathTwist = data.primShape.PathTwist; + update.PathTwistBegin = data.primShape.PathTwistBegin; + update.PCode = data.primShape.PCode; + update.ProfileBegin = data.primShape.ProfileBegin; + update.ProfileCurve = data.primShape.ProfileCurve; + update.ProfileEnd = data.primShape.ProfileEnd; + update.ProfileHollow = data.primShape.ProfileHollow; + update.PSBlock = data.particleSystem ?? Utils.EmptyBytes; + update.TextColor = data.color ?? Color4.Black.GetBytes(true); + update.TextureAnim = data.textureanim ?? Utils.EmptyBytes; + update.TextureEntry = data.primShape.TextureEntry ?? Utils.EmptyBytes; + update.Scale = data.primShape.Scale; + update.Text = Util.StringToBytes256(data.text); + update.UpdateFlags = (uint)data.flags; + + if (data.SoundId != UUID.Zero) + { + update.Sound = data.SoundId; + update.OwnerID = data.ownerID; + update.Gain = (float)data.SoundVolume; + update.Radius = (float)data.SoundRadius; + update.Flags = data.SoundFlags; + } + + switch ((PCode)data.primShape.PCode) + { + case PCode.Grass: + case PCode.Tree: + case PCode.NewTree: + update.Data = new byte[] { data.primShape.State }; + break; + default: + // TODO: Support ScratchPad + //if (prim.ScratchPad != null) + //{ + // update.Data = new byte[prim.ScratchPad.Length]; + // Buffer.BlockCopy(prim.ScratchPad, 0, update.Data, 0, update.Data.Length); + //} + //else + //{ + // update.Data = Utils.EmptyBytes; + //} + update.Data = Utils.EmptyBytes; + break; + } + + return update; } public void SendNameReply(UUID profileId, string firstname, string lastname) @@ -4682,8 +4405,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.UUIDNameBlock = new UUIDNameReplyPacket.UUIDNameBlockBlock[1]; packet.UUIDNameBlock[0] = new UUIDNameReplyPacket.UUIDNameBlockBlock(); packet.UUIDNameBlock[0].ID = profileId; - packet.UUIDNameBlock[0].FirstName = Utils.StringToBytes(firstname); - packet.UUIDNameBlock[0].LastName = Utils.StringToBytes(lastname); + packet.UUIDNameBlock[0].FirstName = Util.StringToBytes256(firstname); + packet.UUIDNameBlock[0].LastName = Util.StringToBytes256(lastname); OutPacket(packet, ThrottleOutPacketType.Task); } @@ -4879,8 +4602,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP scriptQuestion.Data.TaskID = taskID; scriptQuestion.Data.ItemID = itemID; scriptQuestion.Data.Questions = question; - scriptQuestion.Data.ObjectName = Utils.StringToBytes(taskName); - scriptQuestion.Data.ObjectOwner = Utils.StringToBytes(ownerName); + scriptQuestion.Data.ObjectName = Util.StringToBytes256(taskName); + scriptQuestion.Data.ObjectOwner = Util.StringToBytes256(ownerName); OutPacket(scriptQuestion, ThrottleOutPacketType.Task); } @@ -5274,7 +4997,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_moneyBalance + debit >= 0) { m_moneyBalance += debit; - SendMoneyBalance(UUID.Zero, true, Utils.StringToBytes("Poof Poof!"), m_moneyBalance); + SendMoneyBalance(UUID.Zero, true, Util.StringToBytes256("Poof Poof!"), m_moneyBalance); return true; } return false; @@ -9218,7 +8941,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP new GroupTitlesReplyPacket.GroupDataBlock(); groupTitlesReply.GroupData[i].Title = - Utils.StringToBytes(d.Name); + Util.StringToBytes256(d.Name); groupTitlesReply.GroupData[i].RoleID = d.UUID; groupTitlesReply.GroupData[i].Selected = @@ -9255,10 +8978,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP groupProfileRequest.GroupData.GroupID); groupProfileReply.GroupData.GroupID = d.GroupID; - groupProfileReply.GroupData.Name = Utils.StringToBytes(d.Name); - groupProfileReply.GroupData.Charter = Utils.StringToBytes(d.Charter); + groupProfileReply.GroupData.Name = Util.StringToBytes256(d.Name); + groupProfileReply.GroupData.Charter = Util.StringToBytes1024(d.Charter); groupProfileReply.GroupData.ShowInList = d.ShowInList; - groupProfileReply.GroupData.MemberTitle = Utils.StringToBytes(d.MemberTitle); + groupProfileReply.GroupData.MemberTitle = Util.StringToBytes256(d.MemberTitle); groupProfileReply.GroupData.PowersMask = d.PowersMask; groupProfileReply.GroupData.InsigniaID = d.InsigniaID; groupProfileReply.GroupData.FounderID = d.FounderID; @@ -9330,11 +9053,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP groupMembersReply.MemberData[i].Contribution = m.Contribution; groupMembersReply.MemberData[i].OnlineStatus = - Utils.StringToBytes(m.OnlineStatus); + Util.StringToBytes256(m.OnlineStatus); groupMembersReply.MemberData[i].AgentPowers = m.AgentPowers; groupMembersReply.MemberData[i].Title = - Utils.StringToBytes(m.Title); + Util.StringToBytes256(m.Title); groupMembersReply.MemberData[i].IsOwner = m.IsOwner; } @@ -9395,11 +9118,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP groupRolesReply.RoleData[i].RoleID = d.RoleID; groupRolesReply.RoleData[i].Name = - Utils.StringToBytes(d.Name); + Util.StringToBytes256(d.Name); groupRolesReply.RoleData[i].Title = - Utils.StringToBytes(d.Title); + Util.StringToBytes256(d.Title); groupRolesReply.RoleData[i].Description = - Utils.StringToBytes(d.Description); + Util.StringToBytes1024(d.Description); groupRolesReply.RoleData[i].Powers = d.Powers; groupRolesReply.RoleData[i].Members = @@ -9626,9 +9349,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP groupNoticesListReply.Data[i].Timestamp = g.Timestamp; groupNoticesListReply.Data[i].FromName = - Utils.StringToBytes(g.FromName); + Util.StringToBytes256(g.FromName); groupNoticesListReply.Data[i].Subject = - Utils.StringToBytes(g.Subject); + Util.StringToBytes256(g.Subject); groupNoticesListReply.Data[i].HasAttachment = g.HasAttachment; groupNoticesListReply.Data[i].AssetType = @@ -10227,12 +9950,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte mediaLoop) { ParcelMediaUpdatePacket updatePacket = new ParcelMediaUpdatePacket(); - updatePacket.DataBlock.MediaURL = Utils.StringToBytes(mediaUrl); + updatePacket.DataBlock.MediaURL = Util.StringToBytes256(mediaUrl); updatePacket.DataBlock.MediaID = mediaTextureID; updatePacket.DataBlock.MediaAutoScale = autoScale; - updatePacket.DataBlockExtended.MediaType = Utils.StringToBytes(mediaType); - updatePacket.DataBlockExtended.MediaDesc = Utils.StringToBytes(mediaDesc); + updatePacket.DataBlockExtended.MediaType = Util.StringToBytes256(mediaType); + updatePacket.DataBlockExtended.MediaDesc = Util.StringToBytes256(mediaDesc); updatePacket.DataBlockExtended.MediaWidth = mediaWidth; updatePacket.DataBlockExtended.MediaHeight = mediaHeight; updatePacket.DataBlockExtended.MediaLoop = mediaLoop; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 4f3478b64c..66e1468a91 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -733,10 +733,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP // on to en-US to avoid number parsing issues Culture.SetCurrentCulture(); - IncomingPacket incomingPacket = null; - while (base.IsRunning) { + IncomingPacket incomingPacket = null; + try { if (packetInbox.Dequeue(100, ref incomingPacket)) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 30fe976490..70b11c328e 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -357,13 +357,6 @@ namespace OpenSim.Region.Framework.Scenes get { return m_defaultScriptEngine; } } - // Reference to all of the agents in the scene (root and child) - protected Dictionary m_scenePresences - { - get { return m_sceneGraph.ScenePresences; } - set { m_sceneGraph.ScenePresences = value; } - } - public EntityManager Entities { get { return m_sceneGraph.Entities; } @@ -1183,14 +1176,13 @@ namespace OpenSim.Region.Framework.Scenes /// Stats on the Simulator's performance private void SendSimStatsPackets(SimStats stats) { - List StatSendAgents = GetScenePresences(); - foreach (ScenePresence agent in StatSendAgents) - { - if (!agent.IsChildAgent) + ForEachScenePresence( + delegate(ScenePresence agent) { - agent.ControllingClient.SendSimStats(stats); + if (!agent.IsChildAgent) + agent.ControllingClient.SendSimStats(stats); } - } + ); } /// @@ -3501,10 +3493,8 @@ namespace OpenSim.Region.Framework.Scenes { ScenePresence presence; - lock (m_scenePresences) - { - m_scenePresences.TryGetValue(agentID, out presence); - } + lock (m_sceneGraph.ScenePresences) + m_sceneGraph.ScenePresences.TryGetValue(agentID, out presence); if (presence != null) { @@ -3714,12 +3704,9 @@ namespace OpenSim.Region.Framework.Scenes public void RequestTeleportLocation(IClientAPI remoteClient, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags) { - ScenePresence sp = null; - lock (m_scenePresences) - { - if (m_scenePresences.ContainsKey(remoteClient.AgentId)) - sp = m_scenePresences[remoteClient.AgentId]; - } + ScenePresence sp; + lock (m_sceneGraph.ScenePresences) + m_sceneGraph.ScenePresences.TryGetValue(remoteClient.AgentId, out sp); if (sp != null) { @@ -4168,7 +4155,7 @@ namespace OpenSim.Region.Framework.Scenes public void ForEachScenePresence(Action action) { // We don't want to try to send messages if there are no avatars. - if (m_scenePresences != null) + if (m_sceneGraph != null && m_sceneGraph.ScenePresences != null) { try { diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 79f6366691..a078b3dc0f 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -3791,15 +3791,15 @@ if (m_shape != null) { if (ParentGroup.RootPart == this) lPos = AbsolutePosition; } - + // Causes this thread to dig into the Client Thread Data. // Remember your locking here! remoteClient.SendPrimTerseUpdate(new SendPrimitiveTerseData(m_regionHandle, (ushort)(m_parentGroup.GetTimeDilation() * (float)ushort.MaxValue), LocalId, lPos, - RotationOffset, Velocity, + RotationOffset, Velocity, Acceleration, RotationalVelocity, state, FromItemID, - OwnerID, (int)AttachmentPoint, ParentGroup.GetUpdatePriority(remoteClient))); + OwnerID, (int)AttachmentPoint, null, ParentGroup.GetUpdatePriority(remoteClient))); } public void AddScriptLPS(int count) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index f05c3d8183..bdd80c6fad 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -1160,15 +1160,21 @@ namespace OpenSim.Region.Framework.Scenes /// public void HandleAgentUpdate(IClientAPI remoteClient, AgentUpdateArgs agentData) { - lock (m_agentUpdates) + const int AGENT_UPDATE_TIMEOUT_MS = 1000 * 3; + + if (System.Threading.Monitor.TryEnter(m_agentUpdates, AGENT_UPDATE_TIMEOUT_MS)) { - if (m_updatesAllowed) + try { - RealHandleAgentUpdate(remoteClient, agentData); - return; + if (m_updatesAllowed) + { + RealHandleAgentUpdate(remoteClient, agentData); + return; + } + + m_agentUpdates.Add(agentData); } - - m_agentUpdates.Add(agentData); + finally { System.Threading.Monitor.Exit(m_agentUpdates); } } } @@ -2471,7 +2477,7 @@ namespace OpenSim.Region.Framework.Scenes pos.Z -= m_appearance.HipOffset; remoteClient.SendAvatarTerseUpdate(new SendAvatarTerseData(m_regionHandle, (ushort)(m_scene.TimeDilation * ushort.MaxValue), LocalId, - pos, m_velocity, m_rotation, m_uuid, GetUpdatePriority(remoteClient))); + pos, m_velocity, Vector3.Zero, m_rotation, Vector4.Zero, m_uuid, null, GetUpdatePriority(remoteClient))); m_scene.StatsReporter.AddAgentTime(Environment.TickCount - m_perfMonMS); m_scene.StatsReporter.AddAgentUpdates(1); @@ -3504,7 +3510,6 @@ namespace OpenSim.Region.Framework.Scenes public void Close() { - lock (m_attachments) { // Delete attachments from scene @@ -3535,7 +3540,6 @@ namespace OpenSim.Region.Framework.Scenes m_sceneViewer.Close(); RemoveFromPhysicalScene(); - GC.Collect(); } public ScenePresence() From 2dd8a6beaca5a53039d2068db1cebfd7fd095943 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 19 Oct 2009 14:48:17 -0700 Subject: [PATCH 25/61] More instrumentation in physics. --- OpenSim/Region/Physics/OdePlugin/OdePlugin.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs index 7187fbecef..f7f1f69061 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs @@ -2986,6 +2986,8 @@ namespace OpenSim.Region.Physics.OdePlugin foreach (OdeCharacter actor in _characters) { if (actor != null) + if (actor.bad) + m_log.WarnFormat("[PHYSICS]: BAD Actor {0} in _characters list was not removed?", actor.m_uuid); actor.UpdatePositionAndVelocity(); } } From 590d91e57251cc35b3ce15bb60784249a1c3b15c Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 19 Oct 2009 15:03:55 -0700 Subject: [PATCH 26/61] Forgot {} on last commit. --- OpenSim/Region/Physics/OdePlugin/OdePlugin.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs index f7f1f69061..aba366707b 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs @@ -2986,9 +2986,11 @@ namespace OpenSim.Region.Physics.OdePlugin foreach (OdeCharacter actor in _characters) { if (actor != null) + { if (actor.bad) m_log.WarnFormat("[PHYSICS]: BAD Actor {0} in _characters list was not removed?", actor.m_uuid); actor.UpdatePositionAndVelocity(); + } } } From 142008121e2e9c5ca5fca5de07b8a14e37279800 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Mon, 19 Oct 2009 15:19:09 -0700 Subject: [PATCH 27/61] * Change Util.FireAndForget to use ThreadPool.UnsafeQueueUserWorkItem(). This avoids .NET remoting and a managed->unmanaged->managed jump. Overall, a night and day performance difference * Initialize the LLClientView prim full update queue to the number of prims in the scene for a big performance boost * Reordered some comparisons on hot code paths for a minor speed boost * Removed an unnecessary call to the expensive DateTime.Now function (if you *have* to get the current time as opposed to Environment.TickCount, always use DateTime.UtcNow) * Don't fire the queue empty callback for the Resend category * Run the outgoing packet handler thread loop for each client synchronously. It seems like more time was being spent doing the execution asynchronously, and it made deadlocks very difficult to track down * Rewrote some expensive math in LandObject.cs * Optimized EntityManager to only lock on operations that need locking, and use TryGetValue() where possible * Only update the attachment database when an object is attached or detached * Other small misc. performance improvements --- OpenSim/Framework/Util.cs | 18 ++-- .../ClientStack/LindenUDP/LLClientView.cs | 88 +++++++++---------- .../ClientStack/LindenUDP/LLImageManager.cs | 2 - .../ClientStack/LindenUDP/LLUDPClient.cs | 9 +- .../ClientStack/LindenUDP/LLUDPServer.cs | 21 +++-- .../World/Land/LandManagementModule.cs | 5 +- .../CoreModules/World/Land/LandObject.cs | 6 +- .../Region/Framework/Scenes/EntityManager.cs | 69 +++++++-------- .../Framework/Scenes/Scene.Inventory.cs | 23 ++--- OpenSim/Region/Framework/Scenes/Scene.cs | 4 + OpenSim/Region/Framework/Scenes/SceneGraph.cs | 12 ++- .../Framework/Scenes/SceneObjectGroup.cs | 2 +- 12 files changed, 129 insertions(+), 130 deletions(-) diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 3203fc1d7d..d5ae3b7ebd 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1273,7 +1273,7 @@ namespace OpenSim.Framework /// /// Created to work around a limitation in Mono with nested delegates /// - private class FireAndForgetWrapper + /*private class FireAndForgetWrapper { public void FireAndForget(System.Threading.WaitCallback callback) { @@ -1284,21 +1284,23 @@ namespace OpenSim.Framework { callback.BeginInvoke(obj, EndFireAndForget, callback); } - } + }*/ public static void FireAndForget(System.Threading.WaitCallback callback) { - FireAndForgetWrapper wrapper = Singleton.GetInstance(); - wrapper.FireAndForget(callback); + //FireAndForgetWrapper wrapper = Singleton.GetInstance(); + //wrapper.FireAndForget(callback); + System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, null); } public static void FireAndForget(System.Threading.WaitCallback callback, object obj) { - FireAndForgetWrapper wrapper = Singleton.GetInstance(); - wrapper.FireAndForget(callback, obj); + //FireAndForgetWrapper wrapper = Singleton.GetInstance(); + //wrapper.FireAndForget(callback, obj); + System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, obj); } - private static void EndFireAndForget(IAsyncResult ar) + /*private static void EndFireAndForget(IAsyncResult ar) { System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; @@ -1306,7 +1308,7 @@ namespace OpenSim.Framework catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); } ar.AsyncWaitHandle.Close(); - } + }*/ #endregion FireAndForget Threading Pattern } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 1c463ea9ac..b0278825b2 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -321,12 +321,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private readonly IGroupsModule m_GroupsModule; private int m_cachedTextureSerial; - private PriorityQueue m_avatarTerseUpdates = - new PriorityQueue(); - private PriorityQueue m_primTerseUpdates = - new PriorityQueue(); - private PriorityQueue m_primFullUpdates = - new PriorityQueue(); + private PriorityQueue m_avatarTerseUpdates; + private PriorityQueue m_primTerseUpdates; + private PriorityQueue m_primFullUpdates; private int m_moneyBalance; private int m_animationSequenceNumber = 1; private bool m_SendLogoutPacketWhenClosing = true; @@ -335,7 +332,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected Dictionary m_packetHandlers = new Dictionary(); protected Dictionary m_genericPacketHandlers = new Dictionary(); //PauPaw:Local Generic Message handlers - protected IScene m_scene; + protected Scene m_scene; protected LLImageManager m_imageManager; protected string m_firstName; protected string m_lastName; @@ -408,16 +405,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// Constructor /// - public LLClientView(EndPoint remoteEP, IScene scene, LLUDPServer udpServer, LLUDPClient udpClient, AuthenticateResponse sessionInfo, + public LLClientView(EndPoint remoteEP, Scene scene, LLUDPServer udpServer, LLUDPClient udpClient, AuthenticateResponse sessionInfo, UUID agentId, UUID sessionId, uint circuitCode) { RegisterInterface(this); RegisterInterface(this); RegisterInterface(this); - + InitDefaultAnimations(); m_scene = scene; + + m_avatarTerseUpdates = new PriorityQueue(); + m_primTerseUpdates = new PriorityQueue(); + m_primFullUpdates = new PriorityQueue(m_scene.Entities.Count); + m_assetService = m_scene.RequestModuleInterface(); m_hyperAssets = m_scene.RequestModuleInterface(); m_GroupsModule = scene.RequestModuleInterface(); @@ -3288,10 +3290,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } Quaternion rotation = data.Rotation; - - if (rotation.X == rotation.Y && - rotation.Y == rotation.Z && - rotation.Z == rotation.W && rotation.W == 0.0f) + if (rotation.W == 0.0f && rotation.X == 0.0f && rotation.Y == 0.0f && rotation.Z == 0.0f) rotation = Quaternion.Identity; ImprovedTerseObjectUpdatePacket.ObjectDataBlock terseBlock = CreateImprovedTerseBlock(data); @@ -3377,15 +3376,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP } Quaternion rotation = data.rotation; + if (rotation.W == 0.0f && rotation.X == 0.0f && rotation.Y == 0.0f && rotation.Z == 0.0f) + rotation = Quaternion.Identity; if (data.AttachPoint > 30 && data.ownerID != AgentId) // Someone else's HUD return; - if (data.primShape.PCode == 9 && data.primShape.State != 0 && data.parentID == 0) + if (data.primShape.State != 0 && data.parentID == 0 && data.primShape.PCode == 9) return; - if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0.0f) - rotation = Quaternion.Identity; - ObjectUpdatePacket.ObjectDataBlock objectData = CreatePrimUpdateBlock(data); lock (m_primFullUpdates.SyncRoot) @@ -3397,7 +3395,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP ObjectUpdatePacket outPacket = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); outPacket.Header.Zerocoded = true; - //outPacket.RegionData = new ObjectUpdatePacket.RegionDataBlock(); outPacket.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); @@ -3424,13 +3421,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP } Quaternion rotation = data.Rotation; + if (rotation.W == 0.0f && rotation.X == 0.0f && rotation.Y == 0.0f && rotation.Z == 0.0f) + rotation = Quaternion.Identity; if (data.AttachPoint > 30 && data.OwnerID != AgentId) // Someone else's HUD return; - if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) - rotation = Quaternion.Identity; - ImprovedTerseObjectUpdatePacket.ObjectDataBlock objectData = CreateImprovedTerseBlock(data); lock (m_primTerseUpdates.SyncRoot) @@ -10238,10 +10234,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP { internal delegate bool UpdatePriorityHandler(ref TPriority priority, uint local_id); - private MinHeap[] heaps = new MinHeap[1]; - private Dictionary lookup_table = new Dictionary(); - private Comparison comparison; - private object sync_root = new object(); + private MinHeap[] m_heaps = new MinHeap[1]; + private Dictionary m_lookupTable; + private Comparison m_comparison; + private object m_syncRoot = new object(); internal PriorityQueue() : this(MinHeap.DEFAULT_CAPACITY, Comparer.Default) { } @@ -10255,19 +10251,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP this(capacity, new Comparison(comparer.Compare)) { } internal PriorityQueue(int capacity, Comparison comparison) { - for (int i = 0; i < heaps.Length; ++i) - heaps[i] = new MinHeap(capacity); - this.comparison = comparison; + m_lookupTable = new Dictionary(capacity); + + for (int i = 0; i < m_heaps.Length; ++i) + m_heaps[i] = new MinHeap(capacity); + this.m_comparison = comparison; } - internal object SyncRoot { get { return this.sync_root; } } + internal object SyncRoot { get { return this.m_syncRoot; } } internal int Count { get { int count = 0; - for (int i = 0; i < heaps.Length; ++i) - count = heaps[i].Count; + for (int i = 0; i < m_heaps.Length; ++i) + count = m_heaps[i].Count; return count; } } @@ -10276,36 +10274,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP { LookupItem item; - if (lookup_table.TryGetValue(local_id, out item)) + if (m_lookupTable.TryGetValue(local_id, out item)) { - item.Heap[item.Handle] = new MinHeapItem(priority, value, local_id, this.comparison); + item.Heap[item.Handle] = new MinHeapItem(priority, value, local_id, this.m_comparison); return false; } else { - item.Heap = heaps[0]; - item.Heap.Add(new MinHeapItem(priority, value, local_id, this.comparison), ref item.Handle); - lookup_table.Add(local_id, item); + 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; } } internal TValue Peek() { - for (int i = 0; i < heaps.Length; ++i) - if (heaps[i].Count > 0) - return heaps[i].Min().Value; + 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 TValue Dequeue() { - for (int i = 0; i < heaps.Length; ++i) + for (int i = 0; i < m_heaps.Length; ++i) { - if (heaps[i].Count > 0) + if (m_heaps[i].Count > 0) { - MinHeapItem item = heaps[i].RemoveMin(); - lookup_table.Remove(item.LocalID); + MinHeapItem item = m_heaps[i].RemoveMin(); + m_lookupTable.Remove(item.LocalID); return item.Value; } } @@ -10317,7 +10315,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP MinHeapItem item; TPriority priority; - foreach (LookupItem lookup in new List(this.lookup_table.Values)) + foreach (LookupItem lookup in new List(this.m_lookupTable.Values)) { if (lookup.Heap.TryGetValue(lookup.Handle, out item)) { @@ -10332,7 +10330,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { m_log.Warn("[LLCLIENTVIEW]: UpdatePriorityHandler returned false, dropping update"); lookup.Heap.Remove(lookup.Handle); - this.lookup_table.Remove(item.LocalID); + this.m_lookupTable.Remove(item.LocalID); } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index d25bf95ec0..938cf50468 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -51,7 +51,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private bool m_shuttingdown; - private long m_lastloopprocessed; private AssetBase m_missingImage; private LLClientView m_client; //Client we're assigned to private IAssetService m_assetCache; //Asset Cache @@ -169,7 +168,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public bool ProcessImageQueue(int packetsToSend) { - m_lastloopprocessed = DateTime.Now.Ticks; int packetsSent = 0; while (packetsSent < packetsToSend) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 9476eed5dc..4b6a358588 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -506,8 +506,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// for private void BeginFireQueueEmpty(int throttleIndex) { - if (!m_onQueueEmptyRunning[throttleIndex]) - Util.FireAndForget(FireQueueEmpty, throttleIndex); + // Unknown is -1 and Resend is 0. Make sure we are only firing the + // callback for categories other than those + if (throttleIndex > 0) + { + if (!m_onQueueEmptyRunning[throttleIndex]) + Util.FireAndForget(FireQueueEmpty, throttleIndex); + } } /// diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 66e1468a91..74175d0cee 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -107,7 +107,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Manages authentication for agent circuits private AgentCircuitManager m_circuitManager; /// Reference to the scene this UDP server is attached to - private IScene m_scene; + private Scene m_scene; /// The X/Y coordinates of the scene this UDP server is attached to private Location m_location; /// The measured resolution of Environment.TickCount @@ -184,15 +184,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void AddScene(IScene scene) { - if (m_scene == null) - { - m_scene = scene; - m_location = new Location(m_scene.RegionInfo.RegionHandle); - } - else + if (m_scene != null) { m_log.Error("[LLUDPSERVER]: AddScene() called on an LLUDPServer that already has a scene"); + return; } + + if (!(scene is Scene)) + { + m_log.Error("[LLUDPSERVER]: AddScene() called with an unrecognized scene type " + scene.GetType()); + return; + } + + m_scene = (Scene)scene; + m_location = new Location(m_scene.RegionInfo.RegionHandle); } public bool HandlesRegion(Location x) @@ -794,7 +799,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP elapsed500MS = 0; } - m_scene.ClientManager.ForEach( + m_scene.ClientManager.ForEachSync( delegate(IClientAPI client) { if (client is LLClientView) diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs index 332d3ce59e..53c64cb57d 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs @@ -147,9 +147,10 @@ namespace OpenSim.Region.CoreModules.World.Land client.OnParcelDwellRequest += ClientOnParcelDwellRequest; client.OnParcelDeedToGroup += ClientOnParcelDeedToGroup; - if (m_scene.Entities.ContainsKey(client.AgentId)) + EntityBase presenceEntity; + if (m_scene.Entities.TryGetValue(client.AgentId, out presenceEntity) && presenceEntity is ScenePresence) { - SendLandUpdate((ScenePresence)m_scene.Entities[client.AgentId], true); + SendLandUpdate((ScenePresence)presenceEntity, true); SendParcelOverlay(client); } } diff --git a/OpenSim/Region/CoreModules/World/Land/LandObject.cs b/OpenSim/Region/CoreModules/World/Land/LandObject.cs index b9b7da5bc0..bfe85f1e04 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandObject.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandObject.cs @@ -139,10 +139,8 @@ namespace OpenSim.Region.CoreModules.World.Land } else { - //Normal Calculations - return Convert.ToInt32( - Math.Round((Convert.ToDecimal(LandData.Area) / Convert.ToDecimal(65536)) * m_scene.objectCapacity * - Convert.ToDecimal(m_scene.RegionInfo.RegionSettings.ObjectBonus))); ; + // Normal Calculations + return (int)Math.Round(((float)LandData.Area / 65536.0f) * (float)m_scene.objectCapacity * (float)m_scene.RegionInfo.RegionSettings.ObjectBonus); } } public int GetSimulatorMaxPrimCount(ILandObject thisObject) diff --git a/OpenSim/Region/Framework/Scenes/EntityManager.cs b/OpenSim/Region/Framework/Scenes/EntityManager.cs index 0ceef3911a..099fcce559 100644 --- a/OpenSim/Region/Framework/Scenes/EntityManager.cs +++ b/OpenSim/Region/Framework/Scenes/EntityManager.cs @@ -93,40 +93,31 @@ namespace OpenSim.Region.Framework.Scenes { get { - lock (m_lock) - { - return m_eb_uuid.Count; - } + return m_eb_uuid.Count; } } public bool ContainsKey(UUID id) { - lock (m_lock) + try { - try - { - return m_eb_uuid.ContainsKey(id); - } - catch - { - return false; - } + return m_eb_uuid.ContainsKey(id); + } + catch + { + return false; } } public bool ContainsKey(uint localID) { - lock (m_lock) + try { - try - { - return m_eb_localID.ContainsKey(localID); - } - catch - { - return false; - } + return m_eb_localID.ContainsKey(localID); + } + catch + { + return false; } } @@ -136,7 +127,11 @@ namespace OpenSim.Region.Framework.Scenes { try { - bool a = m_eb_uuid.Remove(m_eb_localID[localID].UUID); + bool a = false; + EntityBase entity; + if (m_eb_localID.TryGetValue(localID, out entity)) + a = m_eb_uuid.Remove(entity.UUID); + bool b = m_eb_localID.Remove(localID); return a && b; } @@ -154,7 +149,11 @@ namespace OpenSim.Region.Framework.Scenes { try { - bool a = m_eb_localID.Remove(m_eb_uuid[id].LocalId); + bool a = false; + EntityBase entity; + if (m_eb_uuid.TryGetValue(id, out entity)) + a = m_eb_localID.Remove(entity.LocalId); + bool b = m_eb_uuid.Remove(id); return a && b; } @@ -206,14 +205,11 @@ namespace OpenSim.Region.Framework.Scenes { lock (m_lock) { - try - { - return m_eb_uuid[id]; - } - catch - { + EntityBase entity; + if (m_eb_uuid.TryGetValue(id, out entity)) + return entity; + else return null; - } } } set @@ -228,14 +224,11 @@ namespace OpenSim.Region.Framework.Scenes { lock (m_lock) { - try - { - return m_eb_localID[localID]; - } - catch - { + EntityBase entity; + if (m_eb_localID.TryGetValue(localID, out entity)) + return entity; + else return null; - } } } set diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index c44c4c7940..c2b9e7371c 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -2351,12 +2351,6 @@ namespace OpenSim.Region.Framework.Scenes item = InventoryService.GetItem(item); presence.Appearance.SetAttachment((int)AttachmentPt, itemID, item.AssetID /*att.UUID*/); - IAvatarFactory ava = RequestModuleInterface(); - if (ava != null) - { - ava.UpdateDatabase(remoteClient.AgentId, presence.Appearance); - } - } return att.UUID; } @@ -2402,12 +2396,6 @@ namespace OpenSim.Region.Framework.Scenes InventoryItemBase item = new InventoryItemBase(itemID, remoteClient.AgentId); item = InventoryService.GetItem(item); presence.Appearance.SetAttachment((int)AttachmentPt, itemID, item.AssetID /*att.UUID*/); - - if (m_AvatarFactory != null) - { - m_log.InfoFormat("[SCENE INVENTORY]: Saving avatar attachment. AgentID:{0} ItemID:{1} AttachmentPoint:{2}", remoteClient.AgentId, itemID, AttachmentPt); - m_AvatarFactory.UpdateDatabase(remoteClient.AgentId, presence.Appearance); - } } } @@ -2447,12 +2435,13 @@ namespace OpenSim.Region.Framework.Scenes if (TryGetAvatar(remoteClient.AgentId, out presence)) { presence.Appearance.DetachAttachment(itemID); - IAvatarFactory ava = RequestModuleInterface(); - if (ava != null) - { - ava.UpdateDatabase(remoteClient.AgentId, presence.Appearance); - } + // Save avatar attachment information + if (m_AvatarFactory != null) + { + m_log.Info("[SCENE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId + ", ItemID: " + itemID); + m_AvatarFactory.UpdateDatabase(remoteClient.AgentId, presence.Appearance); + } } m_sceneGraph.DetachSingleAttachmentToInv(itemID, remoteClient); diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 70b11c328e..4f3cc9838c 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -228,6 +228,10 @@ namespace OpenSim.Region.Framework.Scenes protected IXMLRPC m_xmlrpcModule; protected IWorldComm m_worldCommModule; protected IAvatarFactory m_AvatarFactory; + public IAvatarFactory AvatarFactory + { + get { return m_AvatarFactory; } + } protected IConfigSource m_config; protected IRegionSerialiserModule m_serialiser; protected IInterregionCommsOut m_interregionCommsOut; diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 8ee26c3cd7..e51f6ef9bd 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -467,7 +467,6 @@ namespace OpenSim.Region.Framework.Scenes protected internal void AttachObject(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, bool silent) { // If we can't take it, we can't attach it! - // SceneObjectPart part = m_parentScene.GetSceneObjectPart(objectLocalID); if (part == null) return; @@ -477,9 +476,16 @@ namespace OpenSim.Region.Framework.Scenes return; // Calls attach with a Zero position - // AttachObject(remoteClient, objectLocalID, AttachmentPt, rot, Vector3.Zero, false); m_parentScene.SendAttachEvent(objectLocalID, part.ParentGroup.GetFromItemID(), remoteClient.AgentId); + + // Save avatar attachment information + ScenePresence presence; + if (m_parentScene.AvatarFactory != null && m_parentScene.TryGetAvatar(remoteClient.AgentId, out presence)) + { + m_log.Info("[SCENE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId + ", AttachmentPoint: " + AttachmentPt); + m_parentScene.AvatarFactory.UpdateDatabase(remoteClient.AgentId, presence.Appearance); + } } public SceneObjectGroup RezSingleAttachment( @@ -574,7 +580,7 @@ namespace OpenSim.Region.Framework.Scenes } - group.SetAttachmentPoint(Convert.ToByte(AttachmentPt)); + group.SetAttachmentPoint((byte)AttachmentPt); group.AbsolutePosition = attachPos; // Saves and gets itemID diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 2153b9b8cb..810dfd1d83 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -899,7 +899,7 @@ namespace OpenSim.Region.Framework.Scenes SetAttachmentPoint(Convert.ToByte(attachmentpoint)); avatar.AddAttachment(this); - m_log.DebugFormat("[SOG]: Added att {0} to avie {1}", UUID, avatar.UUID); + m_log.Debug("[SOG]: Added attachment " + UUID + " to avatar " + avatar.UUID); if (!silent) { From fdce1be3db287bed901332b90ba57165e201d3fc Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Mon, 19 Oct 2009 16:52:27 -0700 Subject: [PATCH 28/61] * Removed OpenSim.Data.NHibernate * Replaced calls to ThreadPool.QueueUserWorkItem() with ThreadPool.UnsafeQueueUserWorkItem() since OpenSim does not use Code Access Security sandboxing --- OpenSim/Data/NHibernate/ColorUserType.cs | 104 ---- OpenSim/Data/NHibernate/EstateRegionLink.cs | 76 --- .../Data/NHibernate/LLQuaternionUserType.cs | 119 ----- OpenSim/Data/NHibernate/LLUUIDUserType.cs | 105 ---- OpenSim/Data/NHibernate/LLVector3UserType.cs | 110 ----- OpenSim/Data/NHibernate/Migration/README.txt | 3 - .../NHibernate/Migration/SqliteAssets.sql | 5 - .../NHibernate/Migration/SqliteInventory.pl | 43 -- .../Data/NHibernate/NHibernateAssetData.cs | 135 ----- .../Data/NHibernate/NHibernateEstateData.cs | 168 ------- OpenSim/Data/NHibernate/NHibernateGridData.cs | 236 --------- .../NHibernate/NHibernateInventoryData.cs | 382 --------------- OpenSim/Data/NHibernate/NHibernateManager.cs | 345 ------------- .../Data/NHibernate/NHibernateRegionData.cs | 426 ---------------- OpenSim/Data/NHibernate/NHibernateUserData.cs | 461 ------------------ .../NHibernate/Resources/AssetBase.hbm.xml | 14 - .../Resources/EstateRegionLink.hbm.xml | 12 - .../Resources/EstateSettings.hbm.xml | 68 --- .../Resources/InventoryFolderBase.hbm.xml | 13 - .../Resources/InventoryItemBase.hbm.xml | 26 - .../Resources/MigrationSyntaxDifferences.txt | 14 - .../MsSql2005Dialect/001_AssetStore.sql | 10 - .../MsSql2005Dialect/001_EstateStore.sql | 72 --- .../MsSql2005Dialect/001_GridStore.sql | 35 -- .../MsSql2005Dialect/001_InventoryStore.sql | 36 -- .../MsSql2005Dialect/001_RegionStore.sql | 104 ---- .../MsSql2005Dialect/001_UserStore.sql | 77 --- .../MsSql2005Dialect/002_RegionStore.sql | 51 -- .../MsSql2005Dialect/002_UserStore.sql | 27 - .../Resources/MySQLDialect/001_AssetStore.sql | 10 - .../MySQLDialect/001_EstateStore.sql | 71 --- .../Resources/MySQLDialect/001_GridStore.sql | 35 -- .../MySQLDialect/001_InventoryStore.sql | 39 -- .../MySQLDialect/001_RegionStore.sql | 169 ------- .../Resources/MySQLDialect/001_UserStore.sql | 104 ---- .../OpenSim.Data.NHibernate.addin.xml | 23 - .../PostgreSQLDialect/001_AssetStore.sql | 10 - .../PostgreSQLDialect/001_EstateStore.sql | 72 --- .../PostgreSQLDialect/001_GridStore.sql | 35 -- .../PostgreSQLDialect/001_InventoryStore.sql | 39 -- .../PostgreSQLDialect/001_RegionStore.sql | 169 ------- .../PostgreSQLDialect/001_UserStore.sql | 104 ---- .../Resources/RegionProfileData.hbm.xml | 44 -- .../Resources/RegionSettings.hbm.xml | 56 --- .../NHibernate/Resources/RegionStore.hbm.xml | 147 ------ .../SQLiteDialect/001_AssetStore.sql | 10 - .../SQLiteDialect/001_EstateStore.sql | 71 --- .../Resources/SQLiteDialect/001_GridStore.sql | 35 -- .../SQLiteDialect/001_InventoryStore.sql | 39 -- .../SQLiteDialect/001_RegionStore.sql | 168 ------- .../Resources/SQLiteDialect/001_UserStore.sql | 104 ---- .../Resources/UserAgentData.hbm.xml | 32 -- .../Resources/UserAppearance.hbm.xml | 38 -- .../NHibernate/Resources/UserFriend.hbm.xml | 11 - .../Resources/UserProfileData.hbm.xml | 38 -- OpenSim/Data/NHibernate/SByteType.cs | 111 ----- OpenSim/Data/NHibernate/Terrain.cs | 118 ----- .../Tests/NHibernateMsSqlAssetTest.cs | 81 --- .../Tests/NHibernateMsSqlEstateTest.cs | 81 --- .../Tests/NHibernateMsSqlGridTest.cs | 79 --- .../Tests/NHibernateMsSqlInventoryTest.cs | 80 --- .../Tests/NHibernateMsSqlRegionTest.cs | 81 --- .../Tests/NHibernateMsSqlUserTest.cs | 79 --- .../Tests/NHibernateMySQLAssetTest.cs | 80 --- .../Tests/NHibernateMySQLGridTest.cs | 79 --- .../Tests/NHibernateMySQLInventoryTest.cs | 80 --- .../Tests/NHibernateMySQLRegionTest.cs | 81 --- .../Tests/NHibernateMySQLUserTest.cs | 79 --- .../Tests/NHibernateMySqlEstateTest.cs | 80 --- .../Tests/NHibernatePostgreSQLAssetTest.cs | 80 --- .../Tests/NHibernatePostgreSQLEstateTest.cs | 80 --- .../Tests/NHibernatePostgreSQLGridTest.cs | 79 --- .../NHibernatePostgreSQLInventoryTest.cs | 80 --- .../Tests/NHibernatePostgreSQLRegionTest.cs | 80 --- .../Tests/NHibernatePostgreSQLUserTest.cs | 79 --- .../Tests/NHibernateSQLiteAssetTest.cs | 82 ---- .../Tests/NHibernateSQLiteEstateTest.cs | 82 ---- .../Tests/NHibernateSQLiteGridTest.cs | 80 --- .../Tests/NHibernateSQLiteInventoryTest.cs | 82 ---- .../Tests/NHibernateSQLiteRegionTest.cs | 82 ---- .../Tests/NHibernateSQLiteUserTest.cs | 82 ---- OpenSim/Data/NHibernate/TextureUserType.cs | 115 ----- OpenSim/Data/NHibernate/UInt16Type.cs | 103 ---- OpenSim/Data/NHibernate/UInt32Type.cs | 103 ---- OpenSim/Data/NHibernate/UInt64Type.cs | 103 ---- OpenSim/Data/NHibernate/UserFriend.cs | 72 --- OpenSim/Data/Tests/BasicRegionTest.cs | 1 - OpenSim/Data/Tests/DataTestUtil.cs | 2 +- .../Framework/Communications/RestClient.cs | 2 +- OpenSim/Framework/Parallel.cs | 6 +- .../Servers/HttpServer/BaseHttpServer.cs | 2 +- .../MessagingServer.Modules/MessageService.cs | 2 +- .../ClientStack/LindenUDP/LLClientView.cs | 6 +- .../CoreModules/Asset/FlotsamAssetCache.cs | 4 +- .../World/WorldMap/WorldMapModule.cs | 2 +- bin/AssetInventoryServer.ini.example | 6 - bin/NHibernate.Mapping.Attributes.dll | Bin 231936 -> 0 bytes bin/NHibernate.dll | Bin 1638400 -> 0 bytes bin/OpenSim.32BitLaunch.exe.config | 6 - bin/OpenSim.Grid.UserServer.exe.config | 6 - bin/OpenSim.exe.config | 6 - bin/OpenSim.ini.example | 6 - prebuild.xml | 79 --- 103 files changed, 13 insertions(+), 7726 deletions(-) delete mode 100644 OpenSim/Data/NHibernate/ColorUserType.cs delete mode 100644 OpenSim/Data/NHibernate/EstateRegionLink.cs delete mode 100644 OpenSim/Data/NHibernate/LLQuaternionUserType.cs delete mode 100644 OpenSim/Data/NHibernate/LLUUIDUserType.cs delete mode 100644 OpenSim/Data/NHibernate/LLVector3UserType.cs delete mode 100644 OpenSim/Data/NHibernate/Migration/README.txt delete mode 100644 OpenSim/Data/NHibernate/Migration/SqliteAssets.sql delete mode 100755 OpenSim/Data/NHibernate/Migration/SqliteInventory.pl delete mode 100644 OpenSim/Data/NHibernate/NHibernateAssetData.cs delete mode 100644 OpenSim/Data/NHibernate/NHibernateEstateData.cs delete mode 100644 OpenSim/Data/NHibernate/NHibernateGridData.cs delete mode 100644 OpenSim/Data/NHibernate/NHibernateInventoryData.cs delete mode 100644 OpenSim/Data/NHibernate/NHibernateManager.cs delete mode 100644 OpenSim/Data/NHibernate/NHibernateRegionData.cs delete mode 100644 OpenSim/Data/NHibernate/NHibernateUserData.cs delete mode 100644 OpenSim/Data/NHibernate/Resources/AssetBase.hbm.xml delete mode 100644 OpenSim/Data/NHibernate/Resources/EstateRegionLink.hbm.xml delete mode 100644 OpenSim/Data/NHibernate/Resources/EstateSettings.hbm.xml delete mode 100644 OpenSim/Data/NHibernate/Resources/InventoryFolderBase.hbm.xml delete mode 100644 OpenSim/Data/NHibernate/Resources/InventoryItemBase.hbm.xml delete mode 100644 OpenSim/Data/NHibernate/Resources/MigrationSyntaxDifferences.txt delete mode 100644 OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_AssetStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_EstateStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_GridStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_InventoryStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_RegionStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_UserStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/002_RegionStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/002_UserStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MySQLDialect/001_AssetStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MySQLDialect/001_EstateStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MySQLDialect/001_GridStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MySQLDialect/001_InventoryStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MySQLDialect/001_RegionStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/MySQLDialect/001_UserStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/OpenSim.Data.NHibernate.addin.xml delete mode 100644 OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_AssetStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_EstateStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_GridStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_InventoryStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_RegionStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_UserStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/RegionProfileData.hbm.xml delete mode 100644 OpenSim/Data/NHibernate/Resources/RegionSettings.hbm.xml delete mode 100644 OpenSim/Data/NHibernate/Resources/RegionStore.hbm.xml delete mode 100644 OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_AssetStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_EstateStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_GridStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_InventoryStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_RegionStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_UserStore.sql delete mode 100644 OpenSim/Data/NHibernate/Resources/UserAgentData.hbm.xml delete mode 100644 OpenSim/Data/NHibernate/Resources/UserAppearance.hbm.xml delete mode 100644 OpenSim/Data/NHibernate/Resources/UserFriend.hbm.xml delete mode 100644 OpenSim/Data/NHibernate/Resources/UserProfileData.hbm.xml delete mode 100644 OpenSim/Data/NHibernate/SByteType.cs delete mode 100644 OpenSim/Data/NHibernate/Terrain.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateMsSqlAssetTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateMsSqlEstateTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateMsSqlGridTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateMsSqlInventoryTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateMsSqlRegionTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateMsSqlUserTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateMySQLAssetTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateMySQLGridTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateMySQLInventoryTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateMySQLRegionTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateMySQLUserTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateMySqlEstateTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLAssetTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLEstateTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLGridTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLInventoryTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLRegionTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLUserTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateSQLiteAssetTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateSQLiteEstateTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateSQLiteGridTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateSQLiteInventoryTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateSQLiteRegionTest.cs delete mode 100644 OpenSim/Data/NHibernate/Tests/NHibernateSQLiteUserTest.cs delete mode 100644 OpenSim/Data/NHibernate/TextureUserType.cs delete mode 100644 OpenSim/Data/NHibernate/UInt16Type.cs delete mode 100644 OpenSim/Data/NHibernate/UInt32Type.cs delete mode 100644 OpenSim/Data/NHibernate/UInt64Type.cs delete mode 100644 OpenSim/Data/NHibernate/UserFriend.cs delete mode 100644 bin/NHibernate.Mapping.Attributes.dll delete mode 100644 bin/NHibernate.dll diff --git a/OpenSim/Data/NHibernate/ColorUserType.cs b/OpenSim/Data/NHibernate/ColorUserType.cs deleted file mode 100644 index 7ac23601da..0000000000 --- a/OpenSim/Data/NHibernate/ColorUserType.cs +++ /dev/null @@ -1,104 +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.Data; -using System.Drawing; -using NHibernate; -using NHibernate.SqlTypes; -using NHibernate.UserTypes; - -namespace OpenSim.Data.NHibernate -{ - [Serializable] - public class ColorUserType : IUserType - { - public object Assemble(object cached, object owner) - { - return cached; - } - - bool IUserType.Equals(object color1, object color2) - { - return color1.Equals(color2); - } - - public object DeepCopy(object color) - { - return color; - } - - public object Disassemble(object color) - { - return color; - } - - public int GetHashCode(object color) - { - return (color == null) ? 0 : color.GetHashCode(); - } - - public bool IsMutable - { - get { return false; } - } - - public object NullSafeGet(IDataReader rs, string[] names, object owner) - { - Color color=Color.Empty; - - int ord = rs.GetOrdinal(names[0]); - if (!rs.IsDBNull(ord)) - { - color = Color.FromArgb(rs.GetInt32(ord)); - } - - return color; - } - - public void NullSafeSet(IDbCommand cmd, object obj, int index) - { - Color color = (Color)obj; - ((IDataParameter)cmd.Parameters[index]).Value = color.ToArgb(); - } - - public object Replace(object original, object target, object owner) - { - return original; - } - - public Type ReturnedType - { - get { return typeof(Color); } - } - - public SqlType[] SqlTypes - { - get { return new SqlType [] { NHibernateUtil.Int32.SqlType }; } - } - } -} diff --git a/OpenSim/Data/NHibernate/EstateRegionLink.cs b/OpenSim/Data/NHibernate/EstateRegionLink.cs deleted file mode 100644 index 4b83fa51b1..0000000000 --- a/OpenSim/Data/NHibernate/EstateRegionLink.cs +++ /dev/null @@ -1,76 +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 OpenMetaverse; - -namespace OpenSim.Data.NHibernate -{ - public class EstateRegionLink - { - private UUID estateRegionLinkID; - public UUID EstateRegionLinkID - { - get - { - return estateRegionLinkID; - } - set - { - estateRegionLinkID = value; - } - } - - private uint estateID; - public uint EstateID - { - get - { - return estateID; - } - set - { - estateID = value; - } - } - - private UUID regionID; - public UUID RegionID - { - get - { - return regionID; - } - set - { - regionID = value; - } - } - } -} diff --git a/OpenSim/Data/NHibernate/LLQuaternionUserType.cs b/OpenSim/Data/NHibernate/LLQuaternionUserType.cs deleted file mode 100644 index cf87827d43..0000000000 --- a/OpenSim/Data/NHibernate/LLQuaternionUserType.cs +++ /dev/null @@ -1,119 +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.Data; -using NHibernate; -using NHibernate.SqlTypes; -using NHibernate.UserTypes; -using OpenMetaverse; - -namespace OpenSim.Data.NHibernate -{ - [Serializable] - public class QuaternionUserType: IUserType - { - public object Assemble(object cached, object owner) - { - return cached; - } - - bool IUserType.Equals(object quat1, object quat2) - { - return quat1.Equals(quat2); - } - - public object DeepCopy(object quat) - { - Quaternion q = (Quaternion)quat; - return new Quaternion(q); - } - - public object Disassemble(object quat) - { - return quat; - } - - public int GetHashCode(object quat) - { - return (quat == null) ? 0 : quat.GetHashCode(); - } - - public bool IsMutable - { - get { return false; } - } - - public object NullSafeGet(IDataReader rs, string[] names, object owner) - { - object quat = null; - - int x = rs.GetOrdinal(names[0]); - int y = rs.GetOrdinal(names[1]); - int z = rs.GetOrdinal(names[2]); - int w = rs.GetOrdinal(names[3]); - if (!rs.IsDBNull(x)) - { - float X = (Single)Convert.ToDouble(rs[x].ToString()); - float Y = (Single)Convert.ToDouble(rs[y].ToString()); - float Z = (Single)Convert.ToDouble(rs[z].ToString()); - float W = (Single)Convert.ToDouble(rs[w].ToString()); - quat = new Quaternion(X, Y, Z, W); - } - return quat; - } - - public void NullSafeSet(IDbCommand cmd, object obj, int index) - { - Quaternion quat = (Quaternion)obj; - ((IDataParameter)cmd.Parameters[index]).Value = quat.X; - ((IDataParameter)cmd.Parameters[index + 1]).Value = quat.Y; - ((IDataParameter)cmd.Parameters[index + 2]).Value = quat.Z; - ((IDataParameter)cmd.Parameters[index + 3]).Value = quat.W; - } - - public object Replace(object original, object target, object owner) - { - return original; - } - - public Type ReturnedType - { - get { return typeof(Quaternion); } - } - - public SqlType[] SqlTypes - { - get { return new SqlType [] { - NHibernateUtil.Single.SqlType, - NHibernateUtil.Single.SqlType, - NHibernateUtil.Single.SqlType, - NHibernateUtil.Single.SqlType - }; } - } - } -} diff --git a/OpenSim/Data/NHibernate/LLUUIDUserType.cs b/OpenSim/Data/NHibernate/LLUUIDUserType.cs deleted file mode 100644 index 8e652ff1a4..0000000000 --- a/OpenSim/Data/NHibernate/LLUUIDUserType.cs +++ /dev/null @@ -1,105 +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.Data; -using NHibernate; -using NHibernate.SqlTypes; -using NHibernate.UserTypes; -using OpenMetaverse; - -namespace OpenSim.Data.NHibernate -{ - [Serializable] - public class UUIDUserType: IUserType - { - public object Assemble(object cached, object owner) - { - return cached; - } - - bool IUserType.Equals(object uuid1, object uuid2) - { - return uuid1.Equals(uuid2); - } - - public object DeepCopy(object uuid) - { - return uuid; - } - - public object Disassemble(object uuid) - { - return uuid; - } - - public int GetHashCode(object uuid) - { - return (uuid == null) ? 0 : uuid.GetHashCode(); - } - - public bool IsMutable - { - get { return false; } - } - - public object NullSafeGet(IDataReader rs, string[] names, object owner) - { - object uuid = null; - - int ord = rs.GetOrdinal(names[0]); - if (!rs.IsDBNull(ord)) - { - string first = (string)rs.GetString(ord); - uuid = new UUID(first); - } - - return uuid; - } - - public void NullSafeSet(IDbCommand cmd, object obj, int index) - { - UUID uuid = (UUID)obj; - ((IDataParameter)cmd.Parameters[index]).Value = uuid.ToString(); - } - - public object Replace(object original, object target, object owner) - { - return original; - } - - public Type ReturnedType - { - get { return typeof(UUID); } - } - - public SqlType[] SqlTypes - { - get { return new SqlType [] { NHibernateUtil.String.SqlType }; } - } - } -} diff --git a/OpenSim/Data/NHibernate/LLVector3UserType.cs b/OpenSim/Data/NHibernate/LLVector3UserType.cs deleted file mode 100644 index 9fa4603496..0000000000 --- a/OpenSim/Data/NHibernate/LLVector3UserType.cs +++ /dev/null @@ -1,110 +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.Data; -using NHibernate; -using NHibernate.SqlTypes; -using NHibernate.UserTypes; -using OpenMetaverse; - -namespace OpenSim.Data.NHibernate -{ - [Serializable] - public class Vector3UserType: IUserType - { - public object Assemble(object cached, object owner) - { - return cached; - } - - bool IUserType.Equals(object vector1, object vector2) - { - return vector1.Equals(vector2); - } - - public object DeepCopy(object vector) - { - return new Vector3((Vector3) vector); - } - - public object Disassemble(object vector) - { - return vector; - } - - public int GetHashCode(object vector) - { - return (vector == null) ? 0 : vector.GetHashCode(); - } - - public bool IsMutable - { - get { return false; } - } - - public object NullSafeGet(IDataReader rs, string[] names, object owner) - { - object vector = null; - - int x = rs.GetOrdinal(names[0]); - int y = rs.GetOrdinal(names[1]); - int z = rs.GetOrdinal(names[2]); - if (!rs.IsDBNull(x) && !rs.IsDBNull(y) && !rs.IsDBNull(z)) - { - float X = (Single)Convert.ToDouble(rs[x].ToString()); - float Y = (Single)Convert.ToDouble(rs[y].ToString()); - float Z = (Single)Convert.ToDouble(rs[z].ToString()); - vector = new Vector3(X, Y, Z); - } - return vector; - } - - public void NullSafeSet(IDbCommand cmd, object obj, int index) - { - Vector3 vector = (Vector3)obj; - ((IDataParameter)cmd.Parameters[index]).Value = vector.X; - ((IDataParameter)cmd.Parameters[index + 1]).Value = vector.Y; - ((IDataParameter)cmd.Parameters[index + 2]).Value = vector.Z; - } - - public object Replace(object original, object target, object owner) - { - return original; - } - - public Type ReturnedType - { - get { return typeof(Vector3); } - } - - public SqlType[] SqlTypes - { - get { return new SqlType [] { NHibernateUtil.Single.SqlType, NHibernateUtil.Single.SqlType, NHibernateUtil.Single.SqlType }; } - } - } -} diff --git a/OpenSim/Data/NHibernate/Migration/README.txt b/OpenSim/Data/NHibernate/Migration/README.txt deleted file mode 100644 index 37769605fa..0000000000 --- a/OpenSim/Data/NHibernate/Migration/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -This directory contains migration scripts for migrating from other -database backends in OpenSim to the NHibernate version of that same -database driver. \ No newline at end of file diff --git a/OpenSim/Data/NHibernate/Migration/SqliteAssets.sql b/OpenSim/Data/NHibernate/Migration/SqliteAssets.sql deleted file mode 100644 index 4a7e0d1b75..0000000000 --- a/OpenSim/Data/NHibernate/Migration/SqliteAssets.sql +++ /dev/null @@ -1,5 +0,0 @@ --- The following converts the UUID from XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --- to XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. This puts it in Guid native format --- for .NET, and the prefered format for LLUUID. - -update assets set UUID = SUBSTR(UUID,1,8) || "-" || SUBSTR(UUID,9,4) || "-" || SUBSTR(UUID,13,4) || "-" || SUBSTR(UUID,17,4) || "-" || SUBSTR(UUID,21,12) where UUID not like '%-%'; \ No newline at end of file diff --git a/OpenSim/Data/NHibernate/Migration/SqliteInventory.pl b/OpenSim/Data/NHibernate/Migration/SqliteInventory.pl deleted file mode 100755 index c59cbcee5a..0000000000 --- a/OpenSim/Data/NHibernate/Migration/SqliteInventory.pl +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/perl - -# -- CREATE TABLE inventoryitems(UUID varchar(255) primary key, -# -- assetID varchar(255), -# -- assetType integer, -# -- invType integer, -# -- parentFolderID varchar(255), -# -- avatarID varchar(255), -# -- creatorsID varchar(255), -# -- inventoryName varchar(255), -# -- inventoryDescription varchar(255), -# -- inventoryNextPermissions integer, -# -- inventoryCurrentPermissions integer, -# -- inventoryBasePermissions integer, -# -- inventoryEveryOnePermissions integer); - -# -- CREATE TABLE inventoryfolders(UUID varchar(255) primary key, -# -- name varchar(255), -# -- agentID varchar(255), -# -- parentID varchar(255), -# -- type integer, -# -- version integer); - -my $items = "INSERT INTO InventoryItems(ID, AssetID, AssetType, InvType, Folder, Owner, Creator, Name, Description, NextPermissions, CurrentPermissions, BasePermissions, EveryOnePermissions) "; -my $folders = "INSERT INTO InventoryFolders(ID, Name, Owner, ParentID, Type, Version) "; - -open(SQLITE, "sqlite3 inventoryStore.db .dump |") or die "can't open the database for migration"; -open(WRITE,"| sqlite3 Inventory.db"); - -while(my $line = ) { - $line =~ s/([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})/$1-$2-$3-$4-$5/g; - if($line =~ s/(INSERT INTO "inventoryitems")/$items/) { - print $line; - print WRITE $line; - } - if($line =~ s/(INSERT INTO "inventoryfolders")/$folders/) { - print $line; - print WRITE $line; - } -} - -close(WRITE); -close(SQLITE); diff --git a/OpenSim/Data/NHibernate/NHibernateAssetData.cs b/OpenSim/Data/NHibernate/NHibernateAssetData.cs deleted file mode 100644 index aaba15c713..0000000000 --- a/OpenSim/Data/NHibernate/NHibernateAssetData.cs +++ /dev/null @@ -1,135 +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.Reflection; -using System.Collections.Generic; -using log4net; -using OpenMetaverse; -using OpenSim.Framework; - -namespace OpenSim.Data.NHibernate -{ - /// - /// A User storage interface for the DB4o database system - /// - public class NHibernateAssetData : AssetDataBase - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private NHibernateManager manager; - public NHibernateManager Manager - { - get - { - return manager; - } - } - - override public void Dispose() { } - - public override void Initialise() - { - m_log.Info("[NHibernateGridData]: " + Name + " cannot be default-initialized!"); - throw new PluginNotInitialisedException(Name); - } - - public override void Initialise(string connect) - { - - m_log.InfoFormat("[NHIBERNATE] Initializing NHibernateAssetData"); - manager = new NHibernateManager(connect, "AssetStore"); - - } - - override public AssetBase GetAsset(UUID uuid) - { - return (AssetBase)manager.Get(typeof(AssetBase), uuid); - } - - override public void StoreAsset(AssetBase asset) - { - AssetBase temp = (AssetBase)manager.Get(typeof(AssetBase), asset.FullID); - if (temp == null) - { - m_log.InfoFormat("[NHIBERNATE] inserting asset {0}", asset.FullID); - manager.Insert(asset); - } - else - { - m_log.InfoFormat("[NHIBERNATE] updating asset {0}", asset.FullID); - manager.Update(asset); - } - } - - // private void LogAssetLoad(AssetBase asset) - // { - // string temporary = asset.Temporary ? "Temporary" : "Stored"; - // string local = asset.Local ? "Local" : "Remote"; - - // int assetLength = (asset.Data != null) ? asset.Data.Length : 0; - - // m_log.Info("[SQLITE]: " + - // string.Format("Loaded {6} {5} Asset: [{0}][{3}/{4}] \"{1}\":{2} ({7} bytes)", - // asset.FullID, asset.Name, asset.Description, asset.Type, - // asset.InvType, temporary, local, assetLength)); - // } - - override public bool ExistsAsset(UUID uuid) - { - m_log.InfoFormat("[NHIBERNATE] ExistsAsset: {0}", uuid); - return (GetAsset(uuid) != null); - } - - /// - /// Returns a list of AssetMetadata objects. The list is a subset of - /// the entire data set offset by containing - /// elements. - /// - /// The number of results to discard from the total data set. - /// The number of rows the returned list should contain. - /// A list of AssetMetadata objects. - public override List FetchAssetMetadataSet(int start, int count) - { - List retList = new List(count); - return retList; - } - - public void DeleteAsset(UUID uuid) - { - - } - - public override string Name { - get { return "NHibernate"; } - } - - public override string Version { - get { return "0.1"; } - } - - } -} diff --git a/OpenSim/Data/NHibernate/NHibernateEstateData.cs b/OpenSim/Data/NHibernate/NHibernateEstateData.cs deleted file mode 100644 index 5c5be9f29b..0000000000 --- a/OpenSim/Data/NHibernate/NHibernateEstateData.cs +++ /dev/null @@ -1,168 +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.Reflection; -using log4net; -using OpenMetaverse; -using OpenSim.Framework; -using OpenSim.Region.Framework.Interfaces; -using NHibernate; -using NHibernate.Criterion; -using System.Collections; -using System; - -namespace OpenSim.Data.NHibernate -{ - /// - /// A User storage interface for the DB4o database system - /// - public class NHibernateEstateData : IEstateDataStore - { - - #region Fields - - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private NHibernateManager manager; - public NHibernateManager Manager - { - get - { - return manager; - } - } - - public string Name - { - get { return "NHibernateEstateData"; } - } - - public string Version - { - get { return "0.1"; } - } - - #endregion - - #region Startup and shutdown. - - public void Initialise() - { - m_log.Info("[NHIBERNATE]: " + Name + " cannot be default-initialized!"); - throw new PluginNotInitialisedException(Name); - } - - public void Initialise(string connect) - { - - m_log.InfoFormat("[NHIBERNATE] Initializing " + Name + "."); - manager = new NHibernateManager(connect, "EstateStore"); - } - - public void Dispose() { } - - #endregion - - #region IEstateDataStore Members - - public EstateSettings LoadEstateSettings(UUID regionID) - { - EstateRegionLink link = LoadEstateRegionLink(regionID); - - // Ensure that estate settings exist for the link - if (link != null) - { - if (manager.GetWithStatefullSession(typeof(EstateSettings), link.EstateID) == null) - { - // Delete broken link - manager.Delete(link); - link = null; - } - } - - // If estate link does not exist create estate settings and link it to region. - if (link == null) - { - EstateSettings estateSettings = new EstateSettings(); - //estateSettings.EstateOwner = UUID.Random(); - //estateSettings.BlockDwell = false; - object identifier = manager.Insert(estateSettings); - - if (identifier == null) - { - // Saving failed. Error is logged in the manager. - return null; - } - - uint estateID = (uint)identifier; - link = new EstateRegionLink(); - link.EstateRegionLinkID = UUID.Random(); - link.RegionID = regionID; - link.EstateID = estateID; - manager.InsertWithStatefullSession(link); - } - - // Load estate settings according to the existing or created link. - return (EstateSettings)manager.GetWithStatefullSession(typeof(EstateSettings), link.EstateID); - } - - public void StoreEstateSettings(EstateSettings estateSettings) - { - // Estates are always updated when stored. - // Insert is always done via. load method as with the current API - // this is explicitly the only way to create region link. - manager.UpdateWithStatefullSession(estateSettings); - } - - #endregion - - #region Private Utility Methods - private EstateRegionLink LoadEstateRegionLink(UUID regionID) - { - ICriteria criteria = manager.GetSession().CreateCriteria(typeof(EstateRegionLink)); - criteria.Add(Expression.Eq("RegionID", regionID)); - IList links = criteria.List(); - - // Fail fast if more than one estate links exist - if (links.Count > 1) - { - m_log.Error("[NHIBERNATE]: Region had more than one estate linked: " + regionID); - throw new Exception("[NHIBERNATE]: Region had more than one estate linked: " + regionID); - } - - if (links.Count == 1) - { - return (EstateRegionLink)links[0]; - } - else - { - return null; - } - } - #endregion - } -} diff --git a/OpenSim/Data/NHibernate/NHibernateGridData.cs b/OpenSim/Data/NHibernate/NHibernateGridData.cs deleted file mode 100644 index 018af62c8e..0000000000 --- a/OpenSim/Data/NHibernate/NHibernateGridData.cs +++ /dev/null @@ -1,236 +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; -using System.Collections.Generic; -using System.Reflection; -using log4net; -using NHibernate; -using NHibernate.Criterion; -using OpenMetaverse; -using OpenSim.Framework; - -namespace OpenSim.Data.NHibernate -{ - - /// - /// A GridData Interface to the NHibernate database - /// - public class NHibernateGridData : GridDataBase - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private NHibernateManager manager; - public NHibernateManager Manager - { - get - { - return manager; - } - } - - public override void Initialise() - { - m_log.Info("[NHibernateGridData]: " + Name + " cannot be default-initialized!"); - throw new PluginNotInitialisedException(Name); - } - - public override void Initialise(string connect) - { - m_log.InfoFormat("[NHIBERNATE] Initializing NHibernateGridData"); - manager = new NHibernateManager(connect, "GridStore"); - } - - /*********************************************************************** - * - * Public Interface Functions - * - **********************************************************************/ - - public override void Dispose() { } - - /// - /// The plugin being loaded - /// - /// A string containing the plugin name - public override string Name - { - get { return "NHibernate Grid Data Interface"; } - } - - /// - /// The plugins version - /// - /// A string containing the plugin version - public override string Version - { - get - { - Module module = GetType().Module; - Version dllVersion = module.Assembly.GetName().Version; - - return string.Format("{0}.{1}.{2}.{3}", - dllVersion.Major, dllVersion.Minor, dllVersion.Build, dllVersion.Revision); - } - } - - public override bool AuthenticateSim(UUID UUID, ulong regionHandle, string simrecvkey) - { - bool throwHissyFit = false; // Should be true by 1.0 - - if (throwHissyFit) - throw new Exception("CRYPTOWEAK AUTHENTICATE: Refusing to authenticate due to replay potential."); - - RegionProfileData data = GetProfileByUUID(UUID); - - return (regionHandle == data.regionHandle && simrecvkey == data.regionSecret); - } - - public override ReservationData GetReservationAtPoint(uint x, uint y) - { - throw new NotImplementedException(); - } - - public override DataResponse StoreProfile(RegionProfileData profile) - { - if (manager.Get(typeof(RegionProfileData), profile.Uuid) == null) - { - manager.Insert(profile); - return DataResponse.RESPONSE_OK; - } - else - { - manager.Update(profile); - return DataResponse.RESPONSE_OK; - } - } - - public override DataResponse DeleteProfile(string uuid) - { - RegionProfileData regionProfileData = (RegionProfileData)manager.Get(typeof(RegionProfileData), new UUID(uuid)); - if (regionProfileData != null) - { - manager.Delete(regionProfileData); - return DataResponse.RESPONSE_OK; - } - return DataResponse.RESPONSE_ERROR; - } - - public override RegionProfileData GetProfileByUUID(UUID UUID) - { - return (RegionProfileData)manager.Get(typeof(RegionProfileData), UUID); - } - - public override RegionProfileData GetProfileByHandle(ulong regionHandle) - { - using (ISession session = manager.GetSession()) - { - ICriteria criteria = session.CreateCriteria(typeof(RegionProfileData)); - criteria.Add(Expression.Eq("RegionHandle", regionHandle)); - - IList regions = criteria.List(); - - if (regions.Count == 1) - { - return (RegionProfileData)regions[0]; - } - else - { - return null; - } - } - } - - public override RegionProfileData GetProfileByString(string regionName) - { - - using (ISession session = manager.GetSession()) - { - ICriteria criteria = session.CreateCriteria(typeof(RegionProfileData)); - criteria.Add(Expression.Eq("RegionName", regionName)); - - IList regions = criteria.List(); - - if (regions.Count == 1) - { - return (RegionProfileData)regions[0]; - } - else - { - return null; - } - } - - } - - public override RegionProfileData[] GetProfilesInRange(uint Xmin, uint Ymin, uint Xmax, uint Ymax) - { - using (ISession session = manager.GetSession()) - { - ICriteria criteria = session.CreateCriteria(typeof(RegionProfileData)); - criteria.Add(Expression.Ge("RegionLocX", Xmin)); - criteria.Add(Expression.Ge("RegionLocY", Ymin)); - criteria.Add(Expression.Le("RegionLocX", Xmax)); - criteria.Add(Expression.Le("RegionLocY", Ymax)); - - IList regions = criteria.List(); - RegionProfileData[] regionArray = new RegionProfileData[regions.Count]; - - for (int i=0;i GetRegionsByName(string namePrefix, uint maxNum) - { - using (ISession session = manager.GetSession()) - { - ICriteria criteria = session.CreateCriteria(typeof(RegionProfileData)); - criteria.SetMaxResults((int)maxNum); - - criteria.Add(Expression.Like("RegionName", namePrefix, MatchMode.Start)); - - IList regions = criteria.List(); - List regionList = new List(); - - foreach (RegionProfileData regionProfileData in regions) - { - regionList.Add(regionProfileData); - } - - return regionList; - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/NHibernateInventoryData.cs b/OpenSim/Data/NHibernate/NHibernateInventoryData.cs deleted file mode 100644 index 130be7e56a..0000000000 --- a/OpenSim/Data/NHibernate/NHibernateInventoryData.cs +++ /dev/null @@ -1,382 +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 NHibernate; -using NHibernate.Criterion; -using OpenMetaverse; -using OpenSim.Framework; - -namespace OpenSim.Data.NHibernate -{ - public class NHibernateInventoryData: IInventoryDataPlugin - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private NHibernateManager manager; - public NHibernateManager Manager - { - get - { - return manager; - } - } - - /// - /// The plugin being loaded - /// - /// A string containing the plugin name - public string Name - { - get { return "NHibernate Inventory Data Interface"; } - } - - /// - /// The plugins version - /// - /// A string containing the plugin version - public string Version - { - get - { - Module module = GetType().Module; - // string dllName = module.Assembly.ManifestModule.Name; - Version dllVersion = module.Assembly.GetName().Version; - - - return - string.Format("{0}.{1}.{2}.{3}", dllVersion.Major, dllVersion.Minor, dllVersion.Build, - dllVersion.Revision); - } - } - - public void Initialise() - { - m_log.Info("[NHibernateInventoryData]: " + Name + " cannot be default-initialized!"); - throw new PluginNotInitialisedException (Name); - } - - /// - /// Initialises the interface - /// - public void Initialise(string connect) - { - m_log.InfoFormat("[NHIBERNATE] Initializing NHibernateInventoryData"); - manager = new NHibernateManager(connect, "InventoryStore"); - } - - /// - /// Closes the interface - /// - public void Dispose() - { - } - - /***************************************************************** - * - * Basic CRUD operations on Data - * - ****************************************************************/ - - // READ - - /// - /// Returns an inventory item by its UUID - /// - /// The UUID of the item to be returned - /// A class containing item information - public InventoryItemBase getInventoryItem(UUID item) - { - try - { - m_log.InfoFormat("[NHIBERNATE] getInventoryItem {0}", item); - return (InventoryItemBase)manager.Get(typeof(InventoryItemBase), item); - } - catch - { - m_log.ErrorFormat("Couldn't find inventory item: {0}", item); - return null; - } - } - - /// - /// Creates a new inventory item based on item - /// - /// The item to be created - public void addInventoryItem(InventoryItemBase item) - { - if (!ExistsItem(item.ID)) - { - manager.Insert(item); - } - else - { - m_log.ErrorFormat("[NHIBERNATE] Attempted to add Inventory Item {0} that already exists, updating instead", item.ID); - updateInventoryItem(item); - } - } - - /// - /// Updates an inventory item with item (updates based on ID) - /// - /// The updated item - public void updateInventoryItem(InventoryItemBase item) - { - if (ExistsItem(item.ID)) - { - manager.Update(item); - } - else - { - m_log.ErrorFormat("[NHIBERNATE] Attempted to add Inventory Item {0} that already exists", item.ID); - } - } - - /// - /// - /// - /// - public void deleteInventoryItem(UUID itemID) - { - InventoryItemBase item = (InventoryItemBase)manager.Get(typeof(InventoryItemBase), itemID); - if (item != null) - { - manager.Delete(item); - } - else - { - m_log.ErrorFormat("[NHIBERNATE] Error deleting InventoryItemBase {0}", itemID); - } - - } - - public InventoryItemBase queryInventoryItem(UUID itemID) - { - return null; - } - - public InventoryFolderBase queryInventoryFolder(UUID folderID) - { - return null; - } - - /// - /// Returns an inventory folder by its UUID - /// - /// The UUID of the folder to be returned - /// A class containing folder information - public InventoryFolderBase getInventoryFolder(UUID folder) - { - try - { - return (InventoryFolderBase)manager.Get(typeof(InventoryFolderBase), folder); - } - catch - { - m_log.ErrorFormat("[NHIBERNATE] Couldn't find inventory item: {0}", folder); - return null; - } - } - - /// - /// Creates a new inventory folder based on folder - /// - /// The folder to be created - public void addInventoryFolder(InventoryFolderBase folder) - { - if (!ExistsFolder(folder.ID)) - { - manager.Insert(folder); - } - else - { - m_log.ErrorFormat("[NHIBERNATE] Attempted to add Inventory Folder {0} that already exists, updating instead", folder.ID); - updateInventoryFolder(folder); - } - } - - /// - /// Updates an inventory folder with folder (updates based on ID) - /// - /// The updated folder - public void updateInventoryFolder(InventoryFolderBase folder) - { - if (ExistsFolder(folder.ID)) - { - manager.Update(folder); - } - else - { - m_log.ErrorFormat("[NHIBERNATE] Attempted to add Inventory Folder {0} that already exists", folder.ID); - } - } - - /// - /// - /// - /// - public void deleteInventoryFolder(UUID folderID) - { - InventoryFolderBase item = (InventoryFolderBase)manager.Get(typeof(InventoryFolderBase), folderID); - if (item != null) - { - manager.Delete(item); - } - else - { - m_log.ErrorFormat("[NHIBERNATE] Error deleting InventoryFolderBase {0}", folderID); - } - manager.Delete(folderID); - } - - // useful private methods - private bool ExistsItem(UUID uuid) - { - return (getInventoryItem(uuid) != null) ? true : false; - } - - private bool ExistsFolder(UUID uuid) - { - return (getInventoryFolder(uuid) != null) ? true : false; - } - - public void Shutdown() - { - // TODO: DataSet commit - } - - // Move seems to be just update - - public void moveInventoryFolder(InventoryFolderBase folder) - { - updateInventoryFolder(folder); - } - - public void moveInventoryItem(InventoryItemBase item) - { - updateInventoryItem(item); - } - - - - /// - /// Returns a list of inventory items contained within the specified folder - /// - /// The UUID of the target folder - /// A List of InventoryItemBase items - public List getInventoryInFolder(UUID folderID) - { - // try { - ICriteria criteria = manager.GetSession().CreateCriteria(typeof(InventoryItemBase)); - criteria.Add(Expression.Eq("Folder", folderID)); - List list = new List(); - foreach (InventoryItemBase item in criteria.List()) - { - list.Add(item); - } - return list; - // } - // catch - // { - // return new List(); - // } - } - - public List getUserRootFolders(UUID user) - { - return new List(); - } - - // see InventoryItemBase.getUserRootFolder - public InventoryFolderBase getUserRootFolder(UUID user) - { - ICriteria criteria = manager.GetSession().CreateCriteria(typeof(InventoryFolderBase)); - criteria.Add(Expression.Eq("ParentID", UUID.Zero)); - criteria.Add(Expression.Eq("Owner", user)); - foreach (InventoryFolderBase folder in criteria.List()) - { - return folder; - } - m_log.ErrorFormat("No Inventory Root Folder Found for: {0}", user); - return null; - } - - /// - /// Append a list of all the child folders of a parent folder - /// - /// list where folders will be appended - /// ID of parent - private void getInventoryFolders(ref List folders, UUID parentID) - { - ICriteria criteria = manager.GetSession().CreateCriteria(typeof(InventoryFolderBase)); - criteria.Add(Expression.Eq("ParentID", parentID)); - foreach (InventoryFolderBase item in criteria.List()) - { - folders.Add(item); - } - } - - /// - /// Returns a list of inventory folders contained in the folder 'parentID' - /// - /// The folder to get subfolders for - /// A list of inventory folders - public List getInventoryFolders(UUID parentID) - { - List folders = new List(); - getInventoryFolders(ref folders, parentID); - return folders; - } - - // See IInventoryDataPlugin - public List getFolderHierarchy(UUID parentID) - { - if (parentID == UUID.Zero) - { - // Zero UUID is not a real parent folder. - return new List(); - } - - List folders = new List(); - - getInventoryFolders(ref folders, parentID); - - for (int i = 0; i < folders.Count; i++) - getInventoryFolders(ref folders, folders[i].ID); - - return folders; - } - - public List fetchActiveGestures (UUID avatarID) - { - return null; - } - } -} diff --git a/OpenSim/Data/NHibernate/NHibernateManager.cs b/OpenSim/Data/NHibernate/NHibernateManager.cs deleted file mode 100644 index 2e7081ee55..0000000000 --- a/OpenSim/Data/NHibernate/NHibernateManager.cs +++ /dev/null @@ -1,345 +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.Data.Common; -using System.Reflection; -using log4net; -using NHibernate; -using NHibernate.Cfg; -using NHibernate.Tool.hbm2ddl; -using OpenMetaverse; -using Environment=NHibernate.Cfg.Environment; - -namespace OpenSim.Data.NHibernate -{ - public class NHibernateManager - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private string dialect; - private Configuration configuration; - private ISessionFactory sessionFactory; - - #region Initialization - - /// - /// Initiate NHibernate Manager - /// - /// NHibernate dialect, driver and connection string separated by ';' - /// Name of the store - public NHibernateManager(string connect, string store) - { - ParseConnectionString(connect); - - //To create sql file uncomment code below and write the name of the file - //SchemaExport exp = new SchemaExport(cfg); - //exp.SetOutputFile("nameofthefile.sql"); - //exp.Create(false, true); - - Assembly assembly = GetType().Assembly; - - sessionFactory = configuration.BuildSessionFactory(); - RunMigration(dialect, assembly, store); - } - - /// - /// Initiate NHibernate Manager with spesific assembly - /// - /// NHibernate dialect, driver and connection string separated by ';' - /// Name of the store - /// Outside assembly to be included - public NHibernateManager(string connect, string store, Assembly assembly) - { - ParseConnectionString(connect); - - configuration.AddAssembly(assembly); - sessionFactory = configuration.BuildSessionFactory(); - RunMigration(dialect, assembly, store); - } - - /// - /// Parses the connection string and creates the NHibernate configuration - /// - /// NHibernate dialect, driver and connection string separated by ';' - private void ParseConnectionString(string connect) - { - // Split out the dialect, driver, and connect string - char[] split = { ';' }; - string[] parts = connect.Split(split, 3); - if (parts.Length != 3) - { - // TODO: make this a real exception type - throw new Exception("Malformed Inventory connection string '" + connect + "'"); - } - - dialect = parts[0]; - - // NHibernate setup - configuration = new Configuration(); - configuration.SetProperty(Environment.ConnectionProvider, - "NHibernate.Connection.DriverConnectionProvider"); - configuration.SetProperty(Environment.Dialect, - "NHibernate.Dialect." + dialect); - configuration.SetProperty(Environment.ConnectionDriver, - "NHibernate.Driver." + parts[1]); - configuration.SetProperty(Environment.ConnectionString, parts[2]); - configuration.AddAssembly("OpenSim.Data.NHibernate"); - } - - /// - /// Runs migration for the the store in assembly - /// - /// Dialect in use - /// Assembly where migration files exist - /// Name of the store in use - private void RunMigration(string dialect, Assembly assembly, string store) - { - // Migration subtype is the folder name under which migrations are stored. For mysql this folder is - // MySQLDialect instead of MySQL5Dialect which is the dialect currently in use. To avoid renaming - // this folder each time the mysql version changes creating simple mapping: - String migrationSubType = dialect; - if (dialect.StartsWith("MySQL")) - { - migrationSubType = "MySQLDialect"; - } - - Migration migration = new Migration((DbConnection)sessionFactory.ConnectionProvider.GetConnection(), assembly, migrationSubType, store); - migration.Update(); - } - - #endregion - - /// - /// Gets object of given type from database with given id. - /// Uses stateless session for efficiency. - /// - /// Type of the object. - /// Id of the object. - /// The object or null if object was not found. - public object Get(Type type, Object id) - { - using (IStatelessSession session = sessionFactory.OpenStatelessSession()) - { - object obj = null; - try - { - obj = session.Get(type.FullName, id); - } - catch (Exception e) - { - m_log.ErrorFormat("[NHIBERNATE] {0} of id {1} loading threw exception: " + e.ToString(), type.Name, id); - } - return obj; - } - } - - /// - /// Gets object of given type from database with given id. - /// Use this method for objects containing collections. For flat objects stateless mode is more efficient. - /// - /// Type of the object. - /// Id of the object. - /// The object or null if object was not found. - public object GetWithStatefullSession(Type type, Object id) - { - using (ISession session = sessionFactory.OpenSession()) - { - object obj = null; - try - { - obj = session.Get(type.FullName, id); - } - catch (Exception e) - { - m_log.ErrorFormat("[NHIBERNATE] {0} of id {1} loading threw exception: " + e.ToString(), type.Name, id); - } - return obj; - } - - } - - /// - /// Inserts given object to database. - /// Uses stateless session for efficiency. - /// - /// Object to be insterted. - /// Identifier of the object. Useful for situations when NHibernate generates the identifier. - public object Insert(object obj) - { - try - { - using (IStatelessSession session = sessionFactory.OpenStatelessSession()) - { - using (ITransaction transaction=session.BeginTransaction()) - { - Object identifier=session.Insert(obj); - transaction.Commit(); - return identifier; - } - } - } - catch (Exception e) - { - m_log.Error("[NHIBERNATE] issue inserting object ", e); - return null; - } - } - - /// - /// Inserts given object to database. - /// Use this method for objects containing collections. For flat objects stateless mode is more efficient. - /// - /// Object to be insterted. - /// Identifier of the object. Useful for situations when NHibernate generates the identifier. - public object InsertWithStatefullSession(object obj) - { - try - { - using (ISession session = sessionFactory.OpenSession()) - { - using (ITransaction transaction = session.BeginTransaction()) - { - Object identifier = session.Save(obj); - transaction.Commit(); - return identifier; - } - } - } - catch (Exception e) - { - m_log.Error("[NHIBERNATE] issue inserting object ", e); - return null; - } - } - - /// - /// Updates given object to database. - /// Uses stateless session for efficiency. - /// - /// Object to be updated. - /// True if operation was succesful. - public bool Update(object obj) - { - try - { - using (IStatelessSession session = sessionFactory.OpenStatelessSession()) - { - using (ITransaction transaction = session.BeginTransaction()) - { - session.Update(obj); - transaction.Commit(); - return true; - } - } - } - catch (Exception e) - { - m_log.Error("[NHIBERNATE] issue updating object ", e); - return false; - } - } - - /// - /// Updates given object to database. - /// Use this method for objects containing collections. For flat objects stateless mode is more efficient. - /// - /// Object to be updated. - /// True if operation was succesful. - public bool UpdateWithStatefullSession(object obj) - { - try - { - using (ISession session = sessionFactory.OpenSession()) - { - using (ITransaction transaction = session.BeginTransaction()) - { - session.Update(obj); - transaction.Commit(); - return true; - } - } - } - catch (Exception e) - { - m_log.Error("[NHIBERNATE] issue updating object ", e); - return false; - } - } - - /// - /// Deletes given object from database. - /// - /// Object to be deleted. - /// True if operation was succesful. - public bool Delete(object obj) - { - try - { - using (IStatelessSession session = sessionFactory.OpenStatelessSession()) - { - using (ITransaction transaction = session.BeginTransaction()) - { - session.Delete(obj); - transaction.Commit(); - return true; - } - } - } - catch (Exception e) - { - m_log.Error("[NHIBERNATE] issue deleting object ", e); - return false; - } - } - - /// - /// Returns statefull session which can be used to execute custom nhibernate or sql queries. - /// - /// Statefull session - public ISession GetSession() - { - return sessionFactory.OpenSession(); - } - - /// - /// Drops the database schema. This exist for unit tests. It should not be invoked from other than test teardown. - /// - public void DropSchema() - { - SchemaExport export = new SchemaExport(this.configuration); - export.Drop(true, true); - - using (ISession session = sessionFactory.OpenSession()) - { - ISQLQuery sqlQuery = session.CreateSQLQuery("drop table migrations"); - sqlQuery.ExecuteUpdate(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/NHibernateRegionData.cs b/OpenSim/Data/NHibernate/NHibernateRegionData.cs deleted file mode 100644 index f19fda161f..0000000000 --- a/OpenSim/Data/NHibernate/NHibernateRegionData.cs +++ /dev/null @@ -1,426 +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 NHibernate; -using NHibernate.Criterion; -using OpenMetaverse; -using OpenSim.Framework; -using OpenSim.Region.Framework.Interfaces; -using OpenSim.Region.Framework.Scenes; - -namespace OpenSim.Data.NHibernate -{ - /// - /// A RegionData Interface to the NHibernate database - /// - public class NHibernateRegionData : IRegionDataStore - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private NHibernateManager manager; - public NHibernateManager Manager - { - get - { - return manager; - } - } - - public void Initialise(string connect) - { - m_log.InfoFormat("[NHIBERNATE] Initializing NHibernateRegionData"); - manager = new NHibernateManager(connect, "RegionStore"); - } - - /*********************************************************************** - * - * Public Interface Functions - * - **********************************************************************/ - - public void Dispose() {} - - public void StoreRegionSettings(RegionSettings rs) - { - RegionSettings oldRegionSettings = (RegionSettings)manager.Get(typeof(RegionSettings), rs.RegionUUID); - if (oldRegionSettings != null) - { - manager.Update(rs); - } - else - { - manager.Insert(rs); - } - } - - public RegionSettings LoadRegionSettings(UUID regionUUID) - { - RegionSettings regionSettings = (RegionSettings) manager.Get(typeof(RegionSettings), regionUUID); - - if (regionSettings == null) - { - regionSettings = new RegionSettings(); - regionSettings.RegionUUID = regionUUID; - manager.Insert(regionSettings); - } - - regionSettings.OnSave += StoreRegionSettings; - - return regionSettings; - } - - // This looks inefficient, but it turns out that it isn't - // based on trial runs with nhibernate 1.2 - private void SaveOrUpdate(SceneObjectPart p) - { - try - { - SceneObjectPart old = (SceneObjectPart)manager.Get(typeof(SceneObjectPart), p.UUID); - if (old != null) - { - m_log.InfoFormat("[NHIBERNATE] updating object {0}", p.UUID); - manager.Update(p); - } - else - { - m_log.InfoFormat("[NHIBERNATE] saving object {0}", p.UUID); - manager.Insert(p); - } - - } - catch (Exception e) - { - m_log.Error("[NHIBERNATE] issue saving part", e); - } - } - - private void SaveOrUpdate(Terrain t) - { - try - { - - Terrain old = (Terrain)manager.Get(typeof(Terrain), t.RegionID); - if (old != null) - { - m_log.InfoFormat("[NHIBERNATE] updating terrain {0}", t.RegionID); - manager.Update(t); - } - else - { - m_log.InfoFormat("[NHIBERNATE] saving terrain {0}", t.RegionID); - manager.Insert(t); - } - - } - catch (Exception e) - { - m_log.Error("[NHIBERNATE] issue saving terrain", e); - } - } - - - /// - /// Adds an object into region storage - /// - /// the object - /// the region UUID - public void StoreObject(SceneObjectGroup obj, UUID regionUUID) - { - uint flags = obj.RootPart.GetEffectiveObjectFlags(); - - // Eligibility check - if ((flags & (uint)PrimFlags.Temporary) != 0) - return; - if ((flags & (uint)PrimFlags.TemporaryOnRez) != 0) - return; - - try - { - foreach (SceneObjectPart part in obj.Children.Values) - { - m_log.InfoFormat("Storing part {0}", part.UUID); - SaveOrUpdate(part); - } - } - catch (Exception e) - { - m_log.Error("Can't save: ", e); - } - } - - private SceneObjectGroup LoadObject(UUID uuid, UUID region) - { - ICriteria criteria = manager.GetSession().CreateCriteria(typeof(SceneObjectPart)); - criteria.Add(Expression.Eq("RegionID", region)); - criteria.Add(Expression.Eq("ParentUUID", uuid)); - criteria.AddOrder(Order.Asc("ParentID")); - - IList parts = criteria.List(); - - SceneObjectGroup group = null; - - // Find the root part - for (int i = 0; i < parts.Count; i++) - { - if (parts[i].UUID == uuid) - { - group = new SceneObjectGroup(parts[i]); - break; - } - } - - // Add the children parts - if (group != null) - { - for (int i = 0; i < parts.Count; i++) - { - if (parts[i].UUID != uuid) - group.AddPart(parts[i]); - } - } - else - { - m_log.Error("[NHIBERNATE]: LoadObject() Attempted to load a SceneObjectGroup with no root SceneObjectPart "); - } - - return group; - } - - /// - /// Removes an object from region storage - /// - /// the object - /// the region UUID - public void RemoveObject(UUID obj, UUID regionUUID) - { - SceneObjectGroup g = LoadObject(obj, regionUUID); - foreach (SceneObjectPart p in g.Children.Values) - { - manager.Delete(p); - } - - // m_log.InfoFormat("[REGION DB]: Removing obj: {0} from region: {1}", obj.Guid, regionUUID); - - } - - /// - /// Load persisted objects from region storage. - /// - /// The region UUID - /// List of loaded groups - public List LoadObjects(UUID regionUUID) - { - Dictionary SOG = new Dictionary(); - List ret = new List(); - - ICriteria criteria = manager.GetSession().CreateCriteria(typeof(SceneObjectPart)); - criteria.Add(Expression.Eq("RegionID", regionUUID)); - criteria.AddOrder(Order.Asc("ParentID")); - criteria.AddOrder(Order.Asc("LinkNum")); - foreach (SceneObjectPart p in criteria.List()) - { - // root part - if (p.UUID == p.ParentUUID) - { - SceneObjectGroup group = new SceneObjectGroup(p); - SOG.Add(p.ParentUUID, group); - } - else - { - SOG[p.ParentUUID].AddPart(p); - } - // get the inventory - - ICriteria InvCriteria = manager.GetSession().CreateCriteria(typeof(TaskInventoryItem)); - InvCriteria.Add(Expression.Eq("ParentPartID", p.UUID)); - IList inventory = new List(); - foreach (TaskInventoryItem i in InvCriteria.List()) - { - inventory.Add(i); - } - - if (inventory.Count > 0) - p.Inventory.RestoreInventoryItems(inventory); - } - foreach (SceneObjectGroup g in SOG.Values) - { - ret.Add(g); - } - - return ret; - } - - /// - /// Store a terrain revision in region storage - /// - /// terrain heightfield - /// region UUID - public void StoreTerrain(double[,] ter, UUID regionID) - { - lock (this) { - Terrain t = new Terrain(regionID, ter); - SaveOrUpdate(t); - } - } - - /// - /// Load the latest terrain revision from region storage - /// - /// the region UUID - /// Heightfield data - public double[,] LoadTerrain(UUID regionID) - { - Terrain t = (Terrain)manager.Get(typeof(Terrain), regionID); - if (t != null) - { - return t.Doubles; - } - - m_log.Info("No terrain yet"); - return null; - } - - /// - /// - /// - /// - public void RemoveLandObject(UUID globalID) - { - - } - - /// - /// - /// - /// - public void StoreLandObject(ILandObject parcel) - { - - } - - /// - /// - /// - /// - /// - public List LoadLandObjects(UUID regionUUID) - { - List landDataForRegion = new List(); - - return landDataForRegion; - } - - - /// - /// See - /// - public void Shutdown() - { - //session.Flush(); - } - - /// - /// Load a region banlist - /// - /// the region UUID - /// The banlist - public List LoadRegionBanList(UUID regionUUID) - { - List regionbanlist = new List(); - - return regionbanlist; - } - - /// - /// Add en entry into region banlist - /// - /// - public void AddToRegionBanlist(EstateBan item) - { - - } - - /// - /// remove an entry from the region banlist - /// - /// - public void RemoveFromRegionBanlist(EstateBan item) - { - - } - - /// - /// - /// - /// - /// -// private static Array serializeTerrain(double[,] val) -// { -// MemoryStream str = new MemoryStream(65536*sizeof (double)); -// BinaryWriter bw = new BinaryWriter(str); -// -// // TODO: COMPATIBILITY - Add byte-order conversions -// for (int x = 0; x < (int)Constants.RegionSize; x++) -// for (int y = 0; y < (int)Constants.RegionSize; y++) -// bw.Write(val[x, y]); -// -// return str.ToArray(); -// } - - /// - /// see IRegionDatastore - /// - /// - /// - public void StorePrimInventory(UUID primID, ICollection items) - { - ICriteria criteria = manager.GetSession().CreateCriteria(typeof(TaskInventoryItem)); - criteria.Add(Expression.Eq("ParentPartID", primID)); - try - { - foreach (TaskInventoryItem i in criteria.List()) - { - manager.Delete(i); - } - - foreach (TaskInventoryItem i in items) - { - manager.Insert(i); - - } - } - catch (Exception e) - { - m_log.Error("[NHIBERNATE] StoreInvetory", e); - } - } - } -} diff --git a/OpenSim/Data/NHibernate/NHibernateUserData.cs b/OpenSim/Data/NHibernate/NHibernateUserData.cs deleted file mode 100644 index 1b0c4c96a0..0000000000 --- a/OpenSim/Data/NHibernate/NHibernateUserData.cs +++ /dev/null @@ -1,461 +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.Collections.Generic; -using System.Reflection; -using log4net; -using NHibernate; -using NHibernate.Criterion; -using OpenMetaverse; -using OpenSim.Framework; - -namespace OpenSim.Data.NHibernate -{ - /// - /// A User storage interface for the DB4o database system - /// - public class NHibernateUserData : UserDataBase - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private NHibernateManager manager; - public NHibernateManager Manager - { - get - { - return manager; - } - } - - public override void Initialise() - { - m_log.Info("[NHibernateUserData]: " + Name + " cannot be default-initialized!"); - throw new PluginNotInitialisedException (Name); - } - - public override void Initialise(string connect) - { - m_log.InfoFormat("[NHIBERNATE] Initializing NHibernateUserData"); - manager = new NHibernateManager(connect, "UserStore"); - } - - private bool ExistsUser(UUID uuid) - { - UserProfileData user = null; - - m_log.InfoFormat("[NHIBERNATE] ExistsUser; {0}", uuid); - user = (UserProfileData)manager.Get(typeof(UserProfileData), uuid); - - if (user == null) - { - m_log.InfoFormat("[NHIBERNATE] User with given UUID does not exist {0} ", uuid); - return false; - } - - return true; - - } - - override public UserProfileData GetUserByUUID(UUID uuid) - { - UserProfileData user; - m_log.InfoFormat("[NHIBERNATE] GetUserByUUID: {0} ", uuid); - - user = (UserProfileData)manager.Get(typeof(UserProfileData), uuid); - if (user != null) - { - UserAgentData agent = GetAgentByUUID(uuid); - if (agent != null) - { - user.CurrentAgent = agent; - } - } - - return user; - } - - override public void AddNewUserProfile(UserProfileData profile) - { - if (profile.ID == UUID.Zero) - { - m_log.ErrorFormat("[NHIBERNATE] Attempted to add User {0} {1} with zero UUID, throwintg exception as this is programming error ", profile.FirstName, profile.SurName); - return; - } - - if (!ExistsUser(profile.ID)) - { - m_log.InfoFormat("[NHIBERNATE] AddNewUserProfile {0}", profile.ID); - manager.Insert(profile); - // Agent should not be saved according to BasicUserTest.T015_UserPersistency() - // SetAgentData(profile.ID, profile.CurrentAgent); - - } - else - { - m_log.ErrorFormat("[NHIBERNATE] Attempted to add User {0} {1} that already exists, updating instead", profile.FirstName, profile.SurName); - UpdateUserProfile(profile); - } - } - - /* - private void SetAgentData(UUID uuid, UserAgentData agent) - { - UserAgentData old = (UserAgentData)manager.Load(typeof(UserAgentData), uuid); - if (old != null) - { - m_log.InfoFormat("[NHIBERNATE] SetAgentData deleting old: {0} ",uuid); - manager.Delete(old); - } - if (agent != null) - { - m_log.InfoFormat("[NHIBERNATE] SetAgentData: {0} ", agent.ProfileID); - manager.Save(agent); - } - } - */ - - override public bool UpdateUserProfile(UserProfileData profile) - { - if (ExistsUser(profile.ID)) - { - manager.Update(profile); - // Agent should not be saved according to BasicUserTest.T015_UserPersistency() - // SetAgentData(profile.ID, profile.CurrentAgent); - return true; - } - else - { - m_log.ErrorFormat("[NHIBERNATE] Attempted to update User {0} {1} that doesn't exist, updating instead", profile.FirstName, profile.SurName); - AddNewUserProfile(profile); - return true; - } - } - - override public void AddNewUserAgent(UserAgentData agent) - { - if (agent.ProfileID == UUID.Zero) - { - m_log.ErrorFormat("[NHIBERNATE] Attempted to add new user agent with zero user id. Agent session id: {0}", agent.SessionID); - return; - } - - if (agent.SessionID == UUID.Zero) - { - m_log.ErrorFormat("[NHIBERNATE] Attempted to add new user agent with zero session id. User profile id: {0}", agent.SessionID); - return; - } - - - UserAgentData old = (UserAgentData)manager.Get(typeof(UserAgentData), agent.ProfileID); - if (old != null) - { - manager.Delete(old); - } - - manager.Insert(agent); - - } - - public void UpdateUserAgent(UserAgentData agent) - { - m_log.InfoFormat("[NHIBERNATE] UpdateUserAgent: {0} ", agent.ProfileID); - manager.Update(agent); - } - - override public UserAgentData GetAgentByUUID(UUID uuid) - { - m_log.InfoFormat("[NHIBERNATE] GetAgentByUUID: {0} ", uuid); - return (UserAgentData)manager.Get(typeof(UserAgentData), uuid); - } - - override public UserProfileData GetUserByName(string fname, string lname) - { - m_log.InfoFormat("[NHIBERNATE] GetUserByName: {0} {1} ", fname, lname); - ICriteria criteria = manager.GetSession().CreateCriteria(typeof(UserProfileData)); - criteria.Add(Expression.Eq("FirstName", fname)); - criteria.Add(Expression.Eq("SurName", lname)); - foreach (UserProfileData profile in criteria.List()) - { - profile.CurrentAgent = GetAgentByUUID(profile.ID); - return profile; - } - return null; - } - - override public UserAgentData GetAgentByName(string fname, string lname) - { - return GetUserByName(fname, lname).CurrentAgent; - } - - override public UserAgentData GetAgentByName(string name) - { - return GetAgentByName(name.Split(' ')[0], name.Split(' ')[1]); - } - - override public List GeneratePickerResults(UUID queryID, string query) - { - List results = new List(); - string[] querysplit; - querysplit = query.Split(' '); - - if (querysplit.Length == 2) - { - ICriteria criteria = manager.GetSession().CreateCriteria(typeof(UserProfileData)); - criteria.Add(Expression.Like("FirstName", querysplit[0])); - criteria.Add(Expression.Like("SurName", querysplit[1])); - foreach (UserProfileData profile in criteria.List()) - { - AvatarPickerAvatar user = new AvatarPickerAvatar(); - user.AvatarID = profile.ID; - user.firstName = profile.FirstName; - user.lastName = profile.SurName; - results.Add(user); - } - } - return results; - } - - // TODO: actually implement these - public override void StoreWebLoginKey(UUID agentID, UUID webLoginKey) - { - UserProfileData user=GetUserByUUID(agentID); - user.WebLoginKey = webLoginKey; - UpdateUserProfile(user); - return; - } - - public override void AddNewUserFriend(UUID ownerId, UUID friendId, uint perms) - { - if (!FriendRelationExists(ownerId,friendId)) - { - manager.Insert(new UserFriend(UUID.Random(), ownerId, friendId, perms)); - } - if (!FriendRelationExists(friendId, ownerId)) - { - manager.Insert(new UserFriend(UUID.Random(), friendId, ownerId, perms)); - } - return; - } - - private bool FriendRelationExists(UUID ownerId, UUID friendId) - { - using (ISession session = manager.GetSession()) - { - ICriteria criteria = session.CreateCriteria(typeof(UserFriend)); - criteria.Add(Expression.Eq("OwnerID", ownerId)); - criteria.Add(Expression.Eq("FriendID", friendId)); - return criteria.List().Count > 0; - } - } - - public override void RemoveUserFriend(UUID ownerId, UUID friendId) - { - using (ISession session = manager.GetSession()) - { - using (ITransaction transaction = session.BeginTransaction()) - { - - { - ICriteria criteria = session.CreateCriteria(typeof(UserFriend)); - criteria.Add(Expression.Eq("OwnerID", ownerId)); - criteria.Add(Expression.Eq("FriendID", friendId)); - - foreach (UserFriend userFriend in criteria.List()) - { - session.Delete(userFriend); - } - } - - { - ICriteria criteria = session.CreateCriteria(typeof(UserFriend)); - criteria.Add(Expression.Eq("OwnerID", friendId)); - criteria.Add(Expression.Eq("FriendID", ownerId)); - - foreach (UserFriend userFriend in criteria.List()) - { - session.Delete(userFriend); - } - } - - transaction.Commit(); - } - } - return; - } - - - public override void UpdateUserFriendPerms(UUID ownerId, UUID friendId, uint perms) - { - using (ISession session = manager.GetSession()) - { - using (ITransaction transaction = session.BeginTransaction()) - { - { - ICriteria criteria = session.CreateCriteria(typeof(UserFriend)); - criteria.Add(Expression.Eq("OwnerID", ownerId)); - criteria.Add(Expression.Eq("FriendID", friendId)); - - foreach (UserFriend userFriend in criteria.List()) - { - userFriend.FriendPermissions = perms; - session.Update(userFriend); - } - } - transaction.Commit(); - } - } - return; - } - - public override List GetUserFriendList(UUID ownerId) - { - List friendList=new List(); - Dictionary friendListItemDictionary = new Dictionary(); - - using (ISession session = manager.GetSession()) - { - ICriteria criteria = session.CreateCriteria(typeof(UserFriend)); - criteria.Add(Expression.Or( - Expression.Eq("OwnerID", ownerId), - Expression.Eq("FriendID", ownerId) - )); - - foreach (UserFriend userFriend in criteria.List()) - { - if (userFriend.OwnerID == ownerId) - { - FriendListItem friendListItem = new FriendListItem(); - friendListItem.FriendListOwner = userFriend.OwnerID; - friendListItem.Friend = userFriend.FriendID; - friendListItem.FriendPerms = userFriend.FriendPermissions; - friendListItemDictionary.Add(userFriend.FriendID, friendListItem); - friendList.Add(friendListItem); - } - } - - // Reading permissions to other direction - foreach (UserFriend userFriend in criteria.List()) - { - if (userFriend.FriendID == ownerId) - { - //Ignore if there is no reverse relation existing. - //if (friendListItemDictionary.ContainsKey(userFriend.OwnerID)) - { - FriendListItem friendListItem = friendListItemDictionary[userFriend.OwnerID]; - friendListItem.FriendListOwnerPerms = userFriend.FriendPermissions; - } - } - } - - } - - return friendList; - } - - - public override Dictionary GetFriendRegionInfos (List friendsIds) - { - Dictionary friendRegionInfos=new Dictionary(); - - foreach (UUID friendId in friendsIds) - { - UserAgentData agent=GetAgentByUUID(friendId); - if (agent != null) - { - FriendRegionInfo fri = new FriendRegionInfo(); - fri.isOnline = agent.AgentOnline; - fri.regionHandle = agent.Handle; - - friendRegionInfos[friendId] = fri; - } - } - - return friendRegionInfos; - } - - public override bool MoneyTransferRequest(UUID from, UUID to, uint amount) { return true; } - public override bool InventoryTransferRequest(UUID from, UUID to, UUID inventory) { return true; } - - /// Appearance - /// TODO: stubs for now to get us to a compiling state gently - public override AvatarAppearance GetUserAppearance(UUID user) - { - return (AvatarAppearance)manager.Get(typeof(AvatarAppearance), user); - } - - private bool ExistsAppearance(UUID uuid) - { - AvatarAppearance appearance = (AvatarAppearance)manager.Get(typeof(AvatarAppearance), uuid); - if (appearance == null) - { - return false; - } - - return true; - } - - - public override void UpdateUserAppearance(UUID user, AvatarAppearance appearance) - { - if (appearance == null) - return; - - appearance.Owner = user; - - bool exists = ExistsAppearance(user); - if (exists) - { - manager.Update(appearance); - } - else - { - manager.Insert(appearance); - } - } - - public override void ResetAttachments(UUID userID) - { - } - - public override void LogoutUsers(UUID regionID) - { - } - - public override string Name { - get { return "NHibernate"; } - } - - public override string Version { - get { return "0.1"; } - } - - public override void Dispose() - { - - } - } -} diff --git a/OpenSim/Data/NHibernate/Resources/AssetBase.hbm.xml b/OpenSim/Data/NHibernate/Resources/AssetBase.hbm.xml deleted file mode 100644 index cb8b36d10e..0000000000 --- a/OpenSim/Data/NHibernate/Resources/AssetBase.hbm.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/Resources/EstateRegionLink.hbm.xml b/OpenSim/Data/NHibernate/Resources/EstateRegionLink.hbm.xml deleted file mode 100644 index fd669100e2..0000000000 --- a/OpenSim/Data/NHibernate/Resources/EstateRegionLink.hbm.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/Resources/EstateSettings.hbm.xml b/OpenSim/Data/NHibernate/Resources/EstateSettings.hbm.xml deleted file mode 100644 index d300b93d59..0000000000 --- a/OpenSim/Data/NHibernate/Resources/EstateSettings.hbm.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/Resources/InventoryFolderBase.hbm.xml b/OpenSim/Data/NHibernate/Resources/InventoryFolderBase.hbm.xml deleted file mode 100644 index c5f0115f33..0000000000 --- a/OpenSim/Data/NHibernate/Resources/InventoryFolderBase.hbm.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/Resources/InventoryItemBase.hbm.xml b/OpenSim/Data/NHibernate/Resources/InventoryItemBase.hbm.xml deleted file mode 100644 index ea6032a24b..0000000000 --- a/OpenSim/Data/NHibernate/Resources/InventoryItemBase.hbm.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/Resources/MigrationSyntaxDifferences.txt b/OpenSim/Data/NHibernate/Resources/MigrationSyntaxDifferences.txt deleted file mode 100644 index 1c8951604f..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MigrationSyntaxDifferences.txt +++ /dev/null @@ -1,14 +0,0 @@ -?This file describes the differences in schema creation and migration scripts. - -MySQL is used as reference script against which differences are listed. - -Generally MySQL create table options should be removed for other databases. - -_PostgreSQL_ -* DOUBLE->DOUBLE PRECISION -* BIT->BOOLEAN - -_MsSql_ -* VARCHAR->NVARCHAR -* Remove DEFAULT-keywords -* DOUBLE->REAL diff --git a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_AssetStore.sql b/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_AssetStore.sql deleted file mode 100644 index deee78b85c..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_AssetStore.sql +++ /dev/null @@ -1,10 +0,0 @@ -create table Assets ( - ID NVARCHAR(36) not null, - Type SMALLINT null, - Name NVARCHAR(64) null, - Description NVARCHAR(64) null, - Local BIT null, - Temporary BIT null, - Data VARBINARY(max) null, - primary key (ID) -) diff --git a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_EstateStore.sql b/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_EstateStore.sql deleted file mode 100644 index dd579f9882..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_EstateStore.sql +++ /dev/null @@ -1,72 +0,0 @@ -CREATE TABLE EstateSettings ( - EstateID INT NOT NULL, - ParentEstateID INT NULL, - EstateOwnerID NVARCHAR(36) NULL, - Name NVARCHAR(64) NULL, - RedirectGridX INT NULL, - RedirectGridY INT NULL, - BillableFactor REAL NULL, - PricePerMeter INT NULL, - SunPosition FLOAT NULL, - - UseGlobalTime BIT NULL, - FixedSun BIT NULL, - AllowVoice BIT NULL, - AllowDirectTeleport BIT NULL, - ResetHomeOnTeleport BIT NULL, - PublicAccess BIT NULL, - DenyAnonymous BIT NULL, - DenyIdentified BIT NULL, - DenyTransacted BIT NULL, - DenyMinors BIT NULL, - BlockDwell BIT NULL, - EstateSkipScripts BIT NULL, - TaxFree BIT NULL, - AbuseEmailToEstateOwner BIT NULL, - - AbuseEmail NVARCHAR(255) NULL, - - PRIMARY KEY (EstateID) -); - -CREATE TABLE EstateRegionLink ( - EstateRegionLinkID NVARCHAR(36) NOT NULL, - EstateID INT NULL, - RegionID NVARCHAR(36) NULL, - PRIMARY KEY (EstateRegionLinkID) -); - -CREATE INDEX EstateRegionLinkEstateIDIndex ON EstateRegionLink (EstateID); -CREATE INDEX EstateRegionLinkERegionIDIndex ON EstateRegionLink (RegionID); - - -CREATE TABLE EstateManagers ( - EstateID INT NOT NULL, - ManagerID NVARCHAR(36) NOT NULL, - ArrayIndex INT NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -); - -CREATE TABLE EstateUsers ( - EstateID INT NOT NULL, - UserID NVARCHAR(36) NOT NULL, - ArrayIndex INT NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -); - -CREATE TABLE EstateGroups ( - EstateID INT NOT NULL, - GroupID NVARCHAR(36) NOT NULL, - ArrayIndex INT NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -); - -CREATE TABLE EstateBans ( - EstateID INT NOT NULL, - ArrayIndex INT NOT NULL, - BannedUserID NVARCHAR(36) NOT NULL, - BannedHostAddress NVARCHAR(16) NOT NULL, - BannedHostIPMask NVARCHAR(16) NOT NULL, - BannedHostNameMask NVARCHAR(16) NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -); \ No newline at end of file diff --git a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_GridStore.sql b/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_GridStore.sql deleted file mode 100644 index e4ad525ae7..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_GridStore.sql +++ /dev/null @@ -1,35 +0,0 @@ -create table Regions ( - Uuid NVARCHAR(36) not null, - RegionHandle BIGINT null, - RegionName NVARCHAR(32) null, - RegionRecvKey NVARCHAR(128) null, - RegionSendKey NVARCHAR(128) null, - RegionSecret NVARCHAR(128) null, - RegionDataURI NVARCHAR(255) null, - ServerIP NVARCHAR(64) null, - ServerPort INT null, - ServerURI NVARCHAR(255) null, - RegionLocX INT null, - RegionLocY INT null, - RegionLocZ INT null, - EastOverrideHandle BIGINT null, - WestOverrideHandle BIGINT null, - SouthOverrideHandle BIGINT null, - NorthOverrideHandle BIGINT null, - RegionAssetURI NVARCHAR(255) null, - RegionAssetRecvKey NVARCHAR(128) null, - RegionAssetSendKey NVARCHAR(128) null, - RegionUserURI NVARCHAR(255) null, - RegionUserRecvKey NVARCHAR(128) null, - RegionUserSendKey NVARCHAR(128) null, - ServerHttpPort INT null, - ServerRemotingPort INT null, - RegionMapTextureID NVARCHAR(36) null, - Owner_uuid NVARCHAR(36) null, - OriginUUID NVARCHAR(36) null, - primary key (Uuid) -) -create index region_handle on Regions (RegionHandle) -create index region_name on Regions (RegionName) -create index overrideHandles on Regions (EastOverrideHandle, WestOverrideHandle, SouthOverrideHandle, NorthOverrideHandle) - diff --git a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_InventoryStore.sql b/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_InventoryStore.sql deleted file mode 100644 index 82936c47b3..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_InventoryStore.sql +++ /dev/null @@ -1,36 +0,0 @@ -create table InventoryFolders ( - ID NVARCHAR(255) not null, - Type SMALLINT null, - Version INT null, - ParentID NVARCHAR(255) null, - Owner NVARCHAR(255) null, - Name NVARCHAR(64) null, - primary key (ID) -) -create table InventoryItems ( - ID NVARCHAR(255) not null, - InvType INT null, - AssetType INT null, - AssetID NVARCHAR(255) null, - Folder NVARCHAR(255) null, - Owner NVARCHAR(255) null, - Creator NVARCHAR(255) null, - Name NVARCHAR(64) null, - Description NVARCHAR(64) null, - NextPermissions INT null, - CurrentPermissions INT null, - BasePermissions INT null, - EveryOnePermissions INT null, - GroupID NVARCHAR(255) null, - GroupOwned BIT null, - SalePrice INT null, - SaleType TINYINT null, - Flags INT null, - CreationDate INT null, - primary key (ID) -) -create index item_group_id on InventoryItems (GroupID) -create index item_folder_id on InventoryItems (Folder) -create index item_owner_id on InventoryItems (Owner) -create index folder_owner_id on InventoryFolders (Owner) -create index folder_parent_id on InventoryFolders (ParentID) diff --git a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_RegionStore.sql b/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_RegionStore.sql deleted file mode 100644 index 181a74c86e..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_RegionStore.sql +++ /dev/null @@ -1,104 +0,0 @@ -create table Prims ( - UUID NVARCHAR(255) not null, - ParentID INT null, - ParentUUID NVARCHAR(255) null, - RegionID NVARCHAR(255) null, - CreationDate INT null, - Name NVARCHAR(255) null, - Text NVARCHAR(255) null, - Description NVARCHAR(255) null, - SitName NVARCHAR(255) null, - TouchName NVARCHAR(255) null, - ObjectFlags INT null, - CreatorID NVARCHAR(255) null, - OwnerID NVARCHAR(255) null, - GroupID NVARCHAR(255) null, - LastOwnerID NVARCHAR(255) null, - OwnerMask INT null, - NextOwnerMask INT null, - GroupMask INT null, - EveryoneMask INT null, - BaseMask INT null, - PositionX REAL null, - PositionY REAL null, - PositionZ REAL null, - GroupPositionX REAL null, - GroupPositionY REAL null, - GroupPositionZ REAL null, - VelocityX REAL null, - VelocityY REAL null, - VelocityZ REAL null, - AngularVelocityX REAL null, - AngularVelocityY REAL null, - AngularVelocityZ REAL null, - AccelerationX REAL null, - AccelerationY REAL null, - AccelerationZ REAL null, - SitTargetOffsetX REAL null, - SitTargetOffsetY REAL null, - SitTargetOffsetZ REAL null, - RotationX REAL null, - RotationY REAL null, - RotationZ REAL null, - RotationW REAL null, - SitTargetOrientX REAL null, - SitTargetOrientY REAL null, - SitTargetOrientZ REAL null, - SitTargetOrientW REAL null, - ScaleX REAL null, - ScaleY REAL null, - ScaleZ REAL null, - PCode TINYINT null, - PathBegin INT null, - PathEnd INT null, - PathScaleX TINYINT null, - PathScaleY TINYINT null, - PathShearX TINYINT null, - PathShearY TINYINT null, - PathSkew TINYINT null, - PathCurve TINYINT null, - PathRadiusOffset TINYINT null, - PathRevolutions TINYINT null, - PathTaperX TINYINT null, - PathTaperY TINYINT null, - PathTwist TINYINT null, - ProfileBegin INT null, - ProfileEnd INT null, - ProfileCurve TINYINT null, - ProfileHollow INT null, - Texture VARBINARY(8000) null, - ExtraParams VARBINARY(8000) null, - State TINYINT null, - primary key (UUID) -) - -create table PrimItems ( - ItemID NVARCHAR(255) not null, - PrimID NVARCHAR(255) null, - AssetID NVARCHAR(255) null, - ParentFolderID NVARCHAR(255) null, - CreatorID NVARCHAR(255) null, - OwnerID NVARCHAR(255) null, - GroupID NVARCHAR(255) null, - LastOwnerID NVARCHAR(255) null, - CurrentPermissions INT null, - BasePermissions INT null, - EveryonePermissions INT null, - GroupPermissions INT null, - NextPermissions INT null, - Name NVARCHAR(255) null, - Description NVARCHAR(255) null, - CreationDate INT null, - Flags INT null, - Type INT null, - InvType INT null, - primary key (ItemID) -) - -create table Terrain ( - RegionID NVARCHAR(255) not null, - MapData VARBINARY(max) null, - primary key (RegionID) -) - - diff --git a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_UserStore.sql b/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_UserStore.sql deleted file mode 100644 index c9c6c89a6c..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_UserStore.sql +++ /dev/null @@ -1,77 +0,0 @@ -create table UserAgents ( - ProfileID NVARCHAR(255) not null, - AgentIP NVARCHAR(24) null, - AgentPort INT null, - AgentOnline BIT null, - SessionID NVARCHAR(255) null, - SecureSessionID NVARCHAR(255) null, - InitialRegion NVARCHAR(255) null, - Region NVARCHAR(255) null, - LoginTime INT null, - LogoutTime INT null, - Handle BIGINT null, - primary key (ProfileID) -) - -create table UserProfiles ( - ID NVARCHAR(255) not null, - FirstName NVARCHAR(32) null, - SurName NVARCHAR(32) null, - PasswordHash NVARCHAR(32) null, - PasswordSalt NVARCHAR(32) null, - WebLoginKey NVARCHAR(255) null, - HomeRegionX INT null, - HomeRegionY INT null, - HomeLocationX REAL null, - HomeLocationY REAL null, - HomeLocationZ REAL null, - HomeLookAtX REAL null, - HomeLookAtY REAL null, - HomeLookAtZ REAL null, - Created INT null, - LastLogin INT null, - RootInventoryFolderID NVARCHAR(255) null, - UserInventoryURI NVARCHAR(255) null, - UserAssetURI NVARCHAR(255) null, - Image NVARCHAR(255) null, - FirstLifeImage NVARCHAR(255) null, - AboutText NVARCHAR(255) null, - FirstLifeAboutText NVARCHAR(255) null, - primary key (ID) -) -create table UserAppearances ( - Owner NVARCHAR(255) not null, - BodyItem NVARCHAR(255) null, - BodyAsset NVARCHAR(255) null, - SkinItem NVARCHAR(255) null, - SkinAsset NVARCHAR(255) null, - HairItem NVARCHAR(255) null, - HairAsset NVARCHAR(255) null, - EyesItem NVARCHAR(255) null, - EyesAsset NVARCHAR(255) null, - ShirtItem NVARCHAR(255) null, - ShirtAsset NVARCHAR(255) null, - PantsItem NVARCHAR(255) null, - PantsAsset NVARCHAR(255) null, - ShoesItem NVARCHAR(255) null, - ShoesAsset NVARCHAR(255) null, - SocksItem NVARCHAR(255) null, - SocksAsset NVARCHAR(255) null, - JacketItem NVARCHAR(255) null, - JacketAsset NVARCHAR(255) null, - GlovesItem NVARCHAR(255) null, - GlovesAsset NVARCHAR(255) null, - UnderShirtItem NVARCHAR(255) null, - UnderShirtAsset NVARCHAR(255) null, - UnderPantsItem NVARCHAR(255) null, - UnderPantsAsset NVARCHAR(255) null, - SkirtItem NVARCHAR(255) null, - SkirtAsset NVARCHAR(255) null, - Texture VARBINARY(8000) null, - VisualParams VARBINARY(8000) null, - Serial INT null, - primary key (Owner) -) - -create index user_surname on UserProfiles (SurName) -create index user_firstname on UserProfiles (FirstName) diff --git a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/002_RegionStore.sql b/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/002_RegionStore.sql deleted file mode 100644 index dfaac6e78e..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/002_RegionStore.sql +++ /dev/null @@ -1,51 +0,0 @@ -ALTER TABLE Prims ADD LinkNum INT null; -ALTER TABLE Prims ADD Material TINYINT null; -ALTER TABLE Prims ADD ScriptAccessPin INT null; -ALTER TABLE Prims ADD TextureAnimation VARBINARY(max) null; -ALTER TABLE Prims ADD ParticleSystem VARBINARY(max) null; -ALTER TABLE Prims ADD ClickAction TINYINT null; -ALTER TABLE Prims ADD Color INT null; - -CREATE TABLE RegionSettings -( - RegionID NVARCHAR(255) NOT NULL, - BlockTerraform bit NOT NULL, - BlockFly bit NOT NULL, - AllowDamage bit NOT NULL, - RestrictPushing bit NOT NULL, - AllowLandResell bit NOT NULL, - AllowLandJoinDivide bit NOT NULL, - BlockShowInSearch bit NOT NULL, - AgentLimit int NOT NULL, - ObjectBonus float(53) NOT NULL, - Maturity int NOT NULL, - DisableScripts bit NOT NULL, - DisableCollisions bit NOT NULL, - DisablePhysics bit NOT NULL, - TerrainTexture1 NVARCHAR(36) NOT NULL, - TerrainTexture2 NVARCHAR(36) NOT NULL, - TerrainTexture3 NVARCHAR(36) NOT NULL, - TerrainTexture4 NVARCHAR(36) NOT NULL, - Elevation1NW float(53) NOT NULL, - Elevation2NW float(53) NOT NULL, - Elevation1NE float(53) NOT NULL, - Elevation2NE float(53) NOT NULL, - Elevation1SE float(53) NOT NULL, - Elevation2SE float(53) NOT NULL, - Elevation1SW float(53) NOT NULL, - Elevation2SW float(53) NOT NULL, - WaterHeight float(53) NOT NULL, - TerrainRaiseLimit float(53) NOT NULL, - TerrainLowerLimit float(53) NOT NULL, - UseEstateSun bit NOT NULL, - FixedSun bit NOT NULL, - SunPosition float(53) NOT NULL, - Covenant NVARCHAR(36) NULL DEFAULT (NULL), - Sandbox bit NOT NULL, - SunVectorX float(53) NOT NULL DEFAULT ((0)), - SunVectorY float(53) NOT NULL DEFAULT ((0)), - SunVectorZ float(53) NOT NULL DEFAULT ((0)), - - primary key (RegionID) -) - diff --git a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/002_UserStore.sql b/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/002_UserStore.sql deleted file mode 100644 index f5e0c00a1b..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/002_UserStore.sql +++ /dev/null @@ -1,27 +0,0 @@ -ALTER TABLE UserAgents ADD PositionX REAL null; -ALTER TABLE UserAgents ADD PositionY REAL null; -ALTER TABLE UserAgents ADD PositionZ REAL null; -ALTER TABLE UserAgents ADD LookAtX REAL null; -ALTER TABLE UserAgents ADD LookAtY REAL null; -ALTER TABLE UserAgents ADD LookAtZ REAL null; - -ALTER TABLE UserProfiles ADD Email NVARCHAR(250) null; -ALTER TABLE UserProfiles ADD HomeRegionID NVARCHAR(36) null; -ALTER TABLE UserProfiles ADD CanDoMask INT null; -ALTER TABLE UserProfiles ADD WantDoMask INT null; -ALTER TABLE UserProfiles ADD UserFlags INT null; -ALTER TABLE UserProfiles ADD GodLevel INT null; -ALTER TABLE UserProfiles ADD CustomType NVARCHAR(32) null; -ALTER TABLE UserProfiles ADD Partner NVARCHAR(36) null; - -ALTER TABLE UserAppearances ADD AvatarHeight FLOAT null; - -CREATE TABLE UserFriends ( - UserFriendID NVARCHAR(36) NOT NULL, - OwnerID NVARCHAR(36) NULL, - FriendID NVARCHAR(36) NULL, - FriendPermissions INT NULL, - PRIMARY KEY (UserFriendID) -); - -CREATE INDEX UserFriendsOwnerIdFriendIdIndex ON UserFriends (OwnerID,FriendID); diff --git a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_AssetStore.sql b/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_AssetStore.sql deleted file mode 100644 index cd0958d5f8..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_AssetStore.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE Assets ( - ID VARCHAR(36) NOT NULL, - Type SMALLINT DEFAULT NULL, - Name VARCHAR(64) DEFAULT NULL, - Description VARCHAR(64) DEFAULT NULL, - Local BIT DEFAULT NULL, - Temporary BIT DEFAULT NULL, - Data LONGBLOB, - PRIMARY KEY (ID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; diff --git a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_EstateStore.sql b/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_EstateStore.sql deleted file mode 100644 index e9ae07e990..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_EstateStore.sql +++ /dev/null @@ -1,71 +0,0 @@ -CREATE TABLE EstateSettings ( - EstateID INT NOT NULL, - ParentEstateID INT DEFAULT NULL, - EstateOwnerID VARCHAR(36) DEFAULT NULL, - Name VARCHAR(64) DEFAULT NULL, - RedirectGridX INT DEFAULT NULL, - RedirectGridY INT DEFAULT NULL, - BillableFactor DOUBLE DEFAULT NULL, - PricePerMeter INT DEFAULT NULL, - SunPosition DOUBLE DEFAULT NULL, - - UseGlobalTime BIT DEFAULT NULL, - FixedSun BIT DEFAULT NULL, - AllowVoice BIT DEFAULT NULL, - AllowDirectTeleport BIT DEFAULT NULL, - ResetHomeOnTeleport BIT DEFAULT NULL, - PublicAccess BIT DEFAULT NULL, - DenyAnonymous BIT DEFAULT NULL, - DenyIdentified BIT DEFAULT NULL, - DenyTransacted BIT DEFAULT NULL, - DenyMinors BIT DEFAULT NULL, - BlockDwell BIT DEFAULT NULL, - EstateSkipScripts BIT DEFAULT NULL, - TaxFree BIT DEFAULT NULL, - AbuseEmailToEstateOwner BIT DEFAULT NULL, - - AbuseEmail VARCHAR(255) DEFAULT NULL, - - PRIMARY KEY (EstateID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE TABLE EstateRegionLink ( - EstateRegionLinkID VARCHAR(36) NOT NULL, - EstateID INT DEFAULT NULL, - RegionID VARCHAR(36) DEFAULT NULL, - PRIMARY KEY (EstateRegionLinkID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE INDEX EstateRegionLinkEstateIDIndex ON EstateRegionLink (EstateID); -CREATE INDEX EstateRegionLinkERegionIDIndex ON EstateRegionLink (RegionID); - -CREATE TABLE EstateManagers ( - EstateID INT NOT NULL, - ManagerID VARCHAR(36) NOT NULL, - ArrayIndex INT NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE TABLE EstateUsers ( - EstateID INT NOT NULL, - UserID VARCHAR(36) NOT NULL, - ArrayIndex INT NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE TABLE EstateGroups ( - EstateID INT NOT NULL, - GroupID VARCHAR(36) NOT NULL, - ArrayIndex INT NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE TABLE EstateBans ( - EstateID INT NOT NULL, - ArrayIndex INT NOT NULL, - BannedUserID VARCHAR(36) NOT NULL, - BannedHostAddress VARCHAR(16) NOT NULL, - BannedHostIPMask VARCHAR(16) NOT NULL, - BannedHostNameMask VARCHAR(16) NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; diff --git a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_GridStore.sql b/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_GridStore.sql deleted file mode 100644 index c6fe620ab7..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_GridStore.sql +++ /dev/null @@ -1,35 +0,0 @@ -CREATE TABLE Regions ( - RegionID VARCHAR(36) NOT NULL, - OwnerID VARCHAR(36) DEFAULT NULL, - OriginID VARCHAR(36) DEFAULT NULL, - RegionHandle BIGINT DEFAULT NULL, - RegionName VARCHAR(32) DEFAULT NULL, - RegionRecvKey VARCHAR(128) DEFAULT NULL, - RegionSendKey VARCHAR(128) DEFAULT NULL, - RegionSecret VARCHAR(128) DEFAULT NULL, - RegionDataURI VARCHAR(255) DEFAULT NULL, - ServerIP VARCHAR(64) DEFAULT NULL, - ServerPort INT DEFAULT NULL, - ServerURI VARCHAR(255) DEFAULT NULL, - RegionLocX INT DEFAULT NULL, - RegionLocY INT DEFAULT NULL, - RegionLocZ INT DEFAULT NULL, - EastOverrideHandle BIGINT DEFAULT NULL, - WestOverrideHandle BIGINT DEFAULT NULL, - SouthOverrideHandle BIGINT DEFAULT NULL, - NorthOverrideHandle BIGINT DEFAULT NULL, - RegionAssetURI VARCHAR(255) DEFAULT NULL, - RegionAssetRecvKey VARCHAR(128) DEFAULT NULL, - RegionAssetSendKey VARCHAR(128) DEFAULT NULL, - RegionUserURI VARCHAR(255) DEFAULT NULL, - RegionUserRecvKey VARCHAR(128) DEFAULT NULL, - RegionUserSendKey VARCHAR(128) DEFAULT NULL, - RegionMapTextureId VARCHAR(36) DEFAULT NULL, - ServerHttpPort INT DEFAULT NULL, - ServerRemotingPort INT DEFAULT NULL, - PRIMARY KEY (RegionID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE INDEX RegionNameIndex ON Regions (RegionName); -CREATE INDEX RegionHandleIndex ON Regions (RegionHandle); -CREATE INDEX RegionHandlesIndex ON Regions (EastOverrideHandle,WestOverrideHandle,SouthOverrideHandle,NorthOverrideHandle); diff --git a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_InventoryStore.sql b/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_InventoryStore.sql deleted file mode 100644 index 93d282bcff..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_InventoryStore.sql +++ /dev/null @@ -1,39 +0,0 @@ -CREATE TABLE InventoryFolders ( - ID VARCHAR(36) NOT NULL, - Type SMALLINT DEFAULT NULL, - Version INT DEFAULT NULL, - ParentID VARCHAR(36) DEFAULT NULL, - Owner VARCHAR(36) DEFAULT NULL, - Name VARCHAR(64) DEFAULT NULL, - PRIMARY KEY (ID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE INDEX InventoryFoldersOwnerIdIndex ON InventoryFolders (Owner); -CREATE INDEX InventoryFoldersParentIdIndex ON InventoryFolders (ParentID); - -CREATE TABLE InventoryItems ( - ID VARCHAR(36) NOT NULL, - InvType INT DEFAULT NULL, - AssetType INT DEFAULT NULL, - AssetID VARCHAR(36) DEFAULT NULL, - Folder VARCHAR(36) DEFAULT NULL, - Owner VARCHAR(36) DEFAULT NULL, - Creator VARCHAR(36) DEFAULT NULL, - Name VARCHAR(64) DEFAULT NULL, - Description VARCHAR(64) DEFAULT NULL, - NextPermissions INT DEFAULT NULL, - CurrentPermissions INT DEFAULT NULL, - BasePermissions INT DEFAULT NULL, - EveryOnePermissions INT DEFAULT NULL, - GroupID VARCHAR(36) DEFAULT NULL, - GroupOwned BIT DEFAULT NULL, - SalePrice INT DEFAULT NULL, - SaleType TINYINT DEFAULT NULL, - Flags INT DEFAULT NULL, - CreationDate INT DEFAULT NULL, - PRIMARY KEY (ID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE INDEX InventoryItemsGroupIdIndex ON InventoryItems (GroupID); -CREATE INDEX InventoryItemsOwnerIdIndex ON InventoryItems (Owner); -CREATE INDEX InventoryItemsFolderIdIndex ON InventoryItems (Folder); diff --git a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_RegionStore.sql b/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_RegionStore.sql deleted file mode 100644 index eb1d8fe439..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_RegionStore.sql +++ /dev/null @@ -1,169 +0,0 @@ -CREATE TABLE Prims ( - UUID VARCHAR(36) NOT NULL, - RegionID VARCHAR(36) DEFAULT NULL, - GroupID VARCHAR(36) DEFAULT NULL, - ParentID INT DEFAULT NULL, - ParentUUID VARCHAR(36) DEFAULT NULL, - OwnerID VARCHAR(36) DEFAULT NULL, - LastOwnerID VARCHAR(36) DEFAULT NULL, - CreatorID VARCHAR(36) DEFAULT NULL, - CreationDate INT DEFAULT NULL, - LinkNum INT DEFAULT NULL, - Name VARCHAR(255) DEFAULT NULL, - Text VARCHAR(255) DEFAULT NULL, - Description VARCHAR(255) DEFAULT NULL, - SitName VARCHAR(255) DEFAULT NULL, - TouchName VARCHAR(255) DEFAULT NULL, - ObjectFlags INT DEFAULT NULL, - OwnerMask INT DEFAULT NULL, - NextOwnerMask INT DEFAULT NULL, - GroupMask INT DEFAULT NULL, - EveryoneMask INT DEFAULT NULL, - BaseMask INT DEFAULT NULL, - Material TINYINT DEFAULT NULL, - ScriptAccessPin INT DEFAULT NULL, - TextureAnimation BLOB, - ParticleSystem BLOB, - ClickAction TINYINT DEFAULT NULL, - Color INT DEFAULT NULL, - PositionX DOUBLE DEFAULT NULL, - PositionY DOUBLE DEFAULT NULL, - PositionZ DOUBLE DEFAULT NULL, - GroupPositionX DOUBLE DEFAULT NULL, - GroupPositionY DOUBLE DEFAULT NULL, - GroupPositionZ DOUBLE DEFAULT NULL, - VelocityX DOUBLE DEFAULT NULL, - VelocityY DOUBLE DEFAULT NULL, - VelocityZ DOUBLE DEFAULT NULL, - AngularVelocityX DOUBLE DEFAULT NULL, - AngularVelocityY DOUBLE DEFAULT NULL, - AngularVelocityZ DOUBLE DEFAULT NULL, - AccelerationX DOUBLE DEFAULT NULL, - AccelerationY DOUBLE DEFAULT NULL, - AccelerationZ DOUBLE DEFAULT NULL, - RotationX DOUBLE DEFAULT NULL, - RotationY DOUBLE DEFAULT NULL, - RotationZ DOUBLE DEFAULT NULL, - RotationW DOUBLE DEFAULT NULL, - SitTargetOffsetX DOUBLE DEFAULT NULL, - SitTargetOffsetY DOUBLE DEFAULT NULL, - SitTargetOffsetZ DOUBLE DEFAULT NULL, - SitTargetOrientW DOUBLE DEFAULT NULL, - SitTargetOrientX DOUBLE DEFAULT NULL, - SitTargetOrientY DOUBLE DEFAULT NULL, - SitTargetOrientZ DOUBLE DEFAULT NULL, - -- this is the shape - Shape INT DEFAULT NULL, - ScaleX DOUBLE DEFAULT NULL, - ScaleY DOUBLE DEFAULT NULL, - ScaleZ DOUBLE DEFAULT NULL, - PCode INT DEFAULT NULL, - PathBegin INT DEFAULT NULL, - PathEnd INT DEFAULT NULL, - PathScaleX INT DEFAULT NULL, - PathScaleY INT DEFAULT NULL, - PathShearX INT DEFAULT NULL, - PathShearY INT DEFAULT NULL, - PathSkew INT DEFAULT NULL, - PathCurve INT DEFAULT NULL, - PathRadiusOffset INT DEFAULT NULL, - PathRevolutions INT DEFAULT NULL, - PathTaperX INT DEFAULT NULL, - PathTaperY INT DEFAULT NULL, - PathTwist INT DEFAULT NULL, - ProfileBegin INT DEFAULT NULL, - ProfileEnd INT DEFAULT NULL, - ProfileCurve INT DEFAULT NULL, - ProfileHollow INT DEFAULT NULL, - State INT DEFAULT NULL, - Texture LONGBLOB, - ExtraParams LONGBLOB, - PRIMARY KEY (UUID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE INDEX PrimsRegionIdIndex ON Prims (RegionID); -CREATE INDEX PrimsRegionParentUuidIndex ON Prims (ParentUUID); - -CREATE TABLE Terrain ( - RegionID VARCHAR(36) not null, - MapData LONGBLOB, - PRIMARY KEY (RegionID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE TABLE PrimItems ( - ItemID VARCHAR(36) NOT NULL, - GroupID VARCHAR(36) DEFAULT NULL, - PrimID VARCHAR(36) DEFAULT NULL, - ParentFolderID VARCHAR(36) DEFAULT NULL, - AssetID VARCHAR(36) DEFAULT NULL, - OwnerID VARCHAR(36) DEFAULT NULL, - LastOwnerID VARCHAR(36) DEFAULT NULL, - CreatorID VARCHAR(36) DEFAULT NULL, - CreationDate BIGINT DEFAULT NULL, - Type INT DEFAULT NULL, - InvType INT DEFAULT NULL, - Name VARCHAR(255) DEFAULT NULL, - Description VARCHAR(255) DEFAULT NULL, - NextPermissions INT DEFAULT NULL, - CurrentPermissions INT DEFAULT NULL, - BasePermissions INT DEFAULT NULL, - EveryonePermissions INT DEFAULT NULL, - GroupPermissions INT DEFAULT NULL, - Flags INT DEFAULT NULL, - PRIMARY KEY (ItemID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE INDEX PrimItemsPrimIdIndex ON PrimItems (PrimID); - -CREATE TABLE RegionSettings ( - RegionID VARCHAR(36) NOT NULL, - - BlockTerraform BIT DEFAULT NULL, - BlockFly BIT DEFAULT NULL, - AllowDamage BIT DEFAULT NULL, - RestrictPushing BIT DEFAULT NULL, - AllowLandResell BIT DEFAULT NULL, - AllowLandJoinDivide BIT DEFAULT NULL, - BlockShowInSearch BIT DEFAULT NULL, - - AgentLimit INT DEFAULT NULL, - ObjectBonus DOUBLE DEFAULT NULL, - Maturity INT DEFAULT NULL, - - DisableScripts BIT DEFAULT NULL, - DisableCollisions BIT DEFAULT NULL, - DisablePhysics BIT DEFAULT NULL, - - TerrainTexture1 VARCHAR(36) DEFAULT NULL, - TerrainTexture2 VARCHAR(36) DEFAULT NULL, - TerrainTexture3 VARCHAR(36) DEFAULT NULL, - TerrainTexture4 VARCHAR(36) DEFAULT NULL, - - Elevation1NW DOUBLE DEFAULT NULL, - Elevation2NW DOUBLE DEFAULT NULL, - Elevation1NE DOUBLE DEFAULT NULL, - Elevation2NE DOUBLE DEFAULT NULL, - Elevation1SE DOUBLE DEFAULT NULL, - Elevation2SE DOUBLE DEFAULT NULL, - Elevation1SW DOUBLE DEFAULT NULL, - Elevation2SW DOUBLE DEFAULT NULL, - - WaterHeight DOUBLE DEFAULT NULL, - TerrainRaiseLimit DOUBLE DEFAULT NULL, - TerrainLowerLimit DOUBLE DEFAULT NULL, - - UseEstateSun BIT DEFAULT NULL, - Sandbox BIT DEFAULT NULL, - - SunVectorX DOUBLE DEFAULT NULL, - SunVectorY DOUBLE DEFAULT NULL, - SunVectorZ DOUBLE DEFAULT NULL, - - FixedSun BIT DEFAULT NULL, - SunPosition DOUBLE DEFAULT NULL, - - Covenant VARCHAR(36) DEFAULT NULL, - - PRIMARY KEY (RegionID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - diff --git a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_UserStore.sql b/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_UserStore.sql deleted file mode 100644 index 140aea4187..0000000000 --- a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_UserStore.sql +++ /dev/null @@ -1,104 +0,0 @@ -CREATE TABLE UserAgents ( - ProfileID VARCHAR(36) NOT NULL, - AgentIP VARCHAR(24) DEFAULT NULL, - AgentPort INT DEFAULT NULL, - AgentOnline BIT DEFAULT NULL, - SessionID VARCHAR(36) DEFAULT NULL, - SecureSessionID VARCHAR(36) DEFAULT NULL, - InitialRegion VARCHAR(255) DEFAULT NULL, - Region VARCHAR(255) DEFAULT NULL, - LoginTime INT DEFAULT NULL, - LogoutTime INT DEFAULT NULL, - Handle BIGINT DEFAULT NULL, - PositionX DOUBLE DEFAULT NULL, - PositionY DOUBLE DEFAULT NULL, - PositionZ DOUBLE DEFAULT NULL, - LookAtX DOUBLE DEFAULT NULL, - LookAtY DOUBLE DEFAULT NULL, - LookAtZ DOUBLE DEFAULT NULL, - PRIMARY KEY (ProfileID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE TABLE UserProfiles ( - ID VARCHAR(36) NOT NULL, - WebLoginKey VARCHAR(36) DEFAULT NULL, - FirstName VARCHAR(32) DEFAULT NULL, - SurName VARCHAR(32) DEFAULT NULL, - Email VARCHAR(250) DEFAULT NULL, - PasswordHash VARCHAR(32) DEFAULT NULL, - PasswordSalt VARCHAR(32) DEFAULT NULL, - HomeRegionID VARCHAR(36) DEFAULT NULL, - HomeRegionX INT DEFAULT NULL, - HomeRegionY INT DEFAULT NULL, - HomeLocationX DOUBLE DEFAULT NULL, - HomeLocationY DOUBLE DEFAULT NULL, - HomeLocationZ DOUBLE DEFAULT NULL, - HomeLookAtX DOUBLE DEFAULT NULL, - HomeLookAtY DOUBLE DEFAULT NULL, - HomeLookAtZ DOUBLE DEFAULT NULL, - Created INT DEFAULT NULL, - LastLogin INT DEFAULT NULL, - UserInventoryURI VARCHAR(255) DEFAULT NULL, - UserAssetURI VARCHAR(255) DEFAULT NULL, - Image VARCHAR(36) DEFAULT NULL, - FirstLifeImage VARCHAR(36) DEFAULT NULL, - AboutText TEXT DEFAULT NULL, - FirstLifeAboutText TEXT DEFAULT NULL, - CanDoMask INT DEFAULT NULL, - WantDoMask INT DEFAULT NULL, - UserFlags INT DEFAULT NULL, - GodLevel INT DEFAULT NULL, - CustomType VARCHAR(32) DEFAULT NULL, - Partner VARCHAR(36) DEFAULT NULL, - RootInventoryFolderID VARCHAR(36) DEFAULT NULL, - PRIMARY KEY (ID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE INDEX UserSurnameIndex ON UserProfiles (SurName); -CREATE INDEX UserFirstNameIndex ON UserProfiles (FirstName); -CREATE UNIQUE INDEX UserFullNameIndex ON UserProfiles (SurName,FirstName); - -CREATE TABLE UserAppearances ( - Owner VARCHAR(36) NOT NULL, - BodyItem VARCHAR(36) DEFAULT NULL, - BodyAsset VARCHAR(36) DEFAULT NULL, - SkinItem VARCHAR(36) DEFAULT NULL, - SkinAsset VARCHAR(36) DEFAULT NULL, - HairItem VARCHAR(36) DEFAULT NULL, - HairAsset VARCHAR(36) DEFAULT NULL, - EyesItem VARCHAR(36) DEFAULT NULL, - EyesAsset VARCHAR(36) DEFAULT NULL, - ShirtItem VARCHAR(36) DEFAULT NULL, - ShirtAsset VARCHAR(36) DEFAULT NULL, - PantsItem VARCHAR(36) DEFAULT NULL, - PantsAsset VARCHAR(36) DEFAULT NULL, - ShoesItem VARCHAR(36) DEFAULT NULL, - ShoesAsset VARCHAR(36) DEFAULT NULL, - SocksItem VARCHAR(36) DEFAULT NULL, - SocksAsset VARCHAR(36) DEFAULT NULL, - JacketItem VARCHAR(36) DEFAULT NULL, - JacketAsset VARCHAR(36) DEFAULT NULL, - GlovesItem VARCHAR(36) DEFAULT NULL, - GlovesAsset VARCHAR(36) DEFAULT NULL, - UnderShirtItem VARCHAR(36) DEFAULT NULL, - UnderShirtAsset VARCHAR(36) DEFAULT NULL, - UnderPantsItem VARCHAR(36) DEFAULT NULL, - UnderPantsAsset VARCHAR(36) DEFAULT NULL, - SkirtItem VARCHAR(36) DEFAULT NULL, - SkirtAsset VARCHAR(36) DEFAULT NULL, - Texture LONGBLOB, - VisualParams LONGBLOB, - Serial INT DEFAULT NULL, - AvatarHeight FLOAT DEFAULT NULL, - PRIMARY KEY (Owner) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE TABLE UserFriends ( - UserFriendID VARCHAR(36) NOT NULL, - OwnerID VARCHAR(36) DEFAULT NULL, - FriendID VARCHAR(36) DEFAULT NULL, - FriendPermissions INT DEFAULT NULL, - PRIMARY KEY (UserFriendID) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -CREATE UNIQUE INDEX UserFriendsOwnerIdFriendIdIndex ON UserFriends (OwnerID,FriendID); diff --git a/OpenSim/Data/NHibernate/Resources/OpenSim.Data.NHibernate.addin.xml b/OpenSim/Data/NHibernate/Resources/OpenSim.Data.NHibernate.addin.xml deleted file mode 100644 index 37b23cbcbc..0000000000 --- a/OpenSim/Data/NHibernate/Resources/OpenSim.Data.NHibernate.addin.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_AssetStore.sql b/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_AssetStore.sql deleted file mode 100644 index 47531237ac..0000000000 --- a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_AssetStore.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE Assets ( - ID VARCHAR(36) NOT NULL, - Type SMALLINT DEFAULT NULL, - Name VARCHAR(64) DEFAULT NULL, - Description VARCHAR(64) DEFAULT NULL, - Local BOOLEAN DEFAULT NULL, - Temporary BOOLEAN DEFAULT NULL, - Data BYTEA, - PRIMARY KEY (ID) -); diff --git a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_EstateStore.sql b/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_EstateStore.sql deleted file mode 100644 index 3f47930b8a..0000000000 --- a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_EstateStore.sql +++ /dev/null @@ -1,72 +0,0 @@ -CREATE TABLE EstateSettings ( - EstateID INT NOT NULL, - ParentEstateID INT DEFAULT NULL, - EstateOwnerID VARCHAR(36) DEFAULT NULL, - Name VARCHAR(64) DEFAULT NULL, - RedirectGridX INT DEFAULT NULL, - RedirectGridY INT DEFAULT NULL, - BillableFactor DOUBLE PRECISION DEFAULT NULL, - PricePerMeter INT DEFAULT NULL, - SunPosition DOUBLE PRECISION DEFAULT NULL, - - UseGlobalTime BOOLEAN DEFAULT NULL, - FixedSun BOOLEAN DEFAULT NULL, - AllowVoice BOOLEAN DEFAULT NULL, - AllowDirectTeleport BOOLEAN DEFAULT NULL, - ResetHomeOnTeleport BOOLEAN DEFAULT NULL, - PublicAccess BOOLEAN DEFAULT NULL, - DenyAnonymous BOOLEAN DEFAULT NULL, - DenyIdentified BOOLEAN DEFAULT NULL, - DenyTransacted BOOLEAN DEFAULT NULL, - DenyMinors BOOLEAN DEFAULT NULL, - BlockDwell BOOLEAN DEFAULT NULL, - EstateSkipScripts BOOLEAN DEFAULT NULL, - TaxFree BOOLEAN DEFAULT NULL, - AbuseEmailToEstateOwner BOOLEAN DEFAULT NULL, - - AbuseEmail VARCHAR(255) DEFAULT NULL, - - PRIMARY KEY (EstateID) -); - -CREATE TABLE EstateRegionLink ( - EstateRegionLinkID VARCHAR(36) NOT NULL, - EstateID INT DEFAULT NULL, - RegionID VARCHAR(36) DEFAULT NULL, - PRIMARY KEY (EstateRegionLinkID) -); - -CREATE INDEX EstateRegionLinkEstateIDIndex ON EstateRegionLink (EstateID); -CREATE INDEX EstateRegionLinkERegionIDIndex ON EstateRegionLink (RegionID); - - -CREATE TABLE EstateManagers ( - EstateID INT NOT NULL, - ManagerID VARCHAR(36) NOT NULL, - ArrayIndex INT NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -); - -CREATE TABLE EstateUsers ( - EstateID INT NOT NULL, - UserID VARCHAR(36) NOT NULL, - ArrayIndex INT NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -); - -CREATE TABLE EstateGroups ( - EstateID INT NOT NULL, - GroupID VARCHAR(36) NOT NULL, - ArrayIndex INT NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -); - -CREATE TABLE EstateBans ( - EstateID INT NOT NULL, - ArrayIndex INT NOT NULL, - BannedUserID VARCHAR(36) NOT NULL, - BannedHostAddress VARCHAR(16) NOT NULL, - BannedHostIPMask VARCHAR(16) NOT NULL, - BannedHostNameMask VARCHAR(16) NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -); \ No newline at end of file diff --git a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_GridStore.sql b/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_GridStore.sql deleted file mode 100644 index 4366c1ef97..0000000000 --- a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_GridStore.sql +++ /dev/null @@ -1,35 +0,0 @@ -CREATE TABLE Regions ( - RegionID VARCHAR(36) NOT NULL, - OwnerID VARCHAR(36) DEFAULT NULL, - OriginID VARCHAR(36) DEFAULT NULL, - RegionHandle BIGINT DEFAULT NULL, - RegionName VARCHAR(32) DEFAULT NULL, - RegionRecvKey VARCHAR(128) DEFAULT NULL, - RegionSendKey VARCHAR(128) DEFAULT NULL, - RegionSecret VARCHAR(128) DEFAULT NULL, - RegionDataURI VARCHAR(255) DEFAULT NULL, - ServerIP VARCHAR(64) DEFAULT NULL, - ServerPort INT DEFAULT NULL, - ServerURI VARCHAR(255) DEFAULT NULL, - RegionLocX INT DEFAULT NULL, - RegionLocY INT DEFAULT NULL, - RegionLocZ INT DEFAULT NULL, - EastOverrideHandle BIGINT DEFAULT NULL, - WestOverrideHandle BIGINT DEFAULT NULL, - SouthOverrideHandle BIGINT DEFAULT NULL, - NorthOverrideHandle BIGINT DEFAULT NULL, - RegionAssetURI VARCHAR(255) DEFAULT NULL, - RegionAssetRecvKey VARCHAR(128) DEFAULT NULL, - RegionAssetSendKey VARCHAR(128) DEFAULT NULL, - RegionUserURI VARCHAR(255) DEFAULT NULL, - RegionUserRecvKey VARCHAR(128) DEFAULT NULL, - RegionUserSendKey VARCHAR(128) DEFAULT NULL, - RegionMapTextureId VARCHAR(36) DEFAULT NULL, - ServerHttpPort INT DEFAULT NULL, - ServerRemotingPort INT DEFAULT NULL, - PRIMARY KEY (RegionID) -); - -CREATE INDEX RegionNameIndex ON Regions (RegionName); -CREATE INDEX RegionHandleIndex ON Regions (RegionHandle); -CREATE INDEX RegionHandlesIndex ON Regions (EastOverrideHandle,WestOverrideHandle,SouthOverrideHandle,NorthOverrideHandle); diff --git a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_InventoryStore.sql b/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_InventoryStore.sql deleted file mode 100644 index 42af659c1e..0000000000 --- a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_InventoryStore.sql +++ /dev/null @@ -1,39 +0,0 @@ -CREATE TABLE InventoryFolders ( - ID VARCHAR(36) NOT NULL, - Type SMALLINT DEFAULT NULL, - Version INT DEFAULT NULL, - ParentID VARCHAR(36) DEFAULT NULL, - Owner VARCHAR(36) DEFAULT NULL, - Name VARCHAR(64) DEFAULT NULL, - PRIMARY KEY (ID) -); - -CREATE INDEX InventoryFoldersOwnerIdIndex ON InventoryFolders (Owner); -CREATE INDEX InventoryFoldersParentIdIndex ON InventoryFolders (ParentID); - -CREATE TABLE InventoryItems ( - ID VARCHAR(36) NOT NULL, - InvType INT DEFAULT NULL, - AssetType INT DEFAULT NULL, - AssetID VARCHAR(36) DEFAULT NULL, - Folder VARCHAR(36) DEFAULT NULL, - Owner VARCHAR(36) DEFAULT NULL, - Creator VARCHAR(36) DEFAULT NULL, - Name VARCHAR(64) DEFAULT NULL, - Description VARCHAR(64) DEFAULT NULL, - NextPermissions INT DEFAULT NULL, - CurrentPermissions INT DEFAULT NULL, - BasePermissions INT DEFAULT NULL, - EveryOnePermissions INT DEFAULT NULL, - GroupID VARCHAR(36) DEFAULT NULL, - GroupOwned BOOLEAN DEFAULT NULL, - SalePrice INT DEFAULT NULL, - SaleType SMALLINT DEFAULT NULL, - Flags INT DEFAULT NULL, - CreationDate INT DEFAULT NULL, - PRIMARY KEY (ID) -); - -CREATE INDEX InventoryItemsGroupIdIndex ON InventoryItems (GroupID); -CREATE INDEX InventoryItemsOwnerIdIndex ON InventoryItems (Owner); -CREATE INDEX InventoryItemsFolderIdIndex ON InventoryItems (Folder); diff --git a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_RegionStore.sql b/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_RegionStore.sql deleted file mode 100644 index f4a0e68c64..0000000000 --- a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_RegionStore.sql +++ /dev/null @@ -1,169 +0,0 @@ -CREATE TABLE Prims ( - UUID VARCHAR(36) NOT NULL, - RegionID VARCHAR(36) DEFAULT NULL, - GroupID VARCHAR(36) DEFAULT NULL, - ParentID INT DEFAULT NULL, - ParentUUID VARCHAR(36) DEFAULT NULL, - OwnerID VARCHAR(36) DEFAULT NULL, - LastOwnerID VARCHAR(36) DEFAULT NULL, - CreatorID VARCHAR(36) DEFAULT NULL, - CreationDate INT DEFAULT NULL, - LinkNum INT DEFAULT NULL, - Name VARCHAR(255) DEFAULT NULL, - Text VARCHAR(255) DEFAULT NULL, - Description VARCHAR(255) DEFAULT NULL, - SitName VARCHAR(255) DEFAULT NULL, - TouchName VARCHAR(255) DEFAULT NULL, - ObjectFlags INT DEFAULT NULL, - OwnerMask INT DEFAULT NULL, - NextOwnerMask INT DEFAULT NULL, - GroupMask INT DEFAULT NULL, - EveryoneMask INT DEFAULT NULL, - BaseMask INT DEFAULT NULL, - Material SMALLINT DEFAULT NULL, - ScriptAccessPin INT DEFAULT NULL, - TextureAnimation BYTEA, - ParticleSystem BYTEA, - ClickAction SMALLINT DEFAULT NULL, - Color INT DEFAULT NULL, - PositionX DOUBLE PRECISION DEFAULT NULL, - PositionY DOUBLE PRECISION DEFAULT NULL, - PositionZ DOUBLE PRECISION DEFAULT NULL, - GroupPositionX DOUBLE PRECISION DEFAULT NULL, - GroupPositionY DOUBLE PRECISION DEFAULT NULL, - GroupPositionZ DOUBLE PRECISION DEFAULT NULL, - VelocityX DOUBLE PRECISION DEFAULT NULL, - VelocityY DOUBLE PRECISION DEFAULT NULL, - VelocityZ DOUBLE PRECISION DEFAULT NULL, - AngularVelocityX DOUBLE PRECISION DEFAULT NULL, - AngularVelocityY DOUBLE PRECISION DEFAULT NULL, - AngularVelocityZ DOUBLE PRECISION DEFAULT NULL, - AccelerationX DOUBLE PRECISION DEFAULT NULL, - AccelerationY DOUBLE PRECISION DEFAULT NULL, - AccelerationZ DOUBLE PRECISION DEFAULT NULL, - RotationX DOUBLE PRECISION DEFAULT NULL, - RotationY DOUBLE PRECISION DEFAULT NULL, - RotationZ DOUBLE PRECISION DEFAULT NULL, - RotationW DOUBLE PRECISION DEFAULT NULL, - SitTargetOffsetX DOUBLE PRECISION DEFAULT NULL, - SitTargetOffsetY DOUBLE PRECISION DEFAULT NULL, - SitTargetOffsetZ DOUBLE PRECISION DEFAULT NULL, - SitTargetOrientW DOUBLE PRECISION DEFAULT NULL, - SitTargetOrientX DOUBLE PRECISION DEFAULT NULL, - SitTargetOrientY DOUBLE PRECISION DEFAULT NULL, - SitTargetOrientZ DOUBLE PRECISION DEFAULT NULL, - -- this is the shape - Shape INT DEFAULT NULL, - ScaleX DOUBLE PRECISION DEFAULT NULL, - ScaleY DOUBLE PRECISION DEFAULT NULL, - ScaleZ DOUBLE PRECISION DEFAULT NULL, - PCode INT DEFAULT NULL, - PathBegin INT DEFAULT NULL, - PathEnd INT DEFAULT NULL, - PathScaleX INT DEFAULT NULL, - PathScaleY INT DEFAULT NULL, - PathShearX INT DEFAULT NULL, - PathShearY INT DEFAULT NULL, - PathSkew SMALLINT DEFAULT NULL, - PathCurve INT DEFAULT NULL, - PathRadiusOffset SMALLINT DEFAULT NULL, - PathRevolutions INT DEFAULT NULL, - PathTaperX SMALLINT DEFAULT NULL, - PathTaperY SMALLINT DEFAULT NULL, - PathTwist SMALLINT DEFAULT NULL, - ProfileBegin INT DEFAULT NULL, - ProfileEnd INT DEFAULT NULL, - ProfileCurve INT DEFAULT NULL, - ProfileHollow INT DEFAULT NULL, - State INT DEFAULT NULL, - Texture BYTEA, - ExtraParams BYTEA, - PRIMARY KEY (UUID) -); - -CREATE INDEX PrimsRegionIdIndex ON Prims (RegionID); -CREATE INDEX PrimsRegionParentUuidIndex ON Prims (ParentUUID); - -CREATE TABLE Terrain ( - RegionID VARCHAR(36) not null, - MapData BYTEA, - PRIMARY KEY (RegionID) -); - -CREATE TABLE PrimItems ( - ItemID VARCHAR(36) NOT NULL, - GroupID VARCHAR(36) DEFAULT NULL, - PrimID VARCHAR(36) DEFAULT NULL, - ParentFolderID VARCHAR(36) DEFAULT NULL, - AssetID VARCHAR(36) DEFAULT NULL, - OwnerID VARCHAR(36) DEFAULT NULL, - LastOwnerID VARCHAR(36) DEFAULT NULL, - CreatorID VARCHAR(36) DEFAULT NULL, - CreationDate INT DEFAULT NULL, - Type INT DEFAULT NULL, - InvType INT DEFAULT NULL, - Name VARCHAR(255) DEFAULT NULL, - Description VARCHAR(255) DEFAULT NULL, - NextPermissions INT DEFAULT NULL, - CurrentPermissions INT DEFAULT NULL, - BasePermissions INT DEFAULT NULL, - EveryonePermissions INT DEFAULT NULL, - GroupPermissions INT DEFAULT NULL, - Flags INT DEFAULT NULL, - PRIMARY KEY (ItemID) -); - -CREATE INDEX PrimItemsPrimIdIndex ON PrimItems (PrimID); - -CREATE TABLE RegionSettings ( - RegionID VARCHAR(36) NOT NULL, - - BlockTerraform BOOLEAN DEFAULT NULL, - BlockFly BOOLEAN DEFAULT NULL, - AllowDamage BOOLEAN DEFAULT NULL, - RestrictPushing BOOLEAN DEFAULT NULL, - AllowLandResell BOOLEAN DEFAULT NULL, - AllowLandJoinDivide BOOLEAN DEFAULT NULL, - BlockShowInSearch BOOLEAN DEFAULT NULL, - - AgentLimit INT DEFAULT NULL, - ObjectBonus DOUBLE PRECISION DEFAULT NULL, - Maturity INT DEFAULT NULL, - - DisableScripts BOOLEAN DEFAULT NULL, - DisableCollisions BOOLEAN DEFAULT NULL, - DisablePhysics BOOLEAN DEFAULT NULL, - - TerrainTexture1 VARCHAR(36) DEFAULT NULL, - TerrainTexture2 VARCHAR(36) DEFAULT NULL, - TerrainTexture3 VARCHAR(36) DEFAULT NULL, - TerrainTexture4 VARCHAR(36) DEFAULT NULL, - - Elevation1NW DOUBLE PRECISION DEFAULT NULL, - Elevation2NW DOUBLE PRECISION DEFAULT NULL, - Elevation1NE DOUBLE PRECISION DEFAULT NULL, - Elevation2NE DOUBLE PRECISION DEFAULT NULL, - Elevation1SE DOUBLE PRECISION DEFAULT NULL, - Elevation2SE DOUBLE PRECISION DEFAULT NULL, - Elevation1SW DOUBLE PRECISION DEFAULT NULL, - Elevation2SW DOUBLE PRECISION DEFAULT NULL, - - WaterHeight DOUBLE PRECISION DEFAULT NULL, - TerrainRaiseLimit DOUBLE PRECISION DEFAULT NULL, - TerrainLowerLimit DOUBLE PRECISION DEFAULT NULL, - - UseEstateSun BOOLEAN DEFAULT NULL, - Sandbox BOOLEAN DEFAULT NULL, - - SunVectorX DOUBLE PRECISION DEFAULT NULL, - SunVectorY DOUBLE PRECISION DEFAULT NULL, - SunVectorZ DOUBLE PRECISION DEFAULT NULL, - - FixedSun BOOLEAN DEFAULT NULL, - SunPosition DOUBLE PRECISION DEFAULT NULL, - - Covenant VARCHAR(36) DEFAULT NULL, - - PRIMARY KEY (RegionID) -); - diff --git a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_UserStore.sql b/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_UserStore.sql deleted file mode 100644 index b3c7ef157d..0000000000 --- a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_UserStore.sql +++ /dev/null @@ -1,104 +0,0 @@ -CREATE TABLE UserAgents ( - ProfileID VARCHAR(36) NOT NULL, - AgentIP VARCHAR(24) DEFAULT NULL, - AgentPort INT DEFAULT NULL, - AgentOnline BOOLEAN DEFAULT NULL, - SessionID VARCHAR(36) DEFAULT NULL, - SecureSessionID VARCHAR(36) DEFAULT NULL, - InitialRegion VARCHAR(255) DEFAULT NULL, - Region VARCHAR(255) DEFAULT NULL, - LoginTime INT DEFAULT NULL, - LogoutTime INT DEFAULT NULL, - Handle BIGINT DEFAULT NULL, - PositionX DOUBLE PRECISION DEFAULT NULL, - PositionY DOUBLE PRECISION DEFAULT NULL, - PositionZ DOUBLE PRECISION DEFAULT NULL, - LookAtX DOUBLE PRECISION DEFAULT NULL, - LookAtY DOUBLE PRECISION DEFAULT NULL, - LookAtZ DOUBLE PRECISION DEFAULT NULL, - PRIMARY KEY (ProfileID) -); - -CREATE TABLE UserProfiles ( - ID VARCHAR(36) NOT NULL, - WebLoginKey VARCHAR(36) DEFAULT NULL, - FirstName VARCHAR(32) DEFAULT NULL, - SurName VARCHAR(32) DEFAULT NULL, - Email VARCHAR(250) DEFAULT NULL, - PasswordHash VARCHAR(32) DEFAULT NULL, - PasswordSalt VARCHAR(32) DEFAULT NULL, - HomeRegionID VARCHAR(36) DEFAULT NULL, - HomeRegionX INT DEFAULT NULL, - HomeRegionY INT DEFAULT NULL, - HomeLocationX DOUBLE PRECISION DEFAULT NULL, - HomeLocationY DOUBLE PRECISION DEFAULT NULL, - HomeLocationZ DOUBLE PRECISION DEFAULT NULL, - HomeLookAtX DOUBLE PRECISION DEFAULT NULL, - HomeLookAtY DOUBLE PRECISION DEFAULT NULL, - HomeLookAtZ DOUBLE PRECISION DEFAULT NULL, - Created INT DEFAULT NULL, - LastLogin INT DEFAULT NULL, - UserInventoryURI VARCHAR(255) DEFAULT NULL, - UserAssetURI VARCHAR(255) DEFAULT NULL, - Image VARCHAR(36) DEFAULT NULL, - FirstLifeImage VARCHAR(36) DEFAULT NULL, - AboutText TEXT DEFAULT NULL, - FirstLifeAboutText TEXT DEFAULT NULL, - CanDoMask INT DEFAULT NULL, - WantDoMask INT DEFAULT NULL, - UserFlags INT DEFAULT NULL, - GodLevel INT DEFAULT NULL, - CustomType VARCHAR(32) DEFAULT NULL, - Partner VARCHAR(36) DEFAULT NULL, - RootInventoryFolderID VARCHAR(36) DEFAULT NULL, - PRIMARY KEY (ID) -); - -CREATE INDEX UserSurnameIndex ON UserProfiles (SurName); -CREATE INDEX UserFirstNameIndex ON UserProfiles (FirstName); -CREATE UNIQUE INDEX UserFullNameIndex ON UserProfiles (SurName,FirstName); - -CREATE TABLE UserAppearances ( - Owner VARCHAR(36) NOT NULL, - BodyItem VARCHAR(36) DEFAULT NULL, - BodyAsset VARCHAR(36) DEFAULT NULL, - SkinItem VARCHAR(36) DEFAULT NULL, - SkinAsset VARCHAR(36) DEFAULT NULL, - HairItem VARCHAR(36) DEFAULT NULL, - HairAsset VARCHAR(36) DEFAULT NULL, - EyesItem VARCHAR(36) DEFAULT NULL, - EyesAsset VARCHAR(36) DEFAULT NULL, - ShirtItem VARCHAR(36) DEFAULT NULL, - ShirtAsset VARCHAR(36) DEFAULT NULL, - PantsItem VARCHAR(36) DEFAULT NULL, - PantsAsset VARCHAR(36) DEFAULT NULL, - ShoesItem VARCHAR(36) DEFAULT NULL, - ShoesAsset VARCHAR(36) DEFAULT NULL, - SocksItem VARCHAR(36) DEFAULT NULL, - SocksAsset VARCHAR(36) DEFAULT NULL, - JacketItem VARCHAR(36) DEFAULT NULL, - JacketAsset VARCHAR(36) DEFAULT NULL, - GlovesItem VARCHAR(36) DEFAULT NULL, - GlovesAsset VARCHAR(36) DEFAULT NULL, - UnderShirtItem VARCHAR(36) DEFAULT NULL, - UnderShirtAsset VARCHAR(36) DEFAULT NULL, - UnderPantsItem VARCHAR(36) DEFAULT NULL, - UnderPantsAsset VARCHAR(36) DEFAULT NULL, - SkirtItem VARCHAR(36) DEFAULT NULL, - SkirtAsset VARCHAR(36) DEFAULT NULL, - Texture BYTEA, - VisualParams BYTEA, - Serial INT DEFAULT NULL, - AvatarHeight FLOAT DEFAULT NULL, - PRIMARY KEY (Owner) -); - -CREATE TABLE UserFriends ( - UserFriendID VARCHAR(36) NOT NULL, - OwnerID VARCHAR(36) DEFAULT NULL, - FriendID VARCHAR(36) DEFAULT NULL, - FriendPermissions INT DEFAULT NULL, - PRIMARY KEY (UserFriendID) -); - -CREATE UNIQUE INDEX UserFriendsOwnerIdFriendIdIndex ON UserFriends (OwnerID,FriendID); diff --git a/OpenSim/Data/NHibernate/Resources/RegionProfileData.hbm.xml b/OpenSim/Data/NHibernate/Resources/RegionProfileData.hbm.xml deleted file mode 100644 index 5ff37d8598..0000000000 --- a/OpenSim/Data/NHibernate/Resources/RegionProfileData.hbm.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/Resources/RegionSettings.hbm.xml b/OpenSim/Data/NHibernate/Resources/RegionSettings.hbm.xml deleted file mode 100644 index 3843a8d4a0..0000000000 --- a/OpenSim/Data/NHibernate/Resources/RegionSettings.hbm.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/Resources/RegionStore.hbm.xml b/OpenSim/Data/NHibernate/Resources/RegionStore.hbm.xml deleted file mode 100644 index 98c648b1e0..0000000000 --- a/OpenSim/Data/NHibernate/Resources/RegionStore.hbm.xml +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_AssetStore.sql b/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_AssetStore.sql deleted file mode 100644 index aedf76428e..0000000000 --- a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_AssetStore.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE Assets ( - ID VARCHAR(36) NOT NULL, - Type SMALLINT DEFAULT NULL, - Name VARCHAR(64) DEFAULT NULL, - Description VARCHAR(64) DEFAULT NULL, - Local BIT DEFAULT NULL, - Temporary BIT DEFAULT NULL, - Data BLOB, - PRIMARY KEY (ID) -); diff --git a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_EstateStore.sql b/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_EstateStore.sql deleted file mode 100644 index afe702fd14..0000000000 --- a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_EstateStore.sql +++ /dev/null @@ -1,71 +0,0 @@ -CREATE TABLE EstateSettings ( - EstateID INT NOT NULL, - ParentEstateID INT DEFAULT NULL, - EstateOwnerID VARCHAR(36) DEFAULT NULL, - Name VARCHAR(64) DEFAULT NULL, - RedirectGridX INT DEFAULT NULL, - RedirectGridY INT DEFAULT NULL, - BillableFactor DOUBLE DEFAULT NULL, - PricePerMeter INT DEFAULT NULL, - SunPosition DOUBLE DEFAULT NULL, - - UseGlobalTime BIT DEFAULT NULL, - FixedSun BIT DEFAULT NULL, - AllowVoice BIT DEFAULT NULL, - AllowDirectTeleport BIT DEFAULT NULL, - ResetHomeOnTeleport BIT DEFAULT NULL, - PublicAccess BIT DEFAULT NULL, - DenyAnonymous BIT DEFAULT NULL, - DenyIdentified BIT DEFAULT NULL, - DenyTransacted BIT DEFAULT NULL, - DenyMinors BIT DEFAULT NULL, - BlockDwell BIT DEFAULT NULL, - EstateSkipScripts BIT DEFAULT NULL, - TaxFree BIT DEFAULT NULL, - AbuseEmailToEstateOwner BIT DEFAULT NULL, - - AbuseEmail VARCHAR(255) DEFAULT NULL, - - PRIMARY KEY (EstateID) -); - -CREATE TABLE EstateRegionLink ( - EstateRegionLinkID VARCHAR(36) NOT NULL, - EstateID INT DEFAULT NULL, - RegionID VARCHAR(36) DEFAULT NULL, - PRIMARY KEY (EstateRegionLinkID) -); - -CREATE INDEX EstateRegionLinkEstateIDIndex ON EstateRegionLink (EstateID); -CREATE INDEX EstateRegionLinkERegionIDIndex ON EstateRegionLink (RegionID); - -CREATE TABLE EstateManagers ( - EstateID INT NOT NULL, - ManagerID VARCHAR(36) NOT NULL, - ArrayIndex INT NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -); - -CREATE TABLE EstateUsers ( - EstateID INT NOT NULL, - UserID VARCHAR(36) NOT NULL, - ArrayIndex INT NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -); - -CREATE TABLE EstateGroups ( - EstateID INT NOT NULL, - GroupID VARCHAR(36) NOT NULL, - ArrayIndex INT NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -); - -CREATE TABLE EstateBans ( - EstateID INT NOT NULL, - ArrayIndex INT NOT NULL, - BannedUserID VARCHAR(36) NOT NULL, - BannedHostAddress VARCHAR(16) NOT NULL, - BannedHostIPMask VARCHAR(16) NOT NULL, - BannedHostNameMask VARCHAR(16) NOT NULL, - PRIMARY KEY (EstateID,ArrayIndex) -); diff --git a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_GridStore.sql b/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_GridStore.sql deleted file mode 100644 index 4f09848651..0000000000 --- a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_GridStore.sql +++ /dev/null @@ -1,35 +0,0 @@ -CREATE TABLE Regions ( - RegionId VARCHAR(36) NOT NULL, - RegionHandle BIGINT UNSIGNED NOT NULL, - RegionName VARCHAR(32) DEFAULT NULL, - RegionRecvKey VARCHAR(128) DEFAULT NULL, - RegionSendKey VARCHAR(128) DEFAULT NULL, - RegionSecret VARCHAR(128) DEFAULT NULL, - RegionDataURI VARCHAR(255) DEFAULT NULL, - ServerIP VARCHAR(64) DEFAULT NULL, - ServerPort INT UNSIGNED DEFAULT NULL, - ServerURI VARCHAR(255) DEFAULT NULL, - RegionLocX INT UNSIGNED DEFAULT NULL, - RegionLocY INT UNSIGNED DEFAULT NULL, - RegionLocZ INT UNSIGNED DEFAULT NULL, - EastOverrideHandle BIGINT UNSIGNED DEFAULT NULL, - WestOverrideHandle BIGINT UNSIGNED DEFAULT NULL, - SouthOverrideHandle BIGINT UNSIGNED DEFAULT NULL, - NorthOverrideHandle BIGINT UNSIGNED DEFAULT NULL, - RegionAssetURI VARCHAR(255) DEFAULT NULL, - RegionAssetRecvKey VARCHAR(128) DEFAULT NULL, - RegionAssetSendKey VARCHAR(128) DEFAULT NULL, - RegionUserURI VARCHAR(255) DEFAULT NULL, - RegionUserRecvKey VARCHAR(128) DEFAULT NULL, - RegionUserSendKey VARCHAR(128) DEFAULT NULL, - RegionMapTextureId VARCHAR(36) DEFAULT NULL, - ServerHttpPort INT DEFAULT NULL, - ServerRemotingPort INT DEFAULT NULL, - OwnerID VARCHAR(36) DEFAULT NULL, - OriginID VARCHAR(36) DEFAULT NULL, - PRIMARY KEY (RegionId) -); - -CREATE INDEX RegionNameIndex ON Regions (RegionName); -CREATE INDEX RegionHandleIndex ON Regions (RegionHandle); -CREATE INDEX RegionHandlesIndex ON Regions (EastOverrideHandle,WestOverrideHandle,SouthOverrideHandle,NorthOverrideHandle); diff --git a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_InventoryStore.sql b/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_InventoryStore.sql deleted file mode 100644 index 38978ee752..0000000000 --- a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_InventoryStore.sql +++ /dev/null @@ -1,39 +0,0 @@ -CREATE TABLE InventoryFolders ( - ID VARCHAR(36) NOT NULL, - Type SMALLINT DEFAULT NULL, - Version INT DEFAULT NULL, - ParentID VARCHAR(36) DEFAULT NULL, - Owner VARCHAR(36) DEFAULT NULL, - Name VARCHAR(64) DEFAULT NULL, - PRIMARY KEY (ID) -); - -CREATE INDEX InventoryFoldersOwnerIdIndex ON InventoryFolders (Owner); -CREATE INDEX InventoryFoldersParentIdIndex ON InventoryFolders (ParentID); - -CREATE TABLE InventoryItems ( - ID VARCHAR(36) NOT NULL, - InvType INT DEFAULT NULL, - AssetType INT DEFAULT NULL, - AssetID VARCHAR(36) DEFAULT NULL, - Folder VARCHAR(36) DEFAULT NULL, - Owner VARCHAR(36) DEFAULT NULL, - Creator VARCHAR(36) DEFAULT NULL, - Name VARCHAR(64) DEFAULT NULL, - Description VARCHAR(64) DEFAULT NULL, - NextPermissions INT DEFAULT NULL, - CurrentPermissions INT DEFAULT NULL, - BasePermissions INT DEFAULT NULL, - EveryOnePermissions INT DEFAULT NULL, - GroupID VARCHAR(36) DEFAULT NULL, - GroupOwned BIT DEFAULT NULL, - SalePrice INT DEFAULT NULL, - SaleType TINYINT DEFAULT NULL, - Flags INT DEFAULT NULL, - CreationDate INT DEFAULT NULL, - PRIMARY KEY (ID) -); - -CREATE INDEX InventoryItemsGroupIdIndex ON InventoryItems (GroupID); -CREATE INDEX InventoryItemsOwnerIdIndex ON InventoryItems (Owner); -CREATE INDEX InventoryItemsFolderIdIndex ON InventoryItems (Folder); diff --git a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_RegionStore.sql b/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_RegionStore.sql deleted file mode 100644 index 2b8e62ad62..0000000000 --- a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_RegionStore.sql +++ /dev/null @@ -1,168 +0,0 @@ -CREATE TABLE Prims ( - UUID VARCHAR(36) NOT NULL, - RegionID VARCHAR(36) DEFAULT NULL, - GroupID VARCHAR(36) DEFAULT NULL, - ParentID INT DEFAULT NULL, - ParentUUID VARCHAR(36) DEFAULT NULL, - OwnerID VARCHAR(36) DEFAULT NULL, - LastOwnerID VARCHAR(36) DEFAULT NULL, - CreatorID VARCHAR(36) DEFAULT NULL, - CreationDate INT DEFAULT NULL, - LinkNum INT DEFAULT NULL, - Name VARCHAR(255) DEFAULT NULL, - Text VARCHAR(255) DEFAULT NULL, - Description VARCHAR(255) DEFAULT NULL, - SitName VARCHAR(255) DEFAULT NULL, - TouchName VARCHAR(255) DEFAULT NULL, - ObjectFlags INT DEFAULT NULL, - OwnerMask INT DEFAULT NULL, - NextOwnerMask INT DEFAULT NULL, - GroupMask INT DEFAULT NULL, - EveryoneMask INT DEFAULT NULL, - BaseMask INT DEFAULT NULL, - Material TINYINT DEFAULT NULL, - ScriptAccessPin INT DEFAULT NULL, - TextureAnimation BLOB, - ParticleSystem BLOB, - ClickAction TINYINT DEFAULT NULL, - Color INT DEFAULT NULL, - PositionX DOUBLE DEFAULT NULL, - PositionY DOUBLE DEFAULT NULL, - PositionZ DOUBLE DEFAULT NULL, - GroupPositionX DOUBLE DEFAULT NULL, - GroupPositionY DOUBLE DEFAULT NULL, - GroupPositionZ DOUBLE DEFAULT NULL, - VelocityX DOUBLE DEFAULT NULL, - VelocityY DOUBLE DEFAULT NULL, - VelocityZ DOUBLE DEFAULT NULL, - AngularVelocityX DOUBLE DEFAULT NULL, - AngularVelocityY DOUBLE DEFAULT NULL, - AngularVelocityZ DOUBLE DEFAULT NULL, - AccelerationX DOUBLE DEFAULT NULL, - AccelerationY DOUBLE DEFAULT NULL, - AccelerationZ DOUBLE DEFAULT NULL, - RotationX DOUBLE DEFAULT NULL, - RotationY DOUBLE DEFAULT NULL, - RotationZ DOUBLE DEFAULT NULL, - RotationW DOUBLE DEFAULT NULL, - SitTargetOffsetX DOUBLE DEFAULT NULL, - SitTargetOffsetY DOUBLE DEFAULT NULL, - SitTargetOffsetZ DOUBLE DEFAULT NULL, - SitTargetOrientW DOUBLE DEFAULT NULL, - SitTargetOrientX DOUBLE DEFAULT NULL, - SitTargetOrientY DOUBLE DEFAULT NULL, - SitTargetOrientZ DOUBLE DEFAULT NULL, - -- this is the shape - Shape INT DEFAULT NULL, - ScaleX DOUBLE DEFAULT NULL, - ScaleY DOUBLE DEFAULT NULL, - ScaleZ DOUBLE DEFAULT NULL, - PCode INT DEFAULT NULL, - PathBegin INT DEFAULT NULL, - PathEnd INT DEFAULT NULL, - PathScaleX INT DEFAULT NULL, - PathScaleY INT DEFAULT NULL, - PathShearX INT DEFAULT NULL, - PathShearY INT DEFAULT NULL, - PathSkew INT DEFAULT NULL, - PathCurve INT DEFAULT NULL, - PathRadiusOffset INT DEFAULT NULL, - PathRevolutions INT DEFAULT NULL, - PathTaperX INT DEFAULT NULL, - PathTaperY INT DEFAULT NULL, - PathTwist INT DEFAULT NULL, - ProfileBegin INT DEFAULT NULL, - ProfileEnd INT DEFAULT NULL, - ProfileCurve INT DEFAULT NULL, - ProfileHollow INT DEFAULT NULL, - State INT DEFAULT NULL, - Texture BLOB, - ExtraParams BLOB, - PRIMARY KEY (UUID) -); - -CREATE INDEX PrimsRegionIdIndex ON Prims (RegionID); -CREATE INDEX PrimsRegionParentUuidIndex ON Prims (ParentUUID); - -CREATE TABLE Terrain ( - RegionID VARCHAR(36) NOT NULL, - MapData BLOB, - PRIMARY KEY (RegionID) -); - -CREATE TABLE PrimItems ( - ItemID VARCHAR(36) NOT NULL, - GroupID VARCHAR(36) DEFAULT NULL, - PrimID VARCHAR(36) DEFAULT NULL, - ParentFolderID VARCHAR(36) DEFAULT NULL, - AssetID VARCHAR(36) DEFAULT NULL, - OwnerID VARCHAR(36) DEFAULT NULL, - LastOwnerID VARCHAR(36) DEFAULT NULL, - CreatorID VARCHAR(36) DEFAULT NULL, - CreationDate BIGINT DEFAULT NULL, - Type INT DEFAULT NULL, - InvType INT DEFAULT NULL, - Name VARCHAR(255) DEFAULT NULL, - Description VARCHAR(255) DEFAULT NULL, - NextPermissions INT DEFAULT NULL, - CurrentPermissions INT DEFAULT NULL, - BasePermissions INT DEFAULT NULL, - EveryonePermissions INT DEFAULT NULL, - GroupPermissions INT DEFAULT NULL, - Flags INT DEFAULT NULL, - PRIMARY KEY (ItemID) -); - -CREATE INDEX PrimItemsPrimIdIndex ON PrimItems (PrimID); - -CREATE TABLE RegionSettings ( - RegionID VARCHAR(36) NOT NULL, - - BlockTerraform BIT DEFAULT NULL, - BlockFly BIT DEFAULT NULL, - AllowDamage BIT DEFAULT NULL, - RestrictPushing BIT DEFAULT NULL, - AllowLandResell BIT DEFAULT NULL, - AllowLandJoinDivide BIT DEFAULT NULL, - BlockShowInSearch BIT DEFAULT NULL, - - AgentLimit INT DEFAULT NULL, - ObjectBonus DOUBLE DEFAULT NULL, - Maturity INT DEFAULT NULL, - - DisableScripts BIT DEFAULT NULL, - DisableCollisions BIT DEFAULT NULL, - DisablePhysics BIT DEFAULT NULL, - - TerrainTexture1 VARCHAR(36) DEFAULT NULL, - TerrainTexture2 VARCHAR(36) DEFAULT NULL, - TerrainTexture3 VARCHAR(36) DEFAULT NULL, - TerrainTexture4 VARCHAR(36) DEFAULT NULL, - - Elevation1NW DOUBLE DEFAULT NULL, - Elevation2NW DOUBLE DEFAULT NULL, - Elevation1NE DOUBLE DEFAULT NULL, - Elevation2NE DOUBLE DEFAULT NULL, - Elevation1SE DOUBLE DEFAULT NULL, - Elevation2SE DOUBLE DEFAULT NULL, - Elevation1SW DOUBLE DEFAULT NULL, - Elevation2SW DOUBLE DEFAULT NULL, - - WaterHeight DOUBLE DEFAULT NULL, - TerrainRaiseLimit DOUBLE DEFAULT NULL, - TerrainLowerLimit DOUBLE DEFAULT NULL, - - UseEstateSun BIT DEFAULT NULL, - Sandbox BIT DEFAULT NULL, - - SunVectorX DOUBLE DEFAULT NULL, - SunVectorY DOUBLE DEFAULT NULL, - SunVectorZ DOUBLE DEFAULT NULL, - - FixedSun BIT DEFAULT NULL, - SunPosition DOUBLE DEFAULT NULL, - - Covenant VARCHAR(36) DEFAULT NULL, - - PRIMARY KEY (RegionID) -); diff --git a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_UserStore.sql b/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_UserStore.sql deleted file mode 100644 index 6084886d12..0000000000 --- a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_UserStore.sql +++ /dev/null @@ -1,104 +0,0 @@ -CREATE TABLE UserAgents ( - ProfileID VARCHAR(36) NOT NULL, - AgentIP VARCHAR(24) DEFAULT NULL, - AgentPort INT DEFAULT NULL, - AgentOnline BIT DEFAULT NULL, - SessionID VARCHAR(36) DEFAULT NULL, - SecureSessionID VARCHAR(36) DEFAULT NULL, - InitialRegion VARCHAR(255) DEFAULT NULL, - Region VARCHAR(255) DEFAULT NULL, - LoginTime INT DEFAULT NULL, - LogoutTime INT DEFAULT NULL, - Handle BIGINT DEFAULT NULL, - PositionX DOUBLE DEFAULT NULL, - PositionY DOUBLE DEFAULT NULL, - PositionZ DOUBLE DEFAULT NULL, - LookAtX DOUBLE DEFAULT NULL, - LookAtY DOUBLE DEFAULT NULL, - LookAtZ DOUBLE DEFAULT NULL, - PRIMARY KEY (ProfileID) -); - -CREATE TABLE UserProfiles ( - ID VARCHAR(36) NOT NULL, - WebLoginKey VARCHAR(36) DEFAULT NULL, - FirstName VARCHAR(32) DEFAULT NULL, - SurName VARCHAR(32) DEFAULT NULL, - Email VARCHAR(250) DEFAULT NULL, - PasswordHash VARCHAR(32) DEFAULT NULL, - PasswordSalt VARCHAR(32) DEFAULT NULL, - HomeRegionID VARCHAR(36) DEFAULT NULL, - HomeRegionX INT DEFAULT NULL, - HomeRegionY INT DEFAULT NULL, - HomeLocationX DOUBLE DEFAULT NULL, - HomeLocationY DOUBLE DEFAULT NULL, - HomeLocationZ DOUBLE DEFAULT NULL, - HomeLookAtX DOUBLE DEFAULT NULL, - HomeLookAtY DOUBLE DEFAULT NULL, - HomeLookAtZ DOUBLE DEFAULT NULL, - Created INT DEFAULT NULL, - LastLogin INT DEFAULT NULL, - UserInventoryURI VARCHAR(255) DEFAULT NULL, - UserAssetURI VARCHAR(255) DEFAULT NULL, - Image VARCHAR(36) DEFAULT NULL, - FirstLifeImage VARCHAR(36) DEFAULT NULL, - AboutText TEXT DEFAULT NULL, - FirstLifeAboutText TEXT DEFAULT NULL, - CanDoMask INT DEFAULT NULL, - WantDoMask INT DEFAULT NULL, - UserFlags INT DEFAULT NULL, - GodLevel INT DEFAULT NULL, - CustomType VARCHAR(32) DEFAULT NULL, - Partner VARCHAR(36) DEFAULT NULL, - RootInventoryFolderID VARCHAR(36) DEFAULT NULL, - PRIMARY KEY (ID) -); - -CREATE INDEX UserSurnameIndex ON UserProfiles (SurName); -CREATE INDEX UserFirstNameIndex ON UserProfiles (FirstName); -CREATE UNIQUE INDEX UserFullNameIndex ON UserProfiles (SurName,FirstName); - -CREATE TABLE UserAppearances ( - Owner VARCHAR(36) NOT NULL, - BodyItem VARCHAR(36) DEFAULT NULL, - BodyAsset VARCHAR(36) DEFAULT NULL, - SkinItem VARCHAR(36) DEFAULT NULL, - SkinAsset VARCHAR(36) DEFAULT NULL, - HairItem VARCHAR(36) DEFAULT NULL, - HairAsset VARCHAR(36) DEFAULT NULL, - EyesItem VARCHAR(36) DEFAULT NULL, - EyesAsset VARCHAR(36) DEFAULT NULL, - ShirtItem VARCHAR(36) DEFAULT NULL, - ShirtAsset VARCHAR(36) DEFAULT NULL, - PantsItem VARCHAR(36) DEFAULT NULL, - PantsAsset VARCHAR(36) DEFAULT NULL, - ShoesItem VARCHAR(36) DEFAULT NULL, - ShoesAsset VARCHAR(36) DEFAULT NULL, - SocksItem VARCHAR(36) DEFAULT NULL, - SocksAsset VARCHAR(36) DEFAULT NULL, - JacketItem VARCHAR(36) DEFAULT NULL, - JacketAsset VARCHAR(36) DEFAULT NULL, - GlovesItem VARCHAR(36) DEFAULT NULL, - GlovesAsset VARCHAR(36) DEFAULT NULL, - UnderShirtItem VARCHAR(36) DEFAULT NULL, - UnderShirtAsset VARCHAR(36) DEFAULT NULL, - UnderPantsItem VARCHAR(36) DEFAULT NULL, - UnderPantsAsset VARCHAR(36) DEFAULT NULL, - SkirtItem VARCHAR(36) DEFAULT NULL, - SkirtAsset VARCHAR(36) DEFAULT NULL, - Texture BLOB, - VisualParams BLOB, - Serial INT DEFAULT NULL, - AvatarHeight FLOAT DEFAULT NULL, - PRIMARY KEY (Owner) -); - -CREATE TABLE UserFriends ( - UserFriendID VARCHAR(36) NOT NULL, - OwnerID VARCHAR(36) DEFAULT NULL, - FriendID VARCHAR(36) DEFAULT NULL, - FriendPermissions INT DEFAULT NULL, - PRIMARY KEY (UserFriendID) -); - -CREATE UNIQUE INDEX UserFriendsOwnerIdFriendIdIndex ON UserFriends (OwnerID,FriendID); diff --git a/OpenSim/Data/NHibernate/Resources/UserAgentData.hbm.xml b/OpenSim/Data/NHibernate/Resources/UserAgentData.hbm.xml deleted file mode 100644 index 70b69981d8..0000000000 --- a/OpenSim/Data/NHibernate/Resources/UserAgentData.hbm.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/Resources/UserAppearance.hbm.xml b/OpenSim/Data/NHibernate/Resources/UserAppearance.hbm.xml deleted file mode 100644 index 21e547f3ca..0000000000 --- a/OpenSim/Data/NHibernate/Resources/UserAppearance.hbm.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/Resources/UserFriend.hbm.xml b/OpenSim/Data/NHibernate/Resources/UserFriend.hbm.xml deleted file mode 100644 index cb23858a9b..0000000000 --- a/OpenSim/Data/NHibernate/Resources/UserFriend.hbm.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/Resources/UserProfileData.hbm.xml b/OpenSim/Data/NHibernate/Resources/UserProfileData.hbm.xml deleted file mode 100644 index 5b1f9b03dc..0000000000 --- a/OpenSim/Data/NHibernate/Resources/UserProfileData.hbm.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenSim/Data/NHibernate/SByteType.cs b/OpenSim/Data/NHibernate/SByteType.cs deleted file mode 100644 index 92ab0ba83f..0000000000 --- a/OpenSim/Data/NHibernate/SByteType.cs +++ /dev/null @@ -1,111 +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.Data; -using NHibernate; -using NHibernate.SqlTypes; -using NHibernate.UserTypes; - -namespace OpenSim.Data.NHibernate -{ - [Serializable] - public class SByteType: IUserType - { - public object Assemble(object cached, object owner) - { - return cached; - } - - bool IUserType.Equals(object sbyte1, object sbyte2) - { - return sbyte1.Equals(sbyte2); - } - - public object DeepCopy(object sbyte1) - { - return sbyte1; - } - - public object Disassemble(object sbyte1) - { - return sbyte1; - } - - public int GetHashCode(object sbyte1) - { - return (sbyte1 == null) ? 0 : sbyte1.GetHashCode(); - } - - public bool IsMutable - { - get { return false; } - } - - public object NullSafeGet(IDataReader rs, string[] names, object owner) - { - object sbyte1 = null; - - int ord = rs.GetOrdinal(names[0]); - if (!rs.IsDBNull(ord)) - { - object tempO = rs.GetValue(ord); - if (tempO is Byte) - { - sbyte1 = Convert.ToSByte(((byte)tempO)); - } - else - { - short temp = rs.GetInt16(ord); - sbyte1 = Convert.ToSByte(temp); - } - } - return sbyte1; - } - - public void NullSafeSet(IDbCommand cmd, object obj, int index) - { - sbyte b = (sbyte)obj; - ((IDataParameter)cmd.Parameters[index]).Value = Convert.ToInt16(b); - } - - public object Replace(object original, object target, object owner) - { - return original; - } - - public Type ReturnedType - { - get { return typeof(sbyte); } - } - - public SqlType[] SqlTypes - { - get { return new SqlType [] { NHibernateUtil.Byte.SqlType }; } - } - } -} diff --git a/OpenSim/Data/NHibernate/Terrain.cs b/OpenSim/Data/NHibernate/Terrain.cs deleted file mode 100644 index 4be35c648a..0000000000 --- a/OpenSim/Data/NHibernate/Terrain.cs +++ /dev/null @@ -1,118 +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.Reflection; -using log4net; -using OpenMetaverse; -using OpenSim.Framework; - -namespace OpenSim.Data.NHibernate -{ - public class Terrain - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private double[,] map; - private UUID regionID; - - public Terrain(UUID Region, double[,] array) - { - map = array; - regionID = Region; - } - - public Terrain() - { - map = new double[Constants.RegionSize, Constants.RegionSize]; - map.Initialize(); - regionID = UUID.Zero; - } - - public UUID RegionID - { - get { return regionID; } - set { regionID = value; } - } - - public byte[] MapData - { - get { return serializeTerrain(map); } - set { map = parseTerrain(value); } - } - - public double[,] Doubles - { - get {return map;} - set {map = value;} - } - - private static double[,] parseTerrain(byte[] data) - { - double[,] terret = new double[Constants.RegionSize, Constants.RegionSize]; - terret.Initialize(); - - MemoryStream str = new MemoryStream(data); - BinaryReader br = new BinaryReader(str); - try { - for (int x = 0; x < Constants.RegionSize; x++) - { - for (int y = 0; y < Constants.RegionSize; y++) - { - terret[x, y] = br.ReadDouble(); - } - } - } - catch (Exception e) - { - m_log.Error("Issue parsing Map", e); - } - return terret; - } - - private static byte[] serializeTerrain(double[,] val) - { - MemoryStream str = new MemoryStream((int) ((Constants.RegionSize*Constants.RegionSize)*sizeof (double))); - BinaryWriter bw = new BinaryWriter(str); - - // TODO: COMPATIBILITY - Add byte-order conversions - for (int x = 0; x < Constants.RegionSize; x++) - { - for (int y = 0; y < Constants.RegionSize; y++) - { - double height = val[x, y]; - if (height <= 0.0) - height = double.Epsilon; - - bw.Write(height); - } - } - return str.ToArray(); - } - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlAssetTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlAssetTest.cs deleted file mode 100644 index 89c956766d..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlAssetTest.cs +++ /dev/null @@ -1,81 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateMsSqlAssetTest : BasicAssetTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - string connect = "MsSql2005Dialect;SqlClientDriver;Data Source=127.0.0.1;Network Library=DBMSSOCN;Initial Catalog=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit"; - - db = new NHibernateAssetData(); - db.Initialise(connect); - database = ((NHibernateAssetData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlEstateTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlEstateTest.cs deleted file mode 100644 index 6d431a7ac2..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlEstateTest.cs +++ /dev/null @@ -1,81 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateMsSqlEstateTest : BasicEstateTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - string connect = "MsSql2005Dialect;SqlClientDriver;Data Source=127.0.0.1;Network Library=DBMSSOCN;Initial Catalog=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit"; - - db = new NHibernateEstateData(); - db.Initialise(connect); - database = ((NHibernateEstateData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - ((NHibernateEstateData)db).Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlGridTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlGridTest.cs deleted file mode 100644 index 30280af872..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlGridTest.cs +++ /dev/null @@ -1,79 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateMsSqlGridTest : BasicGridTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "MySQL5Dialect;MySqlDataDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateGridData(); - db.Initialise(connect); - database = ((NHibernateGridData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlInventoryTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlInventoryTest.cs deleted file mode 100644 index 6d60006621..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlInventoryTest.cs +++ /dev/null @@ -1,80 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateMsSqlInventoryTest : BasicInventoryTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "MySQL5Dialect;MySqlDataDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateInventoryData(); - db.Initialise(connect); - database = ((NHibernateInventoryData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlRegionTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlRegionTest.cs deleted file mode 100644 index 9a9c41d5d1..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlRegionTest.cs +++ /dev/null @@ -1,81 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateMsSqlRegionTest : BasicRegionTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "MySQL5Dialect;MySqlDataDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit"; - - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateRegionData(); - db.Initialise(connect); - database = ((NHibernateRegionData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlUserTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlUserTest.cs deleted file mode 100644 index cd861de99a..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateMsSqlUserTest.cs +++ /dev/null @@ -1,79 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateMsSqlUserTest : BasicUserTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "MySQL5Dialect;MySqlDataDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateUserData(); - db.Initialise(connect); - database = ((NHibernateUserData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateMySQLAssetTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateMySQLAssetTest.cs deleted file mode 100644 index 7c7b5f7864..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateMySQLAssetTest.cs +++ /dev/null @@ -1,80 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateMySQLAssetTest : BasicAssetTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "MySQL5Dialect;MySqlDataDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateAssetData(); - db.Initialise(connect); - database = ((NHibernateAssetData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateMySQLGridTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateMySQLGridTest.cs deleted file mode 100644 index dbb1d14863..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateMySQLGridTest.cs +++ /dev/null @@ -1,79 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateMySQLGridTest : BasicGridTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "MySQL5Dialect;MySqlDataDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateGridData(); - db.Initialise(connect); - database = ((NHibernateGridData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateMySQLInventoryTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateMySQLInventoryTest.cs deleted file mode 100644 index 6c31a63713..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateMySQLInventoryTest.cs +++ /dev/null @@ -1,80 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateMySQLInventoryTest : BasicInventoryTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "MySQL5Dialect;MySqlDataDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateInventoryData(); - db.Initialise(connect); - database = ((NHibernateInventoryData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateMySQLRegionTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateMySQLRegionTest.cs deleted file mode 100644 index 28660d9930..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateMySQLRegionTest.cs +++ /dev/null @@ -1,81 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateMySQLRegionTest : BasicRegionTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "MySQL5Dialect;MySqlDataDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit"; - - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateRegionData(); - db.Initialise(connect); - database = ((NHibernateRegionData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateMySQLUserTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateMySQLUserTest.cs deleted file mode 100644 index d5caa3e57f..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateMySQLUserTest.cs +++ /dev/null @@ -1,79 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateMySQLUserTest : BasicUserTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "MySQL5Dialect;MySqlDataDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateUserData(); - db.Initialise(connect); - database = ((NHibernateUserData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateMySqlEstateTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateMySqlEstateTest.cs deleted file mode 100644 index 72b43206cf..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateMySqlEstateTest.cs +++ /dev/null @@ -1,80 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateMySQLEstateTest : BasicEstateTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "MySQL5Dialect;MySqlDataDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateEstateData(); - db.Initialise(connect); - database = ((NHibernateEstateData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - ((NHibernateEstateData)db).Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLAssetTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLAssetTest.cs deleted file mode 100644 index d1ec3d0c30..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLAssetTest.cs +++ /dev/null @@ -1,80 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernatePostgreSQLAssetTest : BasicAssetTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "PostgreSQLDialect;NpgsqlDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit;"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateAssetData(); - db.Initialise(connect); - database = ((NHibernateAssetData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLEstateTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLEstateTest.cs deleted file mode 100644 index fdd99ab167..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLEstateTest.cs +++ /dev/null @@ -1,80 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernatePostgreSQLEstateTest : BasicEstateTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "PostgreSQLDialect;NpgsqlDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit;"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateEstateData(); - db.Initialise(connect); - database = ((NHibernateEstateData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - ((NHibernateEstateData)db).Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLGridTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLGridTest.cs deleted file mode 100644 index fe874cbb50..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLGridTest.cs +++ /dev/null @@ -1,79 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernatePostgreSQLGridTest : BasicGridTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "PostgreSQLDialect;NpgsqlDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit;"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateGridData(); - db.Initialise(connect); - database = ((NHibernateGridData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLInventoryTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLInventoryTest.cs deleted file mode 100644 index 048d36f31e..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLInventoryTest.cs +++ /dev/null @@ -1,80 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernatePostgreSQLInventoryTest : BasicInventoryTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "PostgreSQLDialect;NpgsqlDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit;"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateInventoryData(); - db.Initialise(connect); - database = ((NHibernateInventoryData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLRegionTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLRegionTest.cs deleted file mode 100644 index 76b6fead69..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLRegionTest.cs +++ /dev/null @@ -1,80 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernatePostgreSQLRegionTest : BasicRegionTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "PostgreSQLDialect;NpgsqlDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit;"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateRegionData(); - db.Initialise(connect); - database = ((NHibernateRegionData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLUserTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLUserTest.cs deleted file mode 100644 index c437cd8e11..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernatePostgreSQLUserTest.cs +++ /dev/null @@ -1,79 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernatePostgreSQLUserTest : BasicUserTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "PostgreSQLDialect;NpgsqlDriver;Server=localhost;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit;"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateUserData(); - db.Initialise(connect); - database = ((NHibernateUserData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteAssetTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteAssetTest.cs deleted file mode 100644 index c8d6e7564e..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteAssetTest.cs +++ /dev/null @@ -1,82 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateSQLiteAssetTest : BasicAssetTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - string connect = "SQLiteDialect;SQLite20Driver;Data Source=" + Path.GetTempFileName() + ".db;Version=3"; - - db = new NHibernateAssetData(); - db.Initialise(connect); - database = ((NHibernateAssetData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteEstateTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteEstateTest.cs deleted file mode 100644 index 345db84561..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteEstateTest.cs +++ /dev/null @@ -1,82 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateSQLiteEstateTest : BasicEstateTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - string connect = "SQLiteDialect;SQLite20Driver;Data Source=" + Path.GetTempFileName() + ".db;Version=3"; - - db = new NHibernateEstateData(); - db.Initialise(connect); - database = ((NHibernateEstateData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - ((NHibernateEstateData)db).Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteGridTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteGridTest.cs deleted file mode 100644 index 24a19c9a30..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteGridTest.cs +++ /dev/null @@ -1,80 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateSQLiteGridTest : BasicGridTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - public string connect = "SQLiteDialect;SQLite20Driver;Data Source=" + Path.GetTempFileName() + ".db;Version=3"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new NHibernateGridData(); - db.Initialise(connect); - database = ((NHibernateGridData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteInventoryTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteInventoryTest.cs deleted file mode 100644 index ec15c556b1..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteInventoryTest.cs +++ /dev/null @@ -1,82 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateSQLiteInventoryTest : BasicInventoryTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - string connect = "SQLiteDialect;SQLite20Driver;Data Source=" + Path.GetTempFileName() + ".db;Version=3"; - - db = new NHibernateInventoryData(); - db.Initialise(connect); - database = ((NHibernateInventoryData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteRegionTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteRegionTest.cs deleted file mode 100644 index 692ba5ae04..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteRegionTest.cs +++ /dev/null @@ -1,82 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - [TestFixture, DatabaseTest] - public class NHibernateSQLiteRegionTest : BasicRegionTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - string connect = "SQLiteDialect;SQLite20Driver;Data Source=" + Path.GetTempFileName() + ".db;Version=3"; - - db = new NHibernateRegionData(); - db.Initialise(connect); - database = ((NHibernateRegionData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - - } -} diff --git a/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteUserTest.cs b/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteUserTest.cs deleted file mode 100644 index 32a5bbeb5e..0000000000 --- a/OpenSim/Data/NHibernate/Tests/NHibernateSQLiteUserTest.cs +++ /dev/null @@ -1,82 +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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.NHibernate.Tests -{ - - [TestFixture, DatabaseTest] - public class NHibernateSQLiteUserTest : BasicUserTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public NHibernateManager database; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - string connect = "SQLiteDialect;SQLite20Driver;Data Source=" + Path.GetTempFileName() + ".db;Version=3"; - - db = new NHibernateUserData(); - db.Initialise(connect); - database = ((NHibernateUserData)db).Manager; - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - if (database != null) - { - database.DropSchema(); - } - } - } -} diff --git a/OpenSim/Data/NHibernate/TextureUserType.cs b/OpenSim/Data/NHibernate/TextureUserType.cs deleted file mode 100644 index 57a5296edc..0000000000 --- a/OpenSim/Data/NHibernate/TextureUserType.cs +++ /dev/null @@ -1,115 +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.Data; -using NHibernate; -using NHibernate.SqlTypes; -using NHibernate.UserTypes; -using OpenMetaverse; -using OpenSim.Framework; - -namespace OpenSim.Data.NHibernate -{ - [Serializable] - public class TextureUserType: IUserType - { - public object Assemble(object cached, object owner) - { - return cached; - } - - bool IUserType.Equals(object texture1, object texture2) - { - return texture1.Equals(texture2); - } - - public object DeepCopy(object texture) - { - if (texture == null) - { - // TODO: should parametrize this texture out - return new Primitive.TextureEntry(new UUID(Constants.DefaultTexture)); - } - else - { - byte[] bytes = ((Primitive.TextureEntry)texture).GetBytes(); - return new Primitive.TextureEntry(bytes, 0, bytes.Length); - } - } - - public object Disassemble(object texture) - { - return texture; - } - - public int GetHashCode(object texture) - { - return (texture == null) ? 0 : texture.GetHashCode(); - } - - public bool IsMutable - { - get { return false; } - } - - public object NullSafeGet(IDataReader rs, string[] names, object owner) - { - object texture = null; - - int ord = rs.GetOrdinal(names[0]); - if (!rs.IsDBNull(ord)) - { - byte[] bytes = (byte[])rs[ord]; - texture = new Primitive.TextureEntry(bytes, 0, bytes.Length); - } - - return texture; - } - - public void NullSafeSet(IDbCommand cmd, object obj, int index) - { - Primitive.TextureEntry texture = (Primitive.TextureEntry)obj; - ((IDataParameter)cmd.Parameters[index]).Value = texture.GetBytes(); - } - - public object Replace(object original, object target, object owner) - { - return original; - } - - public Type ReturnedType - { - get { return typeof(Primitive.TextureEntry); } - } - - public SqlType[] SqlTypes - { - get { return new SqlType [] { NHibernateUtil.Binary.SqlType }; } - } - } -} diff --git a/OpenSim/Data/NHibernate/UInt16Type.cs b/OpenSim/Data/NHibernate/UInt16Type.cs deleted file mode 100644 index 8e17463628..0000000000 --- a/OpenSim/Data/NHibernate/UInt16Type.cs +++ /dev/null @@ -1,103 +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.Data; -using NHibernate; -using NHibernate.SqlTypes; -using NHibernate.UserTypes; - -namespace OpenSim.Data.NHibernate -{ - [Serializable] - public class UInt16Type : IUserType - { - public object Assemble(object cached, object owner) - { - return cached; - } - - bool IUserType.Equals(object uint1, object uint2) - { - return uint1.Equals(uint2); - } - - public object DeepCopy(object uint1) - { - return uint1; - } - - public object Disassemble(object uint1) - { - return uint1; - } - - public int GetHashCode(object uint1) - { - return (uint1 == null) ? 0 : uint1.GetHashCode(); - } - - public bool IsMutable - { - get { return false; } - } - - public object NullSafeGet(IDataReader rs, string[] names, object owner) - { - object uint1 = null; - - int ord = rs.GetOrdinal(names[0]); - if (!rs.IsDBNull(ord)) - { - uint1 = (UInt16)rs.GetInt32(ord); - } - - return uint1; - } - - public void NullSafeSet(IDbCommand cmd, object obj, int index) - { - UInt16 uint1 = (UInt16)obj; - ((IDataParameter)cmd.Parameters[index]).Value = Convert.ToInt32(uint1); - } - - public object Replace(object original, object target, object owner) - { - return original; - } - - public Type ReturnedType - { - get { return typeof(UInt16); } - } - - public SqlType[] SqlTypes - { - get { return new SqlType [] { NHibernateUtil.Int32.SqlType }; } - } - } -} diff --git a/OpenSim/Data/NHibernate/UInt32Type.cs b/OpenSim/Data/NHibernate/UInt32Type.cs deleted file mode 100644 index 6b1cb36838..0000000000 --- a/OpenSim/Data/NHibernate/UInt32Type.cs +++ /dev/null @@ -1,103 +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.Data; -using NHibernate; -using NHibernate.SqlTypes; -using NHibernate.UserTypes; - -namespace OpenSim.Data.NHibernate -{ - [Serializable] - public class UInt32Type : IUserType - { - public object Assemble(object cached, object owner) - { - return cached; - } - - bool IUserType.Equals(object uint1, object uint2) - { - return uint1.Equals(uint2); - } - - public object DeepCopy(object uint1) - { - return uint1; - } - - public object Disassemble(object uint1) - { - return uint1; - } - - public int GetHashCode(object uint1) - { - return (uint1 == null) ? 0 : uint1.GetHashCode(); - } - - public bool IsMutable - { - get { return false; } - } - - public object NullSafeGet(IDataReader rs, string[] names, object owner) - { - object uint1 = null; - - int ord = rs.GetOrdinal(names[0]); - if (!rs.IsDBNull(ord)) - { - uint1 = (UInt32)rs.GetInt32(ord); - } - - return uint1; - } - - public void NullSafeSet(IDbCommand cmd, object obj, int index) - { - UInt32 uint1 = (UInt32)obj; - ((IDataParameter)cmd.Parameters[index]).Value = Convert.ToInt32(uint1); - } - - public object Replace(object original, object target, object owner) - { - return original; - } - - public Type ReturnedType - { - get { return typeof(UInt32); } - } - - public SqlType[] SqlTypes - { - get { return new SqlType [] { NHibernateUtil.Int32.SqlType }; } - } - } -} diff --git a/OpenSim/Data/NHibernate/UInt64Type.cs b/OpenSim/Data/NHibernate/UInt64Type.cs deleted file mode 100644 index 648436707c..0000000000 --- a/OpenSim/Data/NHibernate/UInt64Type.cs +++ /dev/null @@ -1,103 +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.Data; -using NHibernate; -using NHibernate.SqlTypes; -using NHibernate.UserTypes; - -namespace OpenSim.Data.NHibernate -{ - [Serializable] - public class UInt64Type : IUserType - { - public object Assemble(object cached, object owner) - { - return cached; - } - - bool IUserType.Equals(object uint1, object uint2) - { - return uint1.Equals(uint2); - } - - public object DeepCopy(object uint1) - { - return uint1; - } - - public object Disassemble(object uint1) - { - return uint1; - } - - public int GetHashCode(object uint1) - { - return (uint1 == null) ? 0 : uint1.GetHashCode(); - } - - public bool IsMutable - { - get { return false; } - } - - public object NullSafeGet(IDataReader rs, string[] names, object owner) - { - object uint1 = null; - - int ord = rs.GetOrdinal(names[0]); - if (!rs.IsDBNull(ord)) - { - uint1 = (UInt64)rs.GetInt64(ord); - } - - return uint1; - } - - public void NullSafeSet(IDbCommand cmd, object obj, int index) - { - UInt64 uint1 = (UInt64)obj; - ((IDataParameter)cmd.Parameters[index]).Value = Convert.ToInt64(uint1); - } - - public object Replace(object original, object target, object owner) - { - return original; - } - - public Type ReturnedType - { - get { return typeof(UInt64); } - } - - public SqlType[] SqlTypes - { - get { return new SqlType [] { NHibernateUtil.Int64.SqlType }; } - } - } -} diff --git a/OpenSim/Data/NHibernate/UserFriend.cs b/OpenSim/Data/NHibernate/UserFriend.cs deleted file mode 100644 index f0868a1657..0000000000 --- a/OpenSim/Data/NHibernate/UserFriend.cs +++ /dev/null @@ -1,72 +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 OpenMetaverse; - -namespace OpenSim.Data.NHibernate -{ - public class UserFriend - { - public UserFriend() - { - } - - public UserFriend(UUID userFriendID, UUID ownerID, UUID friendID, uint friendPermissions) - { - this.UserFriendID = userFriendID; - this.OwnerID = ownerID; - this.FriendID = friendID; - this.FriendPermissions = friendPermissions; - } - - private UUID userFriendId; - public UUID UserFriendID - { - get { return userFriendId; } - set { userFriendId = value; } - } - private UUID ownerId; - public UUID OwnerID - { - get { return ownerId; } - set { ownerId = value; } - } - private UUID friendId; - public UUID FriendID - { - get { return friendId; } - set { friendId = value; } - } - private uint friendPermissions; - public uint FriendPermissions - { - get { return friendPermissions; } - set { friendPermissions = value; } - } - - } -} diff --git a/OpenSim/Data/Tests/BasicRegionTest.cs b/OpenSim/Data/Tests/BasicRegionTest.cs index 97990e1b72..60a8874adc 100644 --- a/OpenSim/Data/Tests/BasicRegionTest.cs +++ b/OpenSim/Data/Tests/BasicRegionTest.cs @@ -323,7 +323,6 @@ namespace OpenSim.Data.Tests sop.ObjectFlags = 0; SceneObjectGroup sog = new SceneObjectGroup(sop); - sog.SetScene(scene); // Reguired by nhibernate database module. // Inserts group in DB db.StoreObject(sog,region3); diff --git a/OpenSim/Data/Tests/DataTestUtil.cs b/OpenSim/Data/Tests/DataTestUtil.cs index f781ea6e38..d211ab3b40 100644 --- a/OpenSim/Data/Tests/DataTestUtil.cs +++ b/OpenSim/Data/Tests/DataTestUtil.cs @@ -39,7 +39,7 @@ namespace OpenSim.Data.Tests public class DataTestUtil { public const uint UNSIGNED_INTEGER_MIN = uint.MinValue; - public const uint UNSIGNED_INTEGER_MAX = uint.MaxValue / 2; // NHibernate does not support unsigned integer range. + public const uint UNSIGNED_INTEGER_MAX = uint.MaxValue; public const int INTEGER_MIN = int.MinValue + 1; // Postgresql requires +1 to .NET int.MinValue public const int INTEGER_MAX = int.MaxValue; diff --git a/OpenSim/Framework/Communications/RestClient.cs b/OpenSim/Framework/Communications/RestClient.cs index a74169eb49..317b3a0946 100644 --- a/OpenSim/Framework/Communications/RestClient.cs +++ b/OpenSim/Framework/Communications/RestClient.cs @@ -403,7 +403,7 @@ namespace OpenSim.Framework.Communications /// In case, we are invoked asynchroneously this object will keep track of the state /// AsyncResult ar = new AsyncResult(callback, state); - ThreadPool.QueueUserWorkItem(RequestHelper, ar); + ThreadPool.UnsafeQueueUserWorkItem(RequestHelper, ar); return ar; } diff --git a/OpenSim/Framework/Parallel.cs b/OpenSim/Framework/Parallel.cs index cf4f773147..ab2e108353 100644 --- a/OpenSim/Framework/Parallel.cs +++ b/OpenSim/Framework/Parallel.cs @@ -66,7 +66,7 @@ namespace OpenSim.Framework for (int i = 0; i < threadCount; i++) { - ThreadPool.QueueUserWorkItem( + ThreadPool.UnsafeQueueUserWorkItem( delegate(object o) { int threadIndex = (int)o; @@ -122,7 +122,7 @@ namespace OpenSim.Framework for (int i = 0; i < threadCount; i++) { - ThreadPool.QueueUserWorkItem( + ThreadPool.UnsafeQueueUserWorkItem( delegate(object o) { int threadIndex = (int)o; @@ -178,7 +178,7 @@ namespace OpenSim.Framework for (int i = 0; i < threadCount; i++) { - ThreadPool.QueueUserWorkItem( + ThreadPool.UnsafeQueueUserWorkItem( delegate(object o) { int threadIndex = (int)o; diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index 942fed94d9..85d7be2b99 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -1609,7 +1609,7 @@ namespace OpenSim.Framework.Servers.HttpServer //while (true) //{ // context = m_httpListener.GetContext(); - // ThreadPool.QueueUserWorkItem(new WaitCallback(HandleRequest), context); + // ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(HandleRequest), context); // } } catch (Exception e) diff --git a/OpenSim/Grid/MessagingServer.Modules/MessageService.cs b/OpenSim/Grid/MessagingServer.Modules/MessageService.cs index df5eaab636..49f0fa8e09 100644 --- a/OpenSim/Grid/MessagingServer.Modules/MessageService.cs +++ b/OpenSim/Grid/MessagingServer.Modules/MessageService.cs @@ -149,7 +149,7 @@ namespace OpenSim.Grid.MessagingServer.Modules friendlistupdater.OnGetRegionData += m_regionModule.GetRegionInfo; friendlistupdater.OnDone += PresenceUpdateDone; WaitCallback cb = new WaitCallback(friendlistupdater.go); - ThreadPool.QueueUserWorkItem(cb); + ThreadPool.UnsafeQueueUserWorkItem(cb, null); } else { diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index b0278825b2..a966b4262e 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -791,7 +791,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// heightmap public virtual void SendLayerData(float[] map) { - ThreadPool.QueueUserWorkItem(DoSendLayerData, map); + ThreadPool.UnsafeQueueUserWorkItem(DoSendLayerData, map); } /// @@ -912,7 +912,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// 16x16 array of wind speeds public virtual void SendWindData(Vector2[] windSpeeds) { - ThreadPool.QueueUserWorkItem(new WaitCallback(DoSendWindData), (object)windSpeeds); + ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(DoSendWindData), (object)windSpeeds); } /// @@ -921,7 +921,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// 16x16 array of cloud densities public virtual void SendCloudData(float[] cloudDensity) { - ThreadPool.QueueUserWorkItem(new WaitCallback(DoSendCloudData), (object)cloudDensity); + ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(DoSendCloudData), (object)cloudDensity); } /// diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index 5ca4178ad9..d45c35c351 100644 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs @@ -313,11 +313,11 @@ namespace Flotsam.RegionModules.AssetCache } - ThreadPool.QueueUserWorkItem( + ThreadPool.UnsafeQueueUserWorkItem( delegate { WriteFileCache(filename, asset); - } + }, null ); } } diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs index 4fb4c5149f..126058454c 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs @@ -1095,7 +1095,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap // The reason is so we don't cause the thread to freeze waiting // for the 1 second it costs to start a thread manually. if (!threadrunning) - ThreadPool.QueueUserWorkItem(new WaitCallback(this.StartThread)); + ThreadPool.UnsafeQueueUserWorkItem(this.StartThread, null); lock (m_rootAgents) { diff --git a/bin/AssetInventoryServer.ini.example b/bin/AssetInventoryServer.ini.example index 20e7ba654b..8cfc1f8a68 100644 --- a/bin/AssetInventoryServer.ini.example +++ b/bin/AssetInventoryServer.ini.example @@ -104,11 +104,9 @@ frontends = ReferenceFrontend,OpenSimAssetFrontend,OpenSimInventoryFrontend,Brow ; supported by OpenSim is supported. asset_database_provider = "OpenSim.Data.SQLite.dll" ;asset_database_provider = "OpenSim.Data.MySQL.dll" -;asset_database_provider = "OpenSim.Data.NHibernate.dll" inventory_database_provider = "OpenSim.Data.SQLite.dll" ;inventory_database_provider = "OpenSim.Data.MySQL.dll" -;inventory_database_provider = "OpenSim.Data.NHibernate.dll" ; Database connection string used by the database backend. @@ -119,7 +117,3 @@ inventory_database_connect = "URI=file:Inventory.db,version=3" ; For MySQL ;asset_database_connect = "Server=localhost; Database=opensim; User=changeme; Password=changeme;" ;inventory_database_connect = "Server=localhost; Database=opensim; User=changeme; Password=changeme;" - -; For NHibernate -;asset_database_connect = "SQLiteDialect;SQLite20Driver;Data Source=file:Asset.db;Version=3" -;inventory_database_connect = "SQLiteDialect;SQLite20Driver;Data Source=file:Asset.db;Version=3" diff --git a/bin/NHibernate.Mapping.Attributes.dll b/bin/NHibernate.Mapping.Attributes.dll deleted file mode 100644 index 4e965a423d52f39b1f8e61d3e49d552153d00108..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 231936 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 BJS6}C diff --git a/bin/NHibernate.dll b/bin/NHibernate.dll deleted file mode 100644 index 392852e700f7bcaf708edbfdc8dea4945d6aa605..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1638400 zcmeFad7NBTng4(L-l|*GRo$Jgbf-HZBpsH7qN_XX(uBnzvd9_`B8v#3AgdRv5*Ny9 zqT&v?j!RHM1os8^4IM??#x1CWjz9$WWn9N`bR6;b{ygX0TerG9z&PLeegF89SGw-K zXL-(Zp7U(yIp>~x!kI4*q96zg{Qd23LGUrI`nTSGANXer*+Ubb7z#er|FvB{)_45Z zb~*LJi(0ELZzfM^KJl_uPkQ20pPFo1b>8`_nwy`x>Y}Hvdh|(;U3FRVJo!wzqi5dQXWJ^7@c zbNDj)wf_0`z;pL%cdjhIxBqHs@Hl&-UIrw~rk!QMK<@D&->wP*dXW7q^PF{%)U}(= zf958xyMNvHOMNvwvnuNPxoexP=983UxVgRs_fj_ZZ#@KA+dTi$go1`EI2)|r&MWt* zT(yU9AEv;=6nK~d4^!Y_3Or1Khbizd1s zxcqH{LBFLk1>e6CJg3$dY*-ZpTYTCBU2ZI{`S{xR{P4Xmc=n1TpZwVI=-5AWZti>8 zHDCH`U-Q7<-FeHcN1t`yX~UfZUv$y=kJ~W$=M$3?Z@TiUHy(fKAO5R8eB+|8tv&0@ z*T41J=X~~@mptP5yO+G~ldrh?Cwm-z_spGtc>If>z4Db8?|;pgKL3N!YD`O-7dl@!0)+BZXRFdMpUrs;~6zqd}Y;P3bj3 zlpL`rh_l3lco-;;`Z|lKqlU@}M5XZ9U}M}Bz7GifTa(9F`~Bmm5B3X$Qm?(}{(?1l zXHXhHy;>@y3F|KU+u!~c#;s$i(;)^>2quH&^zm452eF6N$3p8ks#hG=)l$+ge1meb zoYay2ncL}jtv{LO>E<&a-C(ddsvpnOrUQ6NPEe^rsa8lW~- zRjE~`I{|qq`INB4laJ(i)JxMuypd9^B=V`wuqR)wl+tgc+<-*2xK`epoCq>&f?9d} z^pSF{Tyum|we8l2lY}de>!u*v_7%1M&CslGq*|+{;LEjYvVl^;-N78P27Sa|!r+MD zu9bZ�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 z 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�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%0A}R|W383;w2--1Bq&Ni${SC7p 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

~XCh>p1iaR*(N` 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

DlO|3k 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 diff --git a/bin/OpenSim.32BitLaunch.exe.config b/bin/OpenSim.32BitLaunch.exe.config index ce6175b426..6ac0206e6c 100644 --- a/bin/OpenSim.32BitLaunch.exe.config +++ b/bin/OpenSim.32BitLaunch.exe.config @@ -23,12 +23,6 @@ - - - - - - diff --git a/bin/OpenSim.Grid.UserServer.exe.config b/bin/OpenSim.Grid.UserServer.exe.config index d73c2f4e8b..315e69d6d1 100644 --- a/bin/OpenSim.Grid.UserServer.exe.config +++ b/bin/OpenSim.Grid.UserServer.exe.config @@ -18,12 +18,6 @@ - - - - - - diff --git a/bin/OpenSim.exe.config b/bin/OpenSim.exe.config index b7fe1eaba0..c2d93c0449 100644 --- a/bin/OpenSim.exe.config +++ b/bin/OpenSim.exe.config @@ -24,12 +24,6 @@ - - - - - - diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index ba797e6948..2d56f4eaab 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -258,12 +258,9 @@ ; Inventory database provider inventory_plugin = "OpenSim.Data.SQLite.dll" ; inventory_plugin = "OpenSim.Data.MySQL.dll" - ; inventory_plugin = "OpenSim.Data.NHibernate.dll" ; for nhibernate ; Inventory source SQLite example inventory_source = "URI=file:inventoryStore.db,version=3" - ; Inventory Source NHibernate example (DIALECT;DRIVER;CONNECTSTRING) - ; inventory_source = "SQLiteDialect;SqliteClientDriver;URI=file:Inventory.db,version=3" ; Inventory Source MySQL example ;inventory_source = "Data Source=localhost;Database=opensim;User ID=opensim;Password=****;" @@ -277,12 +274,9 @@ ; userDatabase_plugin = "OpenSim.Data.SQLite.dll" ; userDatabase_plugin = "OpenSim.Data.MySQL.dll" - ; userDatabase_plugin = "OpenSim.Data.NHibernate.dll" ; for nhibernate ; User source SQLite example user_source = "URI=file:userprofiles.db,version=3" - ; User Source NHibernate Example (DIALECT;DRIVER;CONNECTSTRING) - ; user_source = "SQLiteDialect;SqliteClientDriver;URI=file:User.db,version=3" ; User Source MySQL example ;user_source = "Data Source=localhost;Database=opensim;User ID=opensim;Password=****;" diff --git a/prebuild.xml b/prebuild.xml index 1a491a7cd7..e17da9ad65 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2387,46 +2387,6 @@ - - - - ../../../bin/ - - - - - ../../../bin/ - - - - ../../../bin/ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3368,45 +3328,6 @@ - - - - ../../../../bin/ - - - - - ../../../../bin/ - - - - ../../../../bin/ - - - - - - - - - - - - - - - - - - - - - - - - - - From 0a6ea33ac8faa015ad8014b429be0046ba3b3e0a Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Mon, 19 Oct 2009 18:50:31 -0700 Subject: [PATCH 29/61] * Optimized sending of terrain data * Send terrain data in a spiral pattern instead of a typewriter pattern (placeholder until terrain data becomes part of the interest list management) * Added a debug line when resent packets are being sent --- .../ClientStack/LindenUDP/LLClientView.cs | 312 ++++++++++-------- .../ClientStack/LindenUDP/LLUDPServer.cs | 23 +- 2 files changed, 180 insertions(+), 155 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index a966b4262e..88faccf09f 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -45,7 +45,7 @@ using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes.Hypergrid; using OpenSim.Services.Interfaces; -using Timer=System.Timers.Timer; +using Timer = System.Timers.Timer; using AssetLandmark = OpenSim.Framework.AssetLandmark; using Nini.Config; @@ -342,7 +342,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected UUID m_activeGroupID; protected string m_activeGroupName = String.Empty; protected ulong m_activeGroupPowers; - protected Dictionary m_groupPowers = new Dictionary(); + protected Dictionary m_groupPowers = new Dictionary(); protected int m_terrainCheckerCount; protected uint m_agentFOVCounter; @@ -621,7 +621,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } // Sound - public void SoundTrigger(UUID soundId, UUID owerid, UUID Objectid,UUID ParentId,float Gain, Vector3 Position,UInt64 Handle) + public void SoundTrigger(UUID soundId, UUID owerid, UUID Objectid, UUID ParentId, float Gain, Vector3 Position, UInt64 Handle) { } @@ -658,7 +658,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP handshake.RegionInfo.CacheID = UUID.Random(); //I guess this is for the client to remember an old setting? handshake.RegionInfo2 = new RegionHandshakePacket.RegionInfo2Block(); handshake.RegionInfo2.RegionID = regionInfo.RegionID; - + handshake.RegionInfo3 = new RegionHandshakePacket.RegionInfo3Block(); handshake.RegionInfo3.CPUClassID = 9; handshake.RegionInfo3.CPURatio = 1; @@ -800,32 +800,55 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// private void DoSendLayerData(object o) { - float[] map = (float[])o; + float[] map = LLHeightFieldMoronize((float[])o); try { - for (int y = 0; y < 16; y++) - { - // For some terrains, sending more than one terrain patch at once results in a libsecondlife exception - // see http://opensimulator.org/mantis/view.php?id=1662 - //for (int x = 0; x < 16; x += 4) - //{ - // SendLayerPacket(map, y, x); - // Thread.Sleep(150); - //} - for (int x = 0; x < 16; x++) - { - SendLayerData(x, y, LLHeightFieldMoronize(map)); - Thread.Sleep(35); - } - } + //for (int y = 0; y < 16; y++) + //{ + // for (int x = 0; x < 16; x++) + // { + // SendLayerData(x, y, map); + // } + //} + + // Send LayerData in a spiral pattern. Fun! + SendLayerTopRight(map, 0, 0, 15, 15); } catch (Exception e) { - m_log.Warn("[CLIENT]: ClientView.API.cs: SendLayerData() - Failed with exception " + e); + m_log.Error("[CLIENT]: SendLayerData() Failed with exception: " + e.Message, e); } } + private void SendLayerTopRight(float[] map, int x1, int y1, int x2, int y2) + { + // Row + for (int i = x1; i <= x2; i++) + SendLayerData(i, y1, map); + + // Column + for (int j = y1 + 1; j <= y2; j++) + SendLayerData(x2, j, map); + + if (x2 - x1 > 0) + SendLayerBottomLeft(map, x1, y1 + 1, x2 - 1, y2); + } + + void SendLayerBottomLeft(float[] map, int x1, int y1, int x2, int y2) + { + // Row in reverse + for (int i = x2; i >= x1; i--) + SendLayerData(i, y2, map); + + // Column in reverse + for (int j = y2 - 1; j >= y1; j--) + SendLayerData(x1, j, map); + + if (x2 - x1 > 0) + SendLayerTopRight(map, x1 + 1, y1, x2, y2 - 1); + } + ///

/// Sends a set of four patches (x, x+1, ..., x+3) to the client /// @@ -854,22 +877,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP { try { - int[] patches = new int[1]; - int patchx, patchy; - patchx = px; - patchy = py; + int[] patches = new int[] { py * 16 + px }; + float[] heightmap = (map.Length == 65536) ? + map : + LLHeightFieldMoronize(map); - patches[0] = patchx + 0 + patchy * 16; - - LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(((map.Length==65536)? map : LLHeightFieldMoronize(map)), patches); - layerpack.Header.Zerocoded = true; + LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches); + layerpack.Header.Reliable = true; OutPacket(layerpack, ThrottleOutPacketType.Land); - } catch (Exception e) { - m_log.Warn("[client]: ClientView.API.cs: SendLayerData() - Failed with exception " + e.ToString()); + m_log.Error("[CLIENT]: SendLayerData() Failed with exception: " + e.Message, e); } } @@ -898,7 +918,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, 256); } - //Array.Copy(map,0,returnmap,0,(map.Length < 65536)? map.Length : 65536); return returnmap; @@ -1010,14 +1029,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP agentData.child = false; agentData.firstname = m_firstName; agentData.lastname = m_lastName; - + ICapabilitiesModule capsModule = m_scene.RequestModuleInterface(); - + if (capsModule == null) // can happen when shutting down. return agentData; agentData.CapsPath = capsModule.GetCapsPath(m_agentId); - agentData.ChildrenCapSeeds = new Dictionary(capsModule.GetChildrenSeeds(m_agentId)); + agentData.ChildrenCapSeeds = new Dictionary(capsModule.GetChildrenSeeds(m_agentId)); return agentData; } @@ -1213,7 +1232,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP pc.PingID.PingID = seq; pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0; - + OutPacket(pc, ThrottleOutPacketType.Unknown); } @@ -1519,7 +1538,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP inventoryReply.Header.Zerocoded = true; OutPacket(inventoryReply, ThrottleOutPacketType.Asset); } - + protected void SendBulkUpdateInventoryFolder(InventoryFolderBase folderBase) { // We will use the same transaction id for all the separate packets to be sent out in this update. @@ -1543,7 +1562,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP bulkUpdate.FolderData = folderDataBlocks.ToArray(); List foo = new List(); bulkUpdate.ItemData = foo.ToArray(); - + //m_log.Debug("SendBulkUpdateInventory :" + bulkUpdate); OutPacket(bulkUpdate, ThrottleOutPacketType.Asset); } @@ -1666,7 +1685,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return itemBlock; } - + public void SendBulkUpdateInventory(InventoryNodeBase node) { if (node is InventoryItemBase) @@ -1676,7 +1695,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP else m_log.ErrorFormat("[CLIENT]: Client for {0} sent unknown inventory node named {1}", Name, node.Name); } - + protected void SendBulkUpdateInventoryItem(InventoryItemBase item) { const uint FULL_MASK_PERMISSIONS = (uint)PermissionMask.All; @@ -3100,9 +3119,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP awb.ItemID = wearables[i].ItemID; aw.WearableData[i] = awb; -// m_log.DebugFormat( -// "[APPEARANCE]: Sending wearable item/asset {0} {1} (index {2}) for {3}", -// awb.ItemID, awb.AssetID, i, Name); + // m_log.DebugFormat( + // "[APPEARANCE]: Sending wearable item/asset {0} {1} (index {2}) for {3}", + // awb.ItemID, awb.AssetID, i, Name); } OutPacket(aw, ThrottleOutPacketType.Task); @@ -3270,7 +3289,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP objupdate.RegionData.RegionHandle = data.RegionHandle; objupdate.RegionData.TimeDilation = ushort.MaxValue; - + objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; objupdate.ObjectData[0] = CreateAvatarUpdateBlock(data); @@ -3343,7 +3362,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { CoarseLocationUpdatePacket.LocationBlock lb = new CoarseLocationUpdatePacket.LocationBlock(); - + lb.X = (byte)CoarseLocations[i].X; lb.Y = (byte)CoarseLocations[i].Y; @@ -3620,7 +3639,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP newPack.Header.Zerocoded = true; OutPacket(newPack, ThrottleOutPacketType.Asset); } - + public void SendInitiateDownload(string simFileName, string clientFileName) { InitiateDownloadPacket newPack = new InitiateDownloadPacket(); @@ -3629,7 +3648,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP newPack.FileData.ViewerFilename = Utils.StringToBytes(clientFileName); OutPacket(newPack, ThrottleOutPacketType.Asset); } - + public void SendImageFirstPart( ushort numParts, UUID ImageUUID, uint ImageSize, byte[] ImageData, byte imageCodec) { @@ -3817,7 +3836,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendBannedUserList(UUID invoice, EstateBan[] bl, uint estateID) { - ListBannedUsers = new List(); + List BannedUsers = new List(); for (int i = 0; i < bl.Length; i++) { @@ -3881,7 +3900,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP rinfoblk.UseEstateSun = args.useEstateSun; rinfoblk.WaterHeight = args.waterHeight; rinfoblk.SimName = Utils.StringToBytes(args.simName); - + rinfopack.RegionInfo2 = new RegionInfoPacket.RegionInfo2Block(); rinfopack.RegionInfo2.HardMaxAgents = uint.MaxValue; rinfopack.RegionInfo2.HardMaxObjects = uint.MaxValue; @@ -4128,7 +4147,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendLandObjectOwners(LandData land, List groups, Dictionary ownersAndCount) { - + int notifyCount = ownersAndCount.Count; ParcelObjectOwnersReplyPacket pack = (ParcelObjectOwnersReplyPacket)PacketPool.Instance.GetPacket(PacketType.ParcelObjectOwnersReply); @@ -4208,7 +4227,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (collisionPlane == Vector4.Zero) collisionPlane = Vector4.UnitW; - + collisionPlane.ToBytes(data, pos); pos += 16; } @@ -4297,7 +4316,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP update.TextureAnim = Utils.EmptyBytes; update.TextureEntry = data.TextureEntry ?? Utils.EmptyBytes; update.UpdateFlags = 61 + (9 << 8) + (130 << 16) + (16 << 24); // TODO: Replace these numbers with PrimFlags - + return update; } @@ -4448,7 +4467,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private bool HandleMoneyTransferRequest(IClientAPI sender, Packet Pack) { - MoneyTransferRequestPacket money = (MoneyTransferRequestPacket) Pack; + MoneyTransferRequestPacket money = (MoneyTransferRequestPacket)Pack; // validate the agent owns the agentID and sessionID if (money.MoneyData.SourceID == sender.AgentId && money.AgentData.AgentID == sender.AgentId && money.AgentData.SessionID == sender.SessionId) @@ -4469,7 +4488,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private bool HandleParcelBuyRequest(IClientAPI sender, Packet Pack) { - ParcelBuyPacket parcel = (ParcelBuyPacket) Pack; + ParcelBuyPacket parcel = (ParcelBuyPacket)Pack; if (parcel.AgentData.AgentID == AgentId && parcel.AgentData.SessionID == SessionId) { ParcelBuy handlerParcelBuy = OnParcelBuy; @@ -4489,7 +4508,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private bool HandleUUIDGroupNameRequest(IClientAPI sender, Packet Pack) { UUIDGroupNameRequestPacket upack = (UUIDGroupNameRequestPacket)Pack; - + for (int i = 0; i < upack.UUIDNameBlock.Length; i++) { @@ -4505,7 +4524,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public bool HandleGenericMessage(IClientAPI sender, Packet pack) { - GenericMessagePacket gmpack = (GenericMessagePacket) pack; + GenericMessagePacket gmpack = (GenericMessagePacket)pack; if (m_genericPacketHandlers.Count == 0) return false; if (gmpack.AgentData.SessionID != SessionId) return false; @@ -4642,7 +4661,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { if (((LogoutRequestPacket)packet).AgentData.SessionID != SessionId) return false; } - + return Logout(client); } @@ -4700,7 +4719,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP cachedresp.Header.Zerocoded = true; OutPacket(cachedresp, ThrottleOutPacketType.Task); - + return true; } @@ -4750,7 +4769,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP UpdatePrimSingleRotation handlerUpdatePrimSingleRotation = OnUpdatePrimSingleRotation; if (handlerUpdatePrimSingleRotation != null) { - // m_log.Info("new tab rotation is " + rot1.X + " , " + rot1.Y + " , " + rot1.Z + " , " + rot1.W); + // m_log.Info("new tab rotation is " + rot1.X + " , " + rot1.Y + " , " + rot1.Z + " , " + rot1.W); handlerUpdatePrimSingleRotation(localId, rot1, this); } break; @@ -4761,8 +4780,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP UpdatePrimSingleRotationPosition handlerUpdatePrimSingleRotationPosition = OnUpdatePrimSingleRotationPosition; if (handlerUpdatePrimSingleRotationPosition != null) { - // m_log.Debug("new mouse rotation position is " + rotPos.X + " , " + rotPos.Y + " , " + rotPos.Z); - // m_log.Info("new mouse rotation is " + rot2.X + " , " + rot2.Y + " , " + rot2.Z + " , " + rot2.W); + // m_log.Debug("new mouse rotation position is " + rotPos.X + " , " + rotPos.Y + " , " + rotPos.Z); + // m_log.Info("new mouse rotation is " + rot2.X + " , " + rot2.Y + " , " + rot2.Z + " , " + rot2.W); handlerUpdatePrimSingleRotationPosition(localId, rot2, rotPos, this); } break; @@ -4773,7 +4792,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP UpdateVector handlerUpdatePrimScale = OnUpdatePrimScale; if (handlerUpdatePrimScale != null) { -// m_log.Debug("new scale is " + scale4.X + " , " + scale4.Y + " , " + scale4.Z); + // m_log.Debug("new scale is " + scale4.X + " , " + scale4.Y + " , " + scale4.Z); handlerUpdatePrimScale(localId, scale4, this); } break; @@ -4812,7 +4831,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP UpdatePrimRotation handlerUpdatePrimRotation = OnUpdatePrimGroupRotation; if (handlerUpdatePrimRotation != null) { - // Console.WriteLine("new rotation is " + rot3.X + " , " + rot3.Y + " , " + rot3.Z + " , " + rot3.W); + // Console.WriteLine("new rotation is " + rot3.X + " , " + rot3.Y + " , " + rot3.Z + " , " + rot3.W); handlerUpdatePrimRotation(localId, rot3, this); } break; @@ -4823,8 +4842,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP handlerUpdatePrimGroupRotation = OnUpdatePrimGroupMouseRotation; if (handlerUpdatePrimGroupRotation != null) { - // m_log.Debug("new rotation position is " + pos.X + " , " + pos.Y + " , " + pos.Z); - // m_log.Debug("new group mouse rotation is " + rot4.X + " , " + rot4.Y + " , " + rot4.Z + " , " + rot4.W); + // m_log.Debug("new rotation position is " + pos.X + " , " + pos.Y + " , " + pos.Z); + // m_log.Debug("new group mouse rotation is " + rot4.X + " , " + rot4.Y + " , " + rot4.Z + " , " + rot4.W); handlerUpdatePrimGroupRotation(localId, pos3, rot4, this); } break; @@ -4835,7 +4854,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP UpdateVector handlerUpdatePrimGroupScale = OnUpdatePrimGroupScale; if (handlerUpdatePrimGroupScale != null) { -// m_log.Debug("new scale is " + scale7.X + " , " + scale7.Y + " , " + scale7.Z); + // m_log.Debug("new scale is " + scale7.X + " , " + scale7.Y + " , " + scale7.Z); handlerUpdatePrimGroupScale(localId, scale7, this); } break; @@ -5067,7 +5086,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Main packet processing conditional switch (Pack.Type) { - #region Scene/Avatar + #region Scene/Avatar case PacketType.AvatarPropertiesRequest: AvatarPropertiesRequestPacket avatarProperties = (AvatarPropertiesRequestPacket)Pack; @@ -5318,7 +5337,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP break; case PacketType.DeRezObject: - DeRezObjectPacket DeRezPacket = (DeRezObjectPacket) Pack; + DeRezObjectPacket DeRezPacket = (DeRezObjectPacket)Pack; #region Packet Session and User Check if (m_checkPackets) @@ -5339,13 +5358,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP { deRezIDs.Add(data.ObjectLocalID); } - // It just so happens that the values on the DeRezAction enumerator match the Destination - // values given by a Second Life client - handlerDeRezObject(this, deRezIDs, - DeRezPacket.AgentBlock.GroupID, - (DeRezAction)DeRezPacket.AgentBlock.Destination, - DeRezPacket.AgentBlock.DestinationID); - + // It just so happens that the values on the DeRezAction enumerator match the Destination + // values given by a Second Life client + handlerDeRezObject(this, deRezIDs, + DeRezPacket.AgentBlock.GroupID, + (DeRezAction)DeRezPacket.AgentBlock.Destination, + DeRezPacket.AgentBlock.DestinationID); + } break; @@ -5449,7 +5468,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - + case PacketType.AgentIsNowWearing: if (OnAvatarNowWearing != null) { @@ -5653,7 +5672,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (lastarg != null) { - update = + update = ( (x.BodyRotation != lastarg.BodyRotation) || (x.CameraAtAxis != lastarg.CameraAtAxis) || @@ -5846,7 +5865,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - + case PacketType.UserInfoRequest: UserInfoRequest handlerUserInfoRequest = OnUserInfoRequest; if (handlerUserInfoRequest != null) @@ -5858,7 +5877,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP SendUserInfoReply(false, true, ""); } break; - + case PacketType.UpdateUserInfo: UpdateUserInfoPacket updateUserInfo = (UpdateUserInfoPacket)Pack; @@ -5885,7 +5904,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP visible, this); } break; - + case PacketType.SetStartLocationRequest: SetStartLocationRequestPacket avSetStartLocationRequestPacket = (SetStartLocationRequestPacket)Pack; @@ -5943,9 +5962,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - #endregion + #endregion - #region Objects/m_sceneObjects + #region Objects/m_sceneObjects case PacketType.ObjectLink: ObjectLinkPacket link = (ObjectLinkPacket)Pack; @@ -5976,7 +5995,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP handlerLinkObjects(this, parentprimid, childrenprims); } break; - + case PacketType.ObjectDelink: ObjectDelinkPacket delink = (ObjectDelinkPacket)Pack; @@ -6005,7 +6024,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - + case PacketType.ObjectAdd: if (OnAddPrim != null) { @@ -6035,7 +6054,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP handlerAddPrim(AgentId, ActiveGroupId, addPacket.ObjectData.RayEnd, addPacket.ObjectData.Rotation, shape, addPacket.ObjectData.BypassRaycast, addPacket.ObjectData.RayStart, addPacket.ObjectData.RayTargetID, addPacket.ObjectData.RayEndIsIntersection); } break; - + case PacketType.ObjectShape: ObjectShapePacket shapePacket = (ObjectShapePacket)Pack; @@ -6080,7 +6099,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } break; - + case PacketType.ObjectExtraParams: ObjectExtraParamsPacket extraPar = (ObjectExtraParamsPacket)Pack; @@ -6096,7 +6115,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP ObjectExtraParams handlerUpdateExtraParams = OnUpdateExtraParams; if (handlerUpdateExtraParams != null) { - for (int i = 0 ; i < extraPar.ObjectData.Length ; i++) + for (int i = 0; i < extraPar.ObjectData.Length; i++) { handlerUpdateExtraParams(m_agentId, extraPar.ObjectData[i].ObjectLocalID, extraPar.ObjectData[i].ParamType, @@ -6497,7 +6516,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP break; case PacketType.ObjectName: ObjectNamePacket objName = (ObjectNamePacket)Pack; - + #region Packet Session and User Check if (m_checkPackets) { @@ -6506,7 +6525,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP break; } #endregion - + GenericCall7 handlerObjectName = null; for (int i = 0; i < objName.ObjectData.Length; i++) { @@ -6739,14 +6758,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - #endregion + #endregion - #region Inventory/Asset/Other related packets + #region Inventory/Asset/Other related packets case PacketType.RequestImage: RequestImagePacket imageRequest = (RequestImagePacket)Pack; //m_log.Debug("image request: " + Pack.ToString()); - + #region Packet Session and User Check if (m_checkPackets) { @@ -6770,7 +6789,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP //handlerTextureRequest = OnRequestTexture; //if (handlerTextureRequest != null) - //OnRequestTexture(this, args); + //OnRequestTexture(this, args); // in the end, we null this, so we have to check if it's null if (m_imageManager != null) @@ -6816,7 +6835,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (ti.OwnerID != AgentId) break; - if ((ti.CurrentPermissions & ((uint)PermissionMask.Modify| (uint)PermissionMask.Copy | (uint)PermissionMask.Transfer)) != ((uint)PermissionMask.Modify| (uint)PermissionMask.Copy | (uint)PermissionMask.Transfer)) + if ((ti.CurrentPermissions & ((uint)PermissionMask.Modify | (uint)PermissionMask.Copy | (uint)PermissionMask.Transfer)) != ((uint)PermissionMask.Modify | (uint)PermissionMask.Copy | (uint)PermissionMask.Transfer)) break; if (ti.AssetID != requestID) @@ -6874,7 +6893,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP case PacketType.AssetUploadRequest: AssetUploadRequestPacket request = (AssetUploadRequestPacket)Pack; - + // m_log.Debug("upload request " + request.ToString()); // m_log.Debug("upload request was for assetid: " + request.AssetBlock.TransactionID.Combine(this.SecureSessionId).ToString()); UUID temp = UUID.Combine(request.AssetBlock.TransactionID, SecureSessionId); @@ -6891,7 +6910,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP break; case PacketType.RequestXfer: RequestXferPacket xferReq = (RequestXferPacket)Pack; - + RequestXfer handlerRequestXfer = OnRequestXfer; if (handlerRequestXfer != null) @@ -6910,7 +6929,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP break; case PacketType.ConfirmXferPacket: ConfirmXferPacketPacket confirmXfer = (ConfirmXferPacketPacket)Pack; - + ConfirmXfer handlerConfirmXfer = OnConfirmXfer; if (handlerConfirmXfer != null) { @@ -7005,7 +7024,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP break; case PacketType.CreateInventoryItem: CreateInventoryItemPacket createItem = (CreateInventoryItemPacket)Pack; - + #region Packet Session and User Check if (m_checkPackets) { @@ -7098,7 +7117,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP break; case PacketType.UpdateInventoryItem: UpdateInventoryItemPacket inventoryItemUpdate = (UpdateInventoryItemPacket)Pack; - + #region Packet Session and User Check if (m_checkPackets) { @@ -7590,7 +7609,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - #endregion + #endregion case PacketType.UUIDNameRequest: UUIDNameRequestPacket incoming = (UUIDNameRequestPacket)Pack; @@ -7605,7 +7624,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - #region Parcel related packets + #region Parcel related packets case PacketType.RegionHandleRequest: RegionHandleRequestPacket rhrPack = (RegionHandleRequestPacket)Pack; @@ -7986,9 +8005,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - #endregion + #endregion - #region Estate Packets + #region Estate Packets case PacketType.EstateOwnerMessage: EstateOwnerMessagePacket messagePacket = (EstateOwnerMessagePacket)Pack; @@ -8022,21 +8041,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP convertParamStringToBool(messagePacket.ParamList[7].Parameter), convertParamStringToBool(messagePacket.ParamList[8].Parameter)); } break; -// case "texturebase": -// if (((Scene)m_scene).Permissions.CanIssueEstateCommand(AgentId, false)) -// { -// foreach (EstateOwnerMessagePacket.ParamListBlock block in messagePacket.ParamList) -// { -// string s = Utils.BytesToString(block.Parameter); -// string[] splitField = s.Split(' '); -// if (splitField.Length == 2) -// { -// UUID tempUUID = new UUID(splitField[1]); -// OnSetEstateTerrainBaseTexture(this, Convert.ToInt16(splitField[0]), tempUUID); -// } -// } -// } -// break; + // case "texturebase": + // if (((Scene)m_scene).Permissions.CanIssueEstateCommand(AgentId, false)) + // { + // foreach (EstateOwnerMessagePacket.ParamListBlock block in messagePacket.ParamList) + // { + // string s = Utils.BytesToString(block.Parameter); + // string[] splitField = s.Split(' '); + // if (splitField.Length == 2) + // { + // UUID tempUUID = new UUID(splitField[1]); + // OnSetEstateTerrainBaseTexture(this, Convert.ToInt16(splitField[0]), tempUUID); + // } + // } + // } + // break; case "texturedetail": if (((Scene)m_scene).Permissions.CanIssueEstateCommand(AgentId, false)) { @@ -8278,7 +8297,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.Error("EstateOwnerMessage: Unknown method requested\n" + messagePacket); break; } - + //int parcelID, uint reportType, uint requestflags, string filter //lsrp.RequestData.ParcelLocalID; @@ -8318,9 +8337,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - #endregion + #endregion - #region GodPackets + #region GodPackets case PacketType.RequestGodlikePowers: RequestGodlikePowersPacket rglpPack = (RequestGodlikePowersPacket)Pack; @@ -8366,9 +8385,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP //OutPacket(kupack, ThrottleOutPacketType.Task); break; - #endregion + #endregion - #region Economy/Transaction Packets + #region Economy/Transaction Packets case PacketType.MoneyBalanceRequest: MoneyBalanceRequestPacket moneybalancerequestpacket = (MoneyBalanceRequestPacket)Pack; @@ -8392,7 +8411,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP break; case PacketType.EconomyDataRequest: - + EconomyDataRequest handlerEconomoyDataRequest = OnEconomyDataRequest; if (handlerEconomoyDataRequest != null) { @@ -8468,9 +8487,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - #endregion + #endregion - #region Script Packets + #region Script Packets case PacketType.GetScriptRunning: GetScriptRunningPacket scriptRunning = (GetScriptRunningPacket)Pack; @@ -8520,9 +8539,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - #endregion + #endregion - #region Gesture Managment + #region Gesture Managment case PacketType.ActivateGestures: ActivateGesturesPacket activateGesturePacket = (ActivateGesturesPacket)Pack; @@ -8589,7 +8608,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - #endregion + #endregion case PacketType.AgentFOV: AgentFOVPacket fovPacket = (AgentFOVPacket)Pack; @@ -8605,7 +8624,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; - #region unimplemented handlers + #region unimplemented handlers case PacketType.ViewerStats: // TODO: handle this packet @@ -8628,8 +8647,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP MapItemRequest handlerMapItemRequest = OnMapItemRequest; if (handlerMapItemRequest != null) { - handlerMapItemRequest(this,mirpk.AgentData.Flags, mirpk.AgentData.EstateID, - mirpk.AgentData.Godlike,mirpk.RequestData.ItemType, + handlerMapItemRequest(this, mirpk.AgentData.Flags, mirpk.AgentData.EstateID, + mirpk.AgentData.Godlike, mirpk.RequestData.ItemType, mirpk.RequestData.RegionHandle); } @@ -9037,7 +9056,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP groupMembersRequestPacket.GroupData.RequestID; groupMembersReply.GroupData.MemberCount = memberCount; - for (int i = 0 ; i < blockCount ; i++) + for (int i = 0; i < blockCount; i++) { GroupMembersData m = members[0]; members.RemoveAt(0); @@ -9174,7 +9193,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP groupRoleMembersReply.MemberData = new GroupRoleMembersReplyPacket.MemberDataBlock[pairs]; - for (int i = 0 ; i < pairs ; i++) + for (int i = 0; i < pairs; i++) { GroupRoleMembersData d = mappings[0]; mappings.RemoveAt(0); @@ -9301,7 +9320,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP ParcelDeedToGroup handlerParcelDeedToGroup = OnParcelDeedToGroup; if (handlerParcelDeedToGroup != null) { - handlerParcelDeedToGroup(parcelDeedToGroup.Data.LocalID, parcelDeedToGroup.Data.GroupID,this); + handlerParcelDeedToGroup(parcelDeedToGroup.Data.LocalID, parcelDeedToGroup.Data.GroupID, this); } } @@ -9759,7 +9778,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP PickDelete handlerPickDelete = OnPickDelete; if (handlerPickDelete != null) - handlerPickDelete(this, pickDelete.Data.PickID); + handlerPickDelete(this, pickDelete.Data.PickID); break; case PacketType.PickGodDelete: PickGodDeletePacket pickGodDelete = @@ -9779,7 +9798,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP handlerPickGodDelete(this, pickGodDelete.AgentData.AgentID, pickGodDelete.Data.PickID, - pickGodDelete.Data.QueryID); + pickGodDelete.Data.QueryID); break; case PacketType.PickInfoUpdate: PickInfoUpdatePacket pickInfoUpdate = @@ -9870,7 +9889,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.Warn("[CLIENT]: unhandled packet " + Pack); break; - #endregion + #endregion } PacketPool.Instance.ReturnPacket(Pack); @@ -9907,7 +9926,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP //shape.Textures = ntex; return shape; } - + public ClientInfo GetClientInfo() { ClientInfo info = m_udpClient.GetClientInfo(); @@ -9935,7 +9954,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { ParcelMediaCommandMessagePacket commandMessagePacket = new ParcelMediaCommandMessagePacket(); commandMessagePacket.CommandBlock.Flags = flags; - commandMessagePacket.CommandBlock.Command =(uint) command; + commandMessagePacket.CommandBlock.Command = (uint)command; commandMessagePacket.CommandBlock.Time = time; OutPacket(commandMessagePacket, ThrottleOutPacketType.Unknown); @@ -9963,7 +9982,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Camera - public void SendSetFollowCamProperties (UUID objectID, SortedDictionary parameters) + public void SendSetFollowCamProperties(UUID objectID, SortedDictionary parameters) { SetFollowCamPropertiesPacket packet = (SetFollowCamPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.SetFollowCamProperties); packet.ObjectData.ObjectID = objectID; @@ -9981,7 +10000,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, ThrottleOutPacketType.Task); } - public void SendClearFollowCamProperties (UUID objectID) + public void SendClearFollowCamProperties(UUID objectID) { ClearFollowCamPropertiesPacket packet = (ClearFollowCamPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ClearFollowCamProperties); packet.ObjectData.ObjectID = objectID; @@ -10083,8 +10102,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP { return m_udpClient.GetStats(); } - - public string XReport(string uptime, string version) + + public string XReport(string uptime, string version) { return String.Empty; } @@ -10158,7 +10177,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } //m_log.DebugFormat("[ASSET CACHE]: Asset transfer request for asset which is {0} already known to be missing. Dropping", requestID); - + // FIXME: We never tell the client about assets which do not exist when requested by this transfer mechanism, which can't be right. return; } @@ -10381,7 +10400,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion #region LookupItem - private struct LookupItem { + private struct LookupItem + { internal MinHeap Heap; internal IHandle Handle; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 74175d0cee..a6ead5e054 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -372,23 +372,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void ResendUnacked(LLUDPClient udpClient) { - if (udpClient.IsConnected && udpClient.NeedAcks.Count > 0) + if (!udpClient.IsConnected) + return; + + // Disconnect an agent if no packets are received for some time + //FIXME: Make 60 an .ini setting + if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60) { - // Disconnect an agent if no packets are received for some time - //FIXME: Make 60 an .ini setting - if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60) - { - m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); + m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); - RemoveClient(udpClient); - return; - } + RemoveClient(udpClient); + return; + } + if (udpClient.NeedAcks.Count > 0) + { // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO List expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO); if (expiredPackets != null) { + m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID); + // Resend packets for (int i = 0; i < expiredPackets.Count; i++) { From 182693628ca1b81c90f3f0296418437eda406bb5 Mon Sep 17 00:00:00 2001 From: Snowcrash Date: Mon, 19 Oct 2009 13:03:14 +0200 Subject: [PATCH 30/61] Fix for index error in llList2String --- .../Shared/Api/Implementation/LSL_Api.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index e10e612296..224b3ccaaa 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -4660,7 +4660,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { index = src.Length + index; } - if (index >= src.Length) + if (index >= src.Length || index < 0) { return 0; } @@ -4685,7 +4685,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { index = src.Length + index; } - if (index >= src.Length) + if (index >= src.Length || index < 0) { return 0.0; } @@ -4712,7 +4712,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { index = src.Length + index; } - if (index >= src.Length) + if (index >= src.Length || index < 0) { return String.Empty; } @@ -4726,7 +4726,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { index = src.Length + index; } - if (index >= src.Length) + if (index >= src.Length || index < 0) { return ""; } @@ -4740,7 +4740,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { index = src.Length + index; } - if (index >= src.Length) + if (index >= src.Length || index < 0) { return new LSL_Vector(0, 0, 0); } @@ -4761,7 +4761,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { index = src.Length + index; } - if (index >= src.Length) + if (index >= src.Length || index < 0) { return new LSL_Rotation(0, 0, 0, 1); } From 26863c04a5f0fc1ed18d15b278d723468427911c Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 20 Oct 2009 14:02:11 +0100 Subject: [PATCH 31/61] Change "config save" to "config save ", which is mandatory. File name is enforced to NOT be OpenSim.ini --- OpenSim/Region/Application/OpenSim.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index ca6a2a3451..143dd2a2a0 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -628,8 +628,20 @@ namespace OpenSim break; case "save": - m_log.Info("Saving configuration file: " + Application.iniFilePath); - m_config.Save(Application.iniFilePath); + if (cmdparams.Length < 2) + { + m_log.Error("SYNTAX: " + n + " SAVE FILE"); + return; + } + + if (Application.iniFilePath == cmdparams[1]) + { + m_log.Error("FILE can not be "+Application.iniFilePath); + return; + } + + m_log.Info("Saving configuration file: " + cmdparams[1]); + m_config.Save(cmdparams[1]); break; } } From 9bc303d2937ab2f62e146f7fdde6444ed8cb50c9 Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 20 Oct 2009 16:57:22 +0100 Subject: [PATCH 32/61] Add MainServer.GetHttpServer(port) method for using multiple listener ports in region modules --- OpenSim/Framework/MainServer.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/OpenSim/Framework/MainServer.cs b/OpenSim/Framework/MainServer.cs index b5f947e4a6..21033b3809 100644 --- a/OpenSim/Framework/MainServer.cs +++ b/OpenSim/Framework/MainServer.cs @@ -26,17 +26,34 @@ */ using OpenSim.Framework.Servers.HttpServer; +using System.Collections.Generic; namespace OpenSim.Framework { public class MainServer { private static BaseHttpServer instance; + private static Dictionary m_Servers = + new Dictionary(); public static BaseHttpServer Instance { get { return instance; } set { instance = value; } } + + public IHttpServer GetHttpServer(uint port) + { + if (port == Instance.Port) + return Instance; + + if (m_Servers.ContainsKey(port)) + return m_Servers[port]; + + m_Servers[port] = new BaseHttpServer(port); + m_Servers[port].Start(); + + return m_Servers[port]; + } } } From 9a5e7222cef7e81fee0c992c4557ac56e9665808 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 20 Oct 2009 10:33:23 -0700 Subject: [PATCH 33/61] * Removing cruft left over from the conversion to the new texture sending and UDP code * Changing the cache modules to only initialize the caches if they are actually enabled. Should save a bit of resources from unused cache systems --- .../ClientStack/ClientStackUserSettings.cs | 48 --- .../Region/ClientStack/ThrottleSettings.cs | 57 ---- .../TextureDownload/TextureDownloadModule.cs | 302 ------------------ .../TextureDownload/TextureNotFoundSender.cs | 87 ----- .../UserTextureDownloadService.cs | 265 --------------- .../TextureSender/Tests/TextureSenderTests.cs | 180 ----------- .../Agent/TextureSender/TextureSender.cs | 212 ------------ .../CoreModules/Asset/CenomeAssetCache.cs | 10 +- .../CoreModules/Asset/CoreAssetCache.cs | 7 +- .../CoreModules/Asset/FlotsamAssetCache.cs | 51 ++- .../Asset/GlynnTuckerAssetCache.cs | 43 ++- .../Framework/Interfaces/ITextureSender.cs | 58 ---- 12 files changed, 45 insertions(+), 1275 deletions(-) delete mode 100644 OpenSim/Region/ClientStack/ClientStackUserSettings.cs delete mode 100644 OpenSim/Region/ClientStack/ThrottleSettings.cs delete mode 100644 OpenSim/Region/CoreModules/Agent/TextureDownload/TextureDownloadModule.cs delete mode 100644 OpenSim/Region/CoreModules/Agent/TextureDownload/TextureNotFoundSender.cs delete mode 100644 OpenSim/Region/CoreModules/Agent/TextureDownload/UserTextureDownloadService.cs delete mode 100644 OpenSim/Region/CoreModules/Agent/TextureSender/Tests/TextureSenderTests.cs delete mode 100644 OpenSim/Region/CoreModules/Agent/TextureSender/TextureSender.cs delete mode 100644 OpenSim/Region/Framework/Interfaces/ITextureSender.cs diff --git a/OpenSim/Region/ClientStack/ClientStackUserSettings.cs b/OpenSim/Region/ClientStack/ClientStackUserSettings.cs deleted file mode 100644 index 231b3aa2f5..0000000000 --- a/OpenSim/Region/ClientStack/ClientStackUserSettings.cs +++ /dev/null @@ -1,48 +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. - */ - -namespace OpenSim.Region.ClientStack -{ - /// - /// Allow users to tweak parameters for the client stack. - /// - /// At the moment this is very incomplete - other tweakable settings could be added. This is also somewhat LL client - /// oriented right now. - /// - public class ClientStackUserSettings - { - /// - /// The settings for the throttle that governs how many packets in total are sent to the client. - /// - public ThrottleSettings TotalThrottleSettings; - - /// - /// A multiplier applied to all client throttle settings. Default value is x2 (temporarily) - /// - public float ClientThrottleMultipler = 2; - } -} diff --git a/OpenSim/Region/ClientStack/ThrottleSettings.cs b/OpenSim/Region/ClientStack/ThrottleSettings.cs deleted file mode 100644 index fe4718cec8..0000000000 --- a/OpenSim/Region/ClientStack/ThrottleSettings.cs +++ /dev/null @@ -1,57 +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. - */ - -namespace OpenSim.Region.ClientStack -{ - /// - /// Represent throttle settings for a client stack. These settings are in bytes per second - /// - public class ThrottleSettings - { - /// - /// Minimum bytes per second that the throttle can be set to. - /// - public int Min; - - /// - /// Maximum bytes per second that the throttle can be set to. - /// - public int Max; - - /// - /// Current bytes per second that the throttle should be set to. - /// - public int Current; - - public ThrottleSettings(int min, int max, int current) - { - Min = min; - Max = max; - Current = current; - } - } -} diff --git a/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureDownloadModule.cs b/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureDownloadModule.cs deleted file mode 100644 index 71ff28c8b5..0000000000 --- a/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureDownloadModule.cs +++ /dev/null @@ -1,302 +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 System.Threading; -using log4net; -using Nini.Config; -using OpenMetaverse; -using OpenSim.Framework; -using OpenSim.Framework.Communications.Cache; -using OpenSim.Region.Framework.Interfaces; -using OpenSim.Region.Framework.Scenes; -using BlockingQueue = OpenSim.Framework.BlockingQueue; -using OpenSim.Services.Interfaces; - -namespace OpenSim.Region.CoreModules.Agent.TextureDownload -{ - public class TextureDownloadModule : IRegionModule - { - private static readonly ILog m_log - = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - /// - /// There is one queue for all textures waiting to be sent, regardless of the requesting user. - /// - private readonly BlockingQueue m_queueSenders - = new BlockingQueue(); - - /// - /// Each user has their own texture download service. - /// - private readonly Dictionary m_userTextureServices = - new Dictionary(); - - private Scene m_scene; - private List m_scenes = new List(); - - public TextureDownloadModule() - { - } - - #region IRegionModule Members - - public void Initialise(Scene scene, IConfigSource config) - { - - if (m_scene == null) - { - //m_log.Debug("Creating Texture download module"); - m_scene = scene; - //m_thread = new Thread(new ThreadStart(ProcessTextureSenders)); - //m_thread.Name = "ProcessTextureSenderThread"; - //m_thread.IsBackground = true; - //m_thread.Start(); - //ThreadTracker.Add(m_thread); - } - - if (!m_scenes.Contains(scene)) - { - m_scenes.Add(scene); - m_scene = scene; - m_scene.EventManager.OnNewClient += NewClient; - m_scene.EventManager.OnRemovePresence += EventManager_OnRemovePresence; - } - } - - public void PostInitialise() - { - } - - public void Close() - { - } - - public string Name - { - get { return "TextureDownloadModule"; } - } - - public bool IsSharedModule - { - get { return false; } - } - - #endregion - - /// - /// Cleanup the texture service related objects for the removed presence. - /// - /// - private void EventManager_OnRemovePresence(UUID agentId) - { - UserTextureDownloadService textureService; - - lock (m_userTextureServices) - { - if (m_userTextureServices.TryGetValue(agentId, out textureService)) - { - textureService.Close(); - //m_log.DebugFormat("[TEXTURE MODULE]: Removing UserTextureServices from {0}", m_scene.RegionInfo.RegionName); - m_userTextureServices.Remove(agentId); - } - } - } - - public void NewClient(IClientAPI client) - { - UserTextureDownloadService textureService; - - lock (m_userTextureServices) - { - if (m_userTextureServices.TryGetValue(client.AgentId, out textureService)) - { - textureService.Close(); - //m_log.DebugFormat("[TEXTURE MODULE]: Removing outdated UserTextureServices from {0}", m_scene.RegionInfo.RegionName); - m_userTextureServices.Remove(client.AgentId); - } - m_userTextureServices.Add(client.AgentId, new UserTextureDownloadService(client, m_scene, m_queueSenders)); - } - - client.OnRequestTexture += TextureRequest; - } - - /// I'm commenting this out, and replacing it with the implementation below, which - /// may return a null value. This is necessary for avoiding race conditions - /// recreating UserTextureServices for clients that have just been closed. - /// That behavior of always returning a UserTextureServices was causing the - /// A-B-A problem (mantis #2855). - /// - ///// - ///// Does this user have a registered texture download service? - ///// - ///// - ///// - ///// Always returns true, since a service is created if one does not already exist - //private bool TryGetUserTextureService( - // IClientAPI client, out UserTextureDownloadService textureService) - //{ - // lock (m_userTextureServices) - // { - // if (m_userTextureServices.TryGetValue(client.AgentId, out textureService)) - // { - // //m_log.DebugFormat("[TEXTURE MODULE]: Found existing UserTextureServices in ", m_scene.RegionInfo.RegionName); - // return true; - // } - - // m_log.DebugFormat("[TEXTURE MODULE]: Creating new UserTextureServices in ", m_scene.RegionInfo.RegionName); - // textureService = new UserTextureDownloadService(client, m_scene, m_queueSenders); - // m_userTextureServices.Add(client.AgentId, textureService); - - // return true; - // } - //} - - /// - /// Does this user have a registered texture download service? - /// - /// - /// - /// A UserTextureDownloadService or null in the output parameter, and true or false accordingly. - private bool TryGetUserTextureService(IClientAPI client, out UserTextureDownloadService textureService) - { - lock (m_userTextureServices) - { - if (m_userTextureServices.TryGetValue(client.AgentId, out textureService)) - { - //m_log.DebugFormat("[TEXTURE MODULE]: Found existing UserTextureServices in ", m_scene.RegionInfo.RegionName); - return true; - } - - textureService = null; - return false; - } - } - - /// - /// Start the process of requesting a given texture. - /// - /// - /// - public void TextureRequest(Object sender, TextureRequestArgs e) - { - IClientAPI client = (IClientAPI)sender; - - if (e.Priority == 1016001f) // Preview - { - if (client.Scene is Scene) - { - Scene scene = (Scene)client.Scene; - - CachedUserInfo profile = scene.CommsManager.UserProfileCacheService.GetUserDetails(client.AgentId); - if (profile == null) // Deny unknown user - return; - - IInventoryService invService = scene.InventoryService; - if (invService.GetRootFolder(client.AgentId) == null) // Deny no inventory - return; - - // Diva 2009-08-13: this test doesn't make any sense to many devs - //if (profile.UserProfile.GodLevel < 200 && profile.RootFolder.FindAsset(e.RequestedAssetID) == null) // Deny if not owned - //{ - // m_log.WarnFormat("[TEXTURE]: user {0} doesn't have permissions to texture {1}"); - // return; - //} - - m_log.Debug("Texture preview"); - } - } - - UserTextureDownloadService textureService; - - if (TryGetUserTextureService(client, out textureService)) - { - textureService.HandleTextureRequest(e); - } - } - - /// - /// Entry point for the thread dedicated to processing the texture queue. - /// - public void ProcessTextureSenders() - { - ITextureSender sender = null; - - try - { - while (true) - { - sender = m_queueSenders.Dequeue(); - - if (sender.Cancel) - { - TextureSent(sender); - - sender.Cancel = false; - } - else - { - bool finished = sender.SendTexturePacket(); - if (finished) - { - TextureSent(sender); - } - else - { - m_queueSenders.Enqueue(sender); - } - } - - // Make sure that any sender we currently have can get garbage collected - sender = null; - - //m_log.InfoFormat("[TEXTURE] Texture sender queue size: {0}", m_queueSenders.Count()); - } - } - catch (Exception e) - { - // TODO: Let users in the sim and those entering it and possibly an external watchdog know what has happened - m_log.ErrorFormat( - "[TEXTURE]: Texture send thread terminating with exception. PLEASE REBOOT YOUR SIM - TEXTURES WILL NOT BE AVAILABLE UNTIL YOU DO. Exception is {0}", - e); - } - } - - /// - /// Called when the texture has finished sending. - /// - /// - private void TextureSent(ITextureSender sender) - { - sender.Sending = false; - //m_log.DebugFormat("[TEXTURE]: Removing download stat for {0}", sender.assetID); - m_scene.StatsReporter.AddPendingDownloads(-1); - } - } -} diff --git a/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureNotFoundSender.cs b/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureNotFoundSender.cs deleted file mode 100644 index ba735a71bc..0000000000 --- a/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureNotFoundSender.cs +++ /dev/null @@ -1,87 +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 OpenMetaverse; -using OpenSim.Framework; -using OpenSim.Region.Framework.Interfaces; - -namespace OpenSim.Region.CoreModules.Agent.TextureDownload -{ - /// - /// Sends a 'texture not found' packet back to the client - /// - public class TextureNotFoundSender : ITextureSender - { - // private static readonly log4net.ILog m_log - // = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - - // private IClientAPI m_client; - // private UUID m_textureId; - - public TextureNotFoundSender(IClientAPI client, UUID textureID) - { - //m_client = client; - //m_textureId = textureID; - } - - #region ITextureSender Members - - public bool Sending - { - get { return false; } - set { } - } - - public bool Cancel - { - get { return false; } - set { } - } - - // See ITextureSender - public void UpdateRequest(int discardLevel, uint packetNumber) - { - // No need to implement since priority changes don't affect this operation - } - - // See ITextureSender - public bool SendTexturePacket() - { - // m_log.DebugFormat( - // "[TEXTURE NOT FOUND SENDER]: Informing the client that texture {0} cannot be found", - // m_textureId); - - // XXX Temporarily disabling as this appears to be causing client crashes on at least - // 1.19.0(5) of the Linden Second Life client. - // m_client.SendImageNotFound(m_textureId); - - return true; - } - - #endregion - } -} diff --git a/OpenSim/Region/CoreModules/Agent/TextureDownload/UserTextureDownloadService.cs b/OpenSim/Region/CoreModules/Agent/TextureDownload/UserTextureDownloadService.cs deleted file mode 100644 index 19f0f90486..0000000000 --- a/OpenSim/Region/CoreModules/Agent/TextureDownload/UserTextureDownloadService.cs +++ /dev/null @@ -1,265 +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.Framework.Communications.Limit; -using OpenSim.Framework.Statistics; -using OpenSim.Region.Framework.Interfaces; -using OpenSim.Region.Framework.Scenes; - -namespace OpenSim.Region.CoreModules.Agent.TextureDownload -{ - /// - /// This module sets up texture senders in response to client texture requests, and places them on a - /// processing queue once those senders have the appropriate data (i.e. a texture retrieved from the - /// asset cache). - /// - public class UserTextureDownloadService - { -// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - /// - /// True if the service has been closed, probably because a user with texture requests still queued - /// logged out. - /// - private bool closed; - - /// - /// We will allow the client to request the same texture n times before dropping further requests - /// - /// This number includes repeated requests for the same texture at different resolutions (which we don't - /// currently handle properly as far as I know). However, this situation should be handled in a more - /// sophisticated way. - /// -// private static readonly int MAX_ALLOWED_TEXTURE_REQUESTS = 5; - - /// - /// XXX Also going to limit requests for found textures. - /// -// private readonly IRequestLimitStrategy foundTextureLimitStrategy -// = new RepeatLimitStrategy(MAX_ALLOWED_TEXTURE_REQUESTS); - -// private readonly IClientAPI m_client; - private readonly Scene m_scene; - - /// - /// Texture Senders are placed in this queue once they have received their texture from the asset - /// cache. Another module actually invokes the send. - /// -// private readonly OpenSim.Framework.BlockingQueue m_sharedSendersQueue; - - /// - /// Holds texture senders before they have received the appropriate texture from the asset cache. - /// - private readonly Dictionary m_textureSenders = new Dictionary(); - - /// - /// We're going to limit requests for the same missing texture. - /// XXX This is really a temporary solution to deal with the situation where a client continually requests - /// the same missing textures - /// -// private readonly IRequestLimitStrategy missingTextureLimitStrategy -// = new RepeatLimitStrategy(MAX_ALLOWED_TEXTURE_REQUESTS); - - public UserTextureDownloadService( - IClientAPI client, Scene scene, OpenSim.Framework.BlockingQueue sharedQueue) - { -// m_client = client; - m_scene = scene; -// m_sharedSendersQueue = sharedQueue; - } - - /// - /// Handle a texture request. This involves creating a texture sender and placing it on the - /// previously passed in shared queue. - /// - /// - public void HandleTextureRequest(TextureRequestArgs e) - { - - //TextureSender.TextureSender textureSender; - - //TODO: should be working out the data size/ number of packets to be sent for each discard level - //if ((e.DiscardLevel >= 0) || (e.Priority != 0)) - //{ - //lock (m_textureSenders) - //{ - //if (m_textureSenders.TryGetValue(e.RequestedAssetID, out textureSender)) - //{ - // If we've received new non UUID information for this request and it hasn't dispatched - // yet, then update the request accordingly. - // textureSender.UpdateRequest(e.DiscardLevel, e.PacketNumber); - //} - //else - //{ - // m_log.DebugFormat("[TEXTURE]: Received a request for texture {0}", e.RequestedAssetID); - - //if (!foundTextureLimitStrategy.AllowRequest(e.RequestedAssetID)) - //{ - // m_log.DebugFormat( - // "[TEXTURE]: Refusing request for {0} from client {1}", - // e.RequestedAssetID, m_client.AgentId); - - //return; - //} - //else if (!missingTextureLimitStrategy.AllowRequest(e.RequestedAssetID)) - //{ - // if (missingTextureLimitStrategy.IsFirstRefusal(e.RequestedAssetID)) - // { - // if (StatsManager.SimExtraStats != null) - // StatsManager.SimExtraStats.AddBlockedMissingTextureRequest(); - - // Commenting out this message for now as it causes too much noise with other - // debug messages. - // m_log.DebugFormat( - // "[TEXTURE]: Dropping requests for notified missing texture {0} for client {1} since we have received more than {2} requests", - // e.RequestedAssetID, m_client.AgentId, MAX_ALLOWED_TEXTURE_REQUESTS); - // } - - // return; - //} - - m_scene.StatsReporter.AddPendingDownloads(1); - - //TextureSender.TextureSender requestHandler = new TextureSender.TextureSender(m_client, e.DiscardLevel, e.PacketNumber); - //m_textureSenders.Add(e.RequestedAssetID, null); - - m_scene.AssetService.Get(e.RequestedAssetID.ToString(), this, TextureReceived); - - - } - - protected void TextureReceived(string id, Object sender, AssetBase asset) - { - if (asset != null) - TextureCallback(asset.FullID, asset); - } - - /// - /// The callback for the asset cache when a texture has been retrieved. This method queues the - /// texture sender for processing. - /// - /// - /// - public void TextureCallback(UUID textureID, AssetBase texture) - { - //m_log.DebugFormat("[USER TEXTURE DOWNLOAD SERVICE]: Calling TextureCallback with {0}, texture == null is {1}", textureID, (texture == null ? true : false)); - - // There may still be texture requests pending for a logged out client - if (closed) - return; - - /* - lock (m_textureSenders) - { - TextureSender.TextureSender textureSender; - if (m_textureSenders.TryGetValue(textureID, out textureSender)) - { - // XXX It may be perfectly valid for a texture to have no data... but if we pass - // this on to the TextureSender it will blow up, so just discard for now. - // Needs investigation. - if (texture == null || texture.Data == null) - { - if (!missingTextureLimitStrategy.IsMonitoringRequests(textureID)) - { - missingTextureLimitStrategy.MonitorRequests(textureID); - - // m_log.DebugFormat( - // "[TEXTURE]: Queueing first TextureNotFoundSender for {0}, client {1}", - // textureID, m_client.AgentId); - } - - ITextureSender textureNotFoundSender = new TextureNotFoundSender(m_client, textureID); - EnqueueTextureSender(textureNotFoundSender); - } - else - { - if (!textureSender.ImageLoaded) - { - textureSender.TextureReceived(texture); - EnqueueTextureSender(textureSender); - - foundTextureLimitStrategy.MonitorRequests(textureID); - } - } - - //m_log.InfoFormat("[TEXTURE] Removing texture sender with uuid {0}", textureID); - m_textureSenders.Remove(textureID); - //m_log.InfoFormat("[TEXTURE] Current texture senders in dictionary: {0}", m_textureSenders.Count); - } - else - { - m_log.WarnFormat( - "[TEXTURE]: Got a texture uuid {0} with no sender object to handle it, this shouldn't happen", - textureID); - } - } - */ - } - - /// - /// Place a ready texture sender on the processing queue. - /// - /// -// private void EnqueueTextureSender(ITextureSender textureSender) -// { -// textureSender.Cancel = false; -// textureSender.Sending = true; -// -// if (!m_sharedSendersQueue.Contains(textureSender)) -// { -// m_sharedSendersQueue.Enqueue(textureSender); -// } -// } - - /// - /// Close this module. - /// - internal void Close() - { - closed = true; - - lock (m_textureSenders) - { - foreach (TextureSender.TextureSender textureSender in m_textureSenders.Values) - { - textureSender.Cancel = true; - } - - m_textureSenders.Clear(); - } - - // XXX: It might be possible to also remove pending texture requests from the asset cache queues, - // though this might also be more trouble than it's worth. - } - } -} diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/Tests/TextureSenderTests.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/Tests/TextureSenderTests.cs deleted file mode 100644 index efa275c61f..0000000000 --- a/OpenSim/Region/CoreModules/Agent/TextureSender/Tests/TextureSenderTests.cs +++ /dev/null @@ -1,180 +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 NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; -using OpenMetaverse; -using OpenSim.Framework; -using OpenSim.Tests.Common; -using OpenSim.Tests.Common.Mock; - -namespace OpenSim.Region.CoreModules.Agent.TextureSender -{ - [TestFixture] - public class UserTextureSenderTests - { - public UUID uuid1; - public UUID uuid2; - public UUID uuid3; - public UUID uuid4; - public int npackets, testsize; - public TestClient client; - public TextureSender ts; - public static Random random = new Random(); - - [TestFixtureSetUp] - public void Init() - { - AgentCircuitData agent = new AgentCircuitData(); - agent.AgentID = UUID.Random(); - agent.firstname = "testfirstname"; - agent.lastname = "testlastname"; - agent.SessionID = UUID.Zero; - agent.SecureSessionID = UUID.Zero; - agent.circuitcode = 123; - agent.BaseFolder = UUID.Zero; - agent.InventoryFolder = UUID.Zero; - agent.startpos = Vector3.Zero; - agent.CapsPath = "http://wibble.com"; - client = new TestClient(agent, null); - ts = new TextureSender(client, 0, 0); - testsize = random.Next(5000,15000); - npackets = CalculateNumPackets(testsize); - uuid1 = UUID.Random(); - uuid2 = UUID.Random(); - uuid3 = UUID.Random(); - uuid4 = UUID.Random(); - } - - /// - /// Test sending package - /// - [Test] - public void T010_SendPkg() - { - TestHelper.InMethod(); - // Normal sending - AssetBase abase = new AssetBase(uuid1, "asset one"); - byte[] abdata = new byte[testsize]; - random.NextBytes(abdata); - abase.Data = abdata; - bool isdone = false; - ts.TextureReceived(abase); - for (int i = 0; i < npackets; i++) { - isdone = ts.SendTexturePacket(); - } - - Assert.That(isdone,Is.False); - isdone = ts.SendTexturePacket(); - Assert.That(isdone,Is.True); - } - - [Test] - public void T011_UpdateReq() - { - TestHelper.InMethod(); - // Test packet number start - AssetBase abase = new AssetBase(uuid2, "asset two"); - byte[] abdata = new byte[testsize]; - random.NextBytes(abdata); - abase.Data = abdata; - - bool isdone = false; - ts.TextureReceived(abase); - ts.UpdateRequest(0,3); - - for (int i = 0; i < npackets-3; i++) { - isdone = ts.SendTexturePacket(); - } - - Assert.That(isdone,Is.False); - isdone = ts.SendTexturePacket(); - Assert.That(isdone,Is.True); - - // Test discard level - abase = new AssetBase(uuid3, "asset three"); - abdata = new byte[testsize]; - random.NextBytes(abdata); - abase.Data = abdata; - isdone = false; - ts.TextureReceived(abase); - ts.UpdateRequest(-1,0); - - Assert.That(ts.SendTexturePacket(),Is.True); - - abase = new AssetBase(uuid4, "asset four"); - abdata = new byte[testsize]; - random.NextBytes(abdata); - abase.Data = abdata; - isdone = false; - ts.TextureReceived(abase); - ts.UpdateRequest(0,5); - - for (int i = 0; i < npackets-5; i++) { - isdone = ts.SendTexturePacket(); - } - Assert.That(isdone,Is.False); - isdone = ts.SendTexturePacket(); - Assert.That(isdone,Is.True); - } - - [Test] - public void T999_FinishStatus() - { - TestHelper.InMethod(); - // Of the 4 assets "sent", only 2 sent the first part. - Assert.That(client.sentdatapkt.Count,Is.EqualTo(2)); - - // Sum of all packets sent: - int totalpkts = (npackets) + (npackets - 2) + (npackets - 4); - Assert.That(client.sentpktpkt.Count,Is.EqualTo(totalpkts)); - } - - /// - /// Calculate the number of packets that will be required to send the texture loaded into this sender - /// This is actually the number of 1000 byte packets not including an initial 600 byte packet... - /// Borrowed from TextureSender.cs - /// - /// - /// - private int CalculateNumPackets(int length) - { - int numPackets = 0; - - if (length > 600) - { - //over 600 bytes so split up file - int restData = (length - 600); - int restPackets = ((restData + 999) / 1000); - numPackets = restPackets; - } - - return numPackets; - } - } -} diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/TextureSender.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/TextureSender.cs deleted file mode 100644 index 62c5a32169..0000000000 --- a/OpenSim/Region/CoreModules/Agent/TextureSender/TextureSender.cs +++ /dev/null @@ -1,212 +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.Reflection; -using log4net; -using OpenSim.Framework; -using OpenSim.Region.Framework.Interfaces; - -namespace OpenSim.Region.CoreModules.Agent.TextureSender -{ - /// - /// A TextureSender handles the process of receiving a texture requested by the client from the - /// AssetCache, and then sending that texture back to the client. - /// - public class TextureSender : ITextureSender - { - private static readonly ILog m_log - = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - /// - /// Records the number of times texture send has been called. - /// - public int counter = 0; - - public bool ImageLoaded = false; - - /// - /// Holds the texture asset to send. - /// - private AssetBase m_asset; - - //public UUID assetID { get { return m_asset.FullID; } } - - // private bool m_cancel = false; - - // See ITextureSender - - // private bool m_sending = false; - - /// - /// This is actually the number of extra packets required to send the texture data! We always assume - /// at least one is required. - /// - private int NumPackets = 0; - - /// - /// Holds the packet number to send next. In this case, each packet is 1000 bytes long and starts - /// at the 600th byte (0th indexed). - /// - private int PacketCounter = 0; - - private int RequestedDiscardLevel = -1; - private IClientAPI RequestUser; - private uint StartPacketNumber = 0; - - public TextureSender(IClientAPI client, int discardLevel, uint packetNumber) - { - RequestUser = client; - RequestedDiscardLevel = discardLevel; - StartPacketNumber = packetNumber; - } - - #region ITextureSender Members - - public bool Cancel - { - get { return false; } - set - { - // m_cancel = value; - } - } - - public bool Sending - { - get { return false; } - set - { - // m_sending = value; - } - } - - // See ITextureSender - public void UpdateRequest(int discardLevel, uint packetNumber) - { - RequestedDiscardLevel = discardLevel; - StartPacketNumber = packetNumber; - PacketCounter = (int)StartPacketNumber; - } - - // See ITextureSender - public bool SendTexturePacket() - { - //m_log.DebugFormat("[TEXTURE SENDER]: Sending packet for {0}", m_asset.FullID); - - SendPacket(); - counter++; - if ((NumPackets == 0) || (RequestedDiscardLevel == -1) || (PacketCounter > NumPackets) || - ((RequestedDiscardLevel > 0) && (counter > 50 + (NumPackets / (RequestedDiscardLevel + 1))))) - { - return true; - } - return false; - } - - #endregion - - /// - /// Load up the texture data to send. - /// - /// - public void TextureReceived(AssetBase asset) - { - m_asset = asset; - NumPackets = CalculateNumPackets(asset.Data.Length); - PacketCounter = (int)StartPacketNumber; - ImageLoaded = true; - } - - /// - /// Sends a texture packet to the client. - /// - private void SendPacket() - { - if (PacketCounter <= NumPackets) - { - if (PacketCounter == 0) - { - if (NumPackets == 0) - { - RequestUser.SendImageFirstPart(1, m_asset.FullID, (uint)m_asset.Data.Length, m_asset.Data, 2); - PacketCounter++; - } - else - { - byte[] ImageData1 = new byte[600]; - Array.Copy(m_asset.Data, 0, ImageData1, 0, 600); - - RequestUser.SendImageFirstPart( - (ushort)(NumPackets), m_asset.FullID, (uint)m_asset.Data.Length, ImageData1, 2); - PacketCounter++; - } - } - else - { - int size = m_asset.Data.Length - 600 - (1000 * (PacketCounter - 1)); - if (size > 1000) size = 1000; - byte[] imageData = new byte[size]; - try - { - Array.Copy(m_asset.Data, 600 + (1000 * (PacketCounter - 1)), imageData, 0, size); - } - catch (ArgumentOutOfRangeException) - { - m_log.Error("[TEXTURE SENDER]: Unable to separate texture into multiple packets: Array bounds failure on asset:" + - m_asset.ID); - return; - } - - RequestUser.SendImageNextPart((ushort)PacketCounter, m_asset.FullID, imageData); - PacketCounter++; - } - } - } - - /// - /// Calculate the number of packets that will be required to send the texture loaded into this sender - /// This is actually the number of 1000 byte packets not including an initial 600 byte packet... - /// - /// - /// - private int CalculateNumPackets(int length) - { - int numPackets = 0; - - if (length > 600) - { - //over 600 bytes so split up file - int restData = (length - 600); - int restPackets = ((restData + 999) / 1000); - numPackets = restPackets; - } - - return numPackets; - } - } -} diff --git a/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs b/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs index 66ca7c2336..1add0ab1be 100644 --- a/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs @@ -154,14 +154,6 @@ namespace OpenSim.Region.CoreModules.Asset ///

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 z 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

private int m_hitCount; - /// - /// Initialize asset cache module with default parameters. - /// - public void Initialize() - { - Initialize(DefaultMaxSize, DefaultMaxCount, DefaultExpirationTime); - } - /// /// Initialize asset cache module, with custom parameters. /// @@ -174,7 +166,7 @@ namespace OpenSim.Region.CoreModules.Asset /// /// Asset's expiration time. /// - public void Initialize(long maximalSize, int maximalCount, TimeSpan expirationTime) + protected void Initialize(long maximalSize, int maximalCount, TimeSpan expirationTime) { if (maximalSize <= 0 || maximalCount <= 0) { diff --git a/OpenSim/Region/CoreModules/Asset/CoreAssetCache.cs b/OpenSim/Region/CoreModules/Asset/CoreAssetCache.cs index 0a7e736599..7da5e7ace9 100644 --- a/OpenSim/Region/CoreModules/Asset/CoreAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/CoreAssetCache.cs @@ -44,10 +44,8 @@ namespace OpenSim.Region.CoreModules.Asset LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); - private bool m_Enabled = false; - private Cache m_Cache = new Cache(CacheMedium.Memory, - CacheStrategy.Aggressive, - CacheFlags.AllowUpdate); + private bool m_Enabled; + private Cache m_Cache; public string Name { @@ -77,6 +75,7 @@ namespace OpenSim.Region.CoreModules.Asset return; } + m_Cache = new Cache(CacheMedium.Memory, CacheStrategy.Aggressive, CacheFlags.AllowUpdate); m_Enabled = true; m_log.Info("[ASSET CACHE]: Core asset cache enabled"); diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index d45c35c351..cbdca16e74 100644 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs @@ -61,24 +61,23 @@ namespace Flotsam.RegionModules.AssetCache LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); - private bool m_Enabled = false; + private bool m_Enabled; private const string m_ModuleName = "FlotsamAssetCache"; private const string m_DefaultCacheDirectory = m_ModuleName; private string m_CacheDirectory = m_DefaultCacheDirectory; - - private List m_InvalidChars = new List(); + private readonly List m_InvalidChars = new List(); private int m_LogLevel = 1; private ulong m_HitRateDisplay = 1; // How often to display hit statistics, given in requests - private static ulong m_Requests = 0; - private static ulong m_RequestsForInprogress = 0; - private static ulong m_DiskHits = 0; - private static ulong m_MemoryHits = 0; - private static double m_HitRateMemory = 0.0; - private static double m_HitRateFile = 0.0; + private static ulong m_Requests; + private static ulong m_RequestsForInprogress; + private static ulong m_DiskHits; + private static ulong m_MemoryHits; + private static double m_HitRateMemory; + private static double m_HitRateFile; #if WAIT_ON_INPROGRESS_REQUESTS private Dictionary m_CurrentlyWriting = new Dictionary(); @@ -87,7 +86,7 @@ namespace Flotsam.RegionModules.AssetCache private List m_CurrentlyWriting = new List(); #endif - private ExpiringCache m_MemoryCache = new ExpiringCache(); + private ExpiringCache m_MemoryCache; private bool m_MemoryCacheEnabled = true; // Expiration is expressed in hours. @@ -101,12 +100,12 @@ namespace Flotsam.RegionModules.AssetCache private static int m_CacheDirectoryTierLen = 3; private static int m_CacheWarnAt = 30000; - private System.Timers.Timer m_CachCleanTimer = new System.Timers.Timer(); + private System.Timers.Timer m_CacheCleanTimer; - private IAssetService m_AssetService = null; + private IAssetService m_AssetService; private List m_Scenes = new List(); - private bool m_DeepScanBeforePurge = false; + private bool m_DeepScanBeforePurge; public FlotsamAssetCache() { @@ -128,14 +127,15 @@ namespace Flotsam.RegionModules.AssetCache { IConfig moduleConfig = source.Configs["Modules"]; - if (moduleConfig != null) { - string name = moduleConfig.GetString("AssetCaching", ""); + string name = moduleConfig.GetString("AssetCaching", String.Empty); if (name == Name) { + m_MemoryCache = new ExpiringCache(); m_Enabled = true; + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} enabled", this.Name); IConfig assetConfig = source.Configs["AssetCache"]; @@ -163,21 +163,11 @@ namespace Flotsam.RegionModules.AssetCache m_FileExpirationCleanupTimer = TimeSpan.FromHours(assetConfig.GetDouble("FileCleanupTimer", m_DefaultFileExpiration)); if ((m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero)) { - m_CachCleanTimer.Interval = m_FileExpirationCleanupTimer.TotalMilliseconds; - m_CachCleanTimer.AutoReset = true; - m_CachCleanTimer.Elapsed += CleanupExpiredFiles; - m_CachCleanTimer.Enabled = true; - lock (m_CachCleanTimer) - { - m_CachCleanTimer.Start(); - } - } - else - { - lock (m_CachCleanTimer) - { - m_CachCleanTimer.Enabled = false; - } + m_CacheCleanTimer = new System.Timers.Timer(m_FileExpirationCleanupTimer.TotalMilliseconds); + m_CacheCleanTimer.AutoReset = true; + m_CacheCleanTimer.Elapsed += CleanupExpiredFiles; + lock (m_CacheCleanTimer) + m_CacheCleanTimer.Start(); } m_CacheDirectoryTiers = assetConfig.GetInt("CacheDirectoryTiers", 1); @@ -208,7 +198,6 @@ namespace Flotsam.RegionModules.AssetCache MainConsole.Instance.Commands.AddCommand(this.Name, true, "fcache clear", "fcache clear [file] [memory]", "Remove all assets in the file and/or memory cache", HandleConsoleCommand); MainConsole.Instance.Commands.AddCommand(this.Name, true, "fcache assets", "fcache assets", "Attempt a deep scan and cache of all assets in all scenes", HandleConsoleCommand); MainConsole.Instance.Commands.AddCommand(this.Name, true, "fcache expire", "fcache expire ", "Purge cached assets older then the specified date/time", HandleConsoleCommand); - } } } diff --git a/OpenSim/Region/CoreModules/Asset/GlynnTuckerAssetCache.cs b/OpenSim/Region/CoreModules/Asset/GlynnTuckerAssetCache.cs index 4869f5d6b8..1365e69117 100644 --- a/OpenSim/Region/CoreModules/Asset/GlynnTuckerAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/GlynnTuckerAssetCache.cs @@ -45,11 +45,13 @@ namespace OpenSim.Region.CoreModules.Asset LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); - private bool m_Enabled = false; - private ICache m_Cache = new GlynnTucker.Cache.SimpleMemoryCache(); + private bool m_Enabled; + private ICache m_Cache; + private ulong m_Hits; + private ulong m_Requests; // Instrumentation - private uint m_DebugRate = 0; + private uint m_DebugRate; public Type ReplaceableInterface { @@ -72,6 +74,7 @@ namespace OpenSim.Region.CoreModules.Asset if (name == Name) { + m_Cache = new GlynnTucker.Cache.SimpleMemoryCache(); m_Enabled = true; m_log.Info("[ASSET CACHE]: GlynnTucker asset cache enabled"); @@ -80,7 +83,6 @@ namespace OpenSim.Region.CoreModules.Asset IConfig cacheConfig = source.Configs["AssetCache"]; if (cacheConfig != null) m_DebugRate = (uint)cacheConfig.GetInt("DebugRate", 0); - } } } @@ -117,24 +119,6 @@ namespace OpenSim.Region.CoreModules.Asset m_Cache.AddOrUpdate(asset.ID, asset); } - private ulong m_Hits = 0; - private ulong m_Requests = 0; - private void Debug(Object asset) - { - // Temporary instrumentation to measure the hit/miss rate - if (m_DebugRate > 0) - { - m_Requests++; - if (asset != null) - m_Hits++; - - if ((m_Requests % m_DebugRate) == 0) - m_log.DebugFormat("[ASSET CACHE]: Hit Rate {0} / {1} == {2}%", m_Hits, m_Requests, ((float)m_Hits / m_Requests) * 100); - - } - // End instrumentation - } - public AssetBase Get(string id) { Object asset = null; @@ -156,5 +140,20 @@ namespace OpenSim.Region.CoreModules.Asset { m_Cache.Clear(); } + + private void Debug(Object asset) + { + // Temporary instrumentation to measure the hit/miss rate + if (m_DebugRate > 0) + { + ++m_Requests; + if (asset != null) + ++m_Hits; + + if ((m_Requests % m_DebugRate) == 0) + m_log.DebugFormat("[ASSET CACHE]: Hit Rate {0} / {1} == {2}%", m_Hits, m_Requests, ((float)m_Hits / (float)m_Requests) * 100.0f); + } + // End instrumentation + } } } diff --git a/OpenSim/Region/Framework/Interfaces/ITextureSender.cs b/OpenSim/Region/Framework/Interfaces/ITextureSender.cs deleted file mode 100644 index c469ae8c9c..0000000000 --- a/OpenSim/Region/Framework/Interfaces/ITextureSender.cs +++ /dev/null @@ -1,58 +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. - */ - -namespace OpenSim.Region.Framework.Interfaces -{ - /// - /// Interface for an object which can send texture information to a client - /// - public interface ITextureSender - { - /// - /// Are we in the process of sending the texture? - /// - bool Sending { get; set; } - - /// - /// Has the texture send been cancelled? - /// - bool Cancel { get; set; } - - /// - /// Update the non data properties of a texture request - /// - /// - /// - void UpdateRequest(int discardLevel, uint packetNumber); - - /// - /// Send a texture packet to the client. - /// - /// True if the last packet has been sent, false otherwise. - bool SendTexturePacket(); - } -} From 8151190a45b98645efb06ea28b1758ffbc75cf7e Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 20 Oct 2009 10:56:15 -0700 Subject: [PATCH 34/61] * Removing ODEPrim and ODECharacter GetHashCode() overrides since they were based on something that could change * Tweaked a few other GetHashCode() overrides to bring them in line with MSDN recommendations --- OpenSim/Framework/Location.cs | 2 +- OpenSim/Region/Physics/OdePlugin/ODECharacter.cs | 5 ----- OpenSim/Region/Physics/OdePlugin/ODEPrim.cs | 5 ----- OpenSim/Region/ScriptEngine/Shared/LSL_Types.cs | 2 +- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/OpenSim/Framework/Location.cs b/OpenSim/Framework/Location.cs index 62ab5c74de..9504e03547 100644 --- a/OpenSim/Framework/Location.cs +++ b/OpenSim/Framework/Location.cs @@ -98,7 +98,7 @@ namespace OpenSim.Framework public override int GetHashCode() { - return X.GetHashCode() * 29 + Y.GetHashCode(); + return X.GetHashCode() ^ Y.GetHashCode(); } public object Clone() diff --git a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs index ef0e56e05f..71ace163d7 100644 --- a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs +++ b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs @@ -231,11 +231,6 @@ namespace OpenSim.Region.Physics.OdePlugin set { m_localID = value; } } - public override int GetHashCode() - { - return (int)m_localID; - } - public override bool Grabbed { set { return; } diff --git a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs index 63bfc909b8..4581d22a03 100644 --- a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs +++ b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs @@ -259,11 +259,6 @@ namespace OpenSim.Region.Physics.OdePlugin m_localID = value; } } - public override int GetHashCode() - { - return (int)m_localID; - } - public override bool Grabbed { set { return; } diff --git a/OpenSim/Region/ScriptEngine/Shared/LSL_Types.cs b/OpenSim/Region/ScriptEngine/Shared/LSL_Types.cs index 2842f6bed6..3f38bb653f 100644 --- a/OpenSim/Region/ScriptEngine/Shared/LSL_Types.cs +++ b/OpenSim/Region/ScriptEngine/Shared/LSL_Types.cs @@ -1975,7 +1975,7 @@ namespace OpenSim.Region.ScriptEngine.Shared public override int GetHashCode() { - return Convert.ToInt32(value); + return value.GetHashCode(); } From f568982e6928d54858d09aff6c474ff73abf6e88 Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 20 Oct 2009 19:38:35 +0100 Subject: [PATCH 35/61] Cleanup and comment the region module loader. Add support for configuring a server port to use for modules in a generic way and also add support for disabling modules that don't support proper disabling. Add support for selective loading by class name (advanced users only) --- .../RegionModulesControllerPlugin.cs | 203 +++++++++++++++--- 1 file changed, 175 insertions(+), 28 deletions(-) diff --git a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs index f30a18b378..13cb34c5b0 100644 --- a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs +++ b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs @@ -30,24 +30,36 @@ using System.Collections.Generic; using System.Reflection; using log4net; using Mono.Addins; +using Nini.Config; using OpenSim; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; namespace OpenSim.ApplicationPlugins.RegionModulesController { - public class RegionModulesControllerPlugin : IRegionModulesController, IApplicationPlugin + public class RegionModulesControllerPlugin : IRegionModulesController, + IApplicationPlugin { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + // Logger + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); - private OpenSimBase m_openSim; // for getting the config + // Config access + private OpenSimBase m_openSim; + // Our name private string m_name; - private List m_nonSharedModules = new List(); - private List m_sharedModules = new List(); + // Internal lists to collect information about modules present + private List m_nonSharedModules = + new List(); + private List m_sharedModules = + new List(); - private List m_sharedInstances = new List(); + // List of shared module instances, for adding to Scenes + private List m_sharedInstances = + new List(); #region IApplicationPlugin implementation @@ -57,40 +69,111 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController m_openSim = openSim; openSim.ApplicationRegistry.RegisterInterface(this); + // Who we are string id = AddinManager.CurrentAddin.Id; - int pos = id.LastIndexOf("."); - if (pos == -1) m_name = id; - else m_name = id.Substring(pos + 1); - //ExtensionNodeList list = AddinManager.GetExtensionNodes("/OpenSim/RegionModules"); - // load all the (new) region-module classes - foreach (TypeExtensionNode node in AddinManager.GetExtensionNodes("/OpenSim/RegionModules")) + // Make friendly name + int pos = id.LastIndexOf("."); + if (pos == -1) + m_name = id; + else + m_name = id.Substring(pos + 1); + + // The [Modules] section in the ini file + IConfig modulesConfig = + openSim.ConfigSource.Source.Configs["Modules"]; + if (modulesConfig == null) + modulesConfig = openSim.ConfigSource.Source.AddConfig("Modules"); + + // Scan modules and load all that aren't disabled + foreach (TypeExtensionNode node in + AddinManager.GetExtensionNodes("/OpenSim/RegionModules")) { - // TODO why does node.Type.isSubclassOf(typeof(ISharedRegionModule)) not work? if (node.Type.GetInterface(typeof(ISharedRegionModule).ToString()) != null) { + // Get the config string + string moduleString = + modulesConfig.GetString(node.Id, String.Empty); + + // We have a selector + if (moduleString != String.Empty) + { + // Allow disabling modules even if they don't have + // support for it + if (moduleString == "disabled") + continue; + + // Split off port, if present + string[] moduleParts = moduleString.Split(new char[] {'/'}, 2); + // Format is [port/][class] + string className = moduleParts[0]; + if (moduleParts.Length > 1) + className = moduleParts[1]; + + // Match the class name if given + if (className != String.Empty && + node.Type.ToString() != className) + continue; + } + m_log.DebugFormat("[REGIONMODULES]: Found shared region module {0}, class {1}", node.Id, node.Type); - m_sharedModules.Add(node.Type); + m_sharedModules.Add(node); } else if (node.Type.GetInterface(typeof(INonSharedRegionModule).ToString()) != null) { m_log.DebugFormat("[REGIONMODULES]: Found non-shared region module {0}, class {1}", node.Id, node.Type); - m_nonSharedModules.Add(node.Type); + m_nonSharedModules.Add(node); } else m_log.DebugFormat("[REGIONMODULES]: Found unknown type of module {0}, class {1}", node.Id, node.Type); } - // now we've got all the region-module classes loaded, create one instance of every ISharedRegionModule, - // initialize and postinitialize it. This Initialise we are in is called before LoadRegion.PostInitialise - // is called (which loads the regions), so we don't have any regions in the server yet. - foreach (Type type in m_sharedModules) + // Load and init the module. We try a constructor with a port + // if a port was given, fall back to one without if there is + // no port or the more specific constructor fails. + // This will be removed, so that any module capable of using a port + // must provide a constructor with a port in the future. + // For now, we do this so migration is easy. + // + foreach (TypeExtensionNode node in m_sharedModules) { - ISharedRegionModule module = (ISharedRegionModule)Activator.CreateInstance(type); + Object[] ctorArgs = new Object[] {0}; + + // Read the config again + string moduleString = + modulesConfig.GetString(node.Id, String.Empty); + + // Get the port number, if there is one + if (moduleString != String.Empty) + { + // Get the port number from the string + string[] moduleParts = moduleString.Split(new char[] {'/'}, + 2); + if (moduleParts.Length > 1) + ctorArgs[0] = Convert.ToUInt32(moduleParts[0]); + } + + // Try loading and initilaizing the module, using the + // port if appropriate + ISharedRegionModule module = null; + + try + { + module = (ISharedRegionModule)Activator.CreateInstance( + node.Type, ctorArgs); + } + catch + { + module = (ISharedRegionModule)Activator.CreateInstance( + node.Type); + } + + // OK, we're up and running m_sharedInstances.Add(module); module.Initialise(openSim.ConfigSource.Source); } + // Immediately run PostInitialise on shared modules foreach (ISharedRegionModule module in m_sharedInstances) { module.PostInitialise(); @@ -105,6 +188,8 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController #region IPlugin implementation + // We don't do that here + // public void Initialise () { throw new System.NotImplementedException(); @@ -114,9 +199,11 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController #region IDisposable implementation + // Cleanup + // public void Dispose () { - // we expect that all regions have been removed already + // We expect that all regions have been removed already while (m_sharedInstances.Count > 0) { m_sharedInstances[0].Close(); @@ -147,6 +234,11 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController #region IRegionModulesController implementation + // The root of all evil. + // This is where we handle adding the modules to scenes when they + // load. This means that here we deal with replaceable interfaces, + // nonshared modules, etc. + // public void AddRegionToModules (Scene scene) { Dictionary deferredSharedModules = @@ -154,12 +246,26 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController Dictionary deferredNonSharedModules = new Dictionary(); + // We need this to see if a module has already been loaded and + // has defined a replaceable interface. It's a generic call, + // so this can't be used directly. It will be used later Type s = scene.GetType(); MethodInfo mi = s.GetMethod("RequestModuleInterface"); - List sharedlist = new List(); + // This will hold the shared modules we actually load + List sharedlist = + new List(); + + // Iterate over the shared modules that have been loaded + // Add them to the new Scene foreach (ISharedRegionModule module in m_sharedInstances) { + // Here is where we check if a replaceable interface + // is defined. If it is, the module is checked against + // the interfaces already defined. If the interface is + // defined, we simply skip the module. Else, if the module + // defines a replaceable interface, we add it to the deferred + // list. Type replaceableInterface = module.ReplaceableInterface; if (replaceableInterface != null) { @@ -185,11 +291,41 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController sharedlist.Add(module); } - List list = new List(); - foreach (Type type in m_nonSharedModules) - { - INonSharedRegionModule module = (INonSharedRegionModule)Activator.CreateInstance(type); + IConfig modulesConfig = + m_openSim.ConfigSource.Source.Configs["Modules"]; + // Scan for, and load, nonshared modules + List list = new List(); + foreach (TypeExtensionNode node in m_nonSharedModules) + { + Object[] ctorArgs = new Object[] {0}; + + // Read the config + string moduleString = + modulesConfig.GetString(node.Id, String.Empty); + + // Get the port number, if there is one + if (moduleString != String.Empty) + { + // Get the port number from the string + string[] moduleParts = moduleString.Split(new char[] {'/'}, + 2); + if (moduleParts.Length > 1) + ctorArgs[0] = Convert.ToUInt32(moduleParts[0]); + } + + // Actually load it + INonSharedRegionModule module = null; + try + { + module = (INonSharedRegionModule)Activator.CreateInstance(node.Type, ctorArgs); + } + catch + { + module = (INonSharedRegionModule)Activator.CreateInstance(node.Type); + } + + // Check for replaceable interfaces Type replaceableInterface = module.ReplaceableInterface; if (replaceableInterface != null) { @@ -209,11 +345,16 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to non-shared module {1}", scene.RegionInfo.RegionName, module.Name); + // Initialise the module module.Initialise(m_openSim.ConfigSource.Source); list.Add(module); } + // Now add the modules that we found to the scene. If a module + // wishes to override a replaceable interface, it needs to + // register it in Initialise, so that the deferred module + // won't load. foreach (INonSharedRegionModule module in list) { module.AddRegion(scene); @@ -223,9 +364,9 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController // Now all modules without a replaceable base interface are loaded // Replaceable modules have either been skipped, or omitted. // Now scan the deferred modules here - foreach (ISharedRegionModule module in deferredSharedModules.Values) { + // Determine if the interface has been replaced Type replaceableInterface = module.ReplaceableInterface; MethodInfo mii = mi.MakeGenericMethod(replaceableInterface); @@ -238,15 +379,20 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to shared module {1} (deferred)", scene.RegionInfo.RegionName, module.Name); + // Not replaced, load the module module.AddRegion(scene); scene.AddRegionModule(module.Name, module); sharedlist.Add(module); } - List deferredlist = new List(); + // Same thing for nonshared modules, load them unless overridden + List deferredlist = + new List(); + foreach (INonSharedRegionModule module in deferredNonSharedModules.Values) { + // Check interface override Type replaceableInterface = module.ReplaceableInterface; if (replaceableInterface != null) { @@ -268,6 +414,7 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController deferredlist.Add(module); } + // Finally, load valid deferred modules foreach (INonSharedRegionModule module in deferredlist) { module.AddRegion(scene); From edd393ff308db2bf6802654dc37de6aa6a10f78a Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 20 Oct 2009 11:58:23 -0700 Subject: [PATCH 36/61] Reverting the deletion of files related to texture sending until we figure out exactly what is and isn't needed --- .../ClientStack/LindenUDP/LLUDPClient.cs | 2 + .../TextureDownload/TextureDownloadModule.cs | 302 ++++++++++++++++++ .../TextureDownload/TextureNotFoundSender.cs | 87 +++++ .../UserTextureDownloadService.cs | 265 +++++++++++++++ .../Agent/TextureSender/TextureSender.cs | 212 ++++++++++++ .../Framework/Interfaces/ITextureSender.cs | 58 ++++ 6 files changed, 926 insertions(+) create mode 100644 OpenSim/Region/CoreModules/Agent/TextureDownload/TextureDownloadModule.cs create mode 100644 OpenSim/Region/CoreModules/Agent/TextureDownload/TextureNotFoundSender.cs create mode 100644 OpenSim/Region/CoreModules/Agent/TextureDownload/UserTextureDownloadService.cs create mode 100644 OpenSim/Region/CoreModules/Agent/TextureSender/TextureSender.cs create mode 100644 OpenSim/Region/Framework/Interfaces/ITextureSender.cs diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 4b6a358588..4a3a04e8fb 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -163,7 +163,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP CircuitCode = circuitCode; m_udpServer = server; m_defaultThrottleRates = rates; + // 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); + // Create an array of token buckets for this clients different throttle categories m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) diff --git a/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureDownloadModule.cs b/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureDownloadModule.cs new file mode 100644 index 0000000000..71ff28c8b5 --- /dev/null +++ b/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureDownloadModule.cs @@ -0,0 +1,302 @@ +/* + * 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 System.Threading; +using log4net; +using Nini.Config; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using BlockingQueue = OpenSim.Framework.BlockingQueue; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Region.CoreModules.Agent.TextureDownload +{ + public class TextureDownloadModule : IRegionModule + { + private static readonly ILog m_log + = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// There is one queue for all textures waiting to be sent, regardless of the requesting user. + /// + private readonly BlockingQueue m_queueSenders + = new BlockingQueue(); + + /// + /// Each user has their own texture download service. + /// + private readonly Dictionary m_userTextureServices = + new Dictionary(); + + private Scene m_scene; + private List m_scenes = new List(); + + public TextureDownloadModule() + { + } + + #region IRegionModule Members + + public void Initialise(Scene scene, IConfigSource config) + { + + if (m_scene == null) + { + //m_log.Debug("Creating Texture download module"); + m_scene = scene; + //m_thread = new Thread(new ThreadStart(ProcessTextureSenders)); + //m_thread.Name = "ProcessTextureSenderThread"; + //m_thread.IsBackground = true; + //m_thread.Start(); + //ThreadTracker.Add(m_thread); + } + + if (!m_scenes.Contains(scene)) + { + m_scenes.Add(scene); + m_scene = scene; + m_scene.EventManager.OnNewClient += NewClient; + m_scene.EventManager.OnRemovePresence += EventManager_OnRemovePresence; + } + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public string Name + { + get { return "TextureDownloadModule"; } + } + + public bool IsSharedModule + { + get { return false; } + } + + #endregion + + /// + /// Cleanup the texture service related objects for the removed presence. + /// + /// + private void EventManager_OnRemovePresence(UUID agentId) + { + UserTextureDownloadService textureService; + + lock (m_userTextureServices) + { + if (m_userTextureServices.TryGetValue(agentId, out textureService)) + { + textureService.Close(); + //m_log.DebugFormat("[TEXTURE MODULE]: Removing UserTextureServices from {0}", m_scene.RegionInfo.RegionName); + m_userTextureServices.Remove(agentId); + } + } + } + + public void NewClient(IClientAPI client) + { + UserTextureDownloadService textureService; + + lock (m_userTextureServices) + { + if (m_userTextureServices.TryGetValue(client.AgentId, out textureService)) + { + textureService.Close(); + //m_log.DebugFormat("[TEXTURE MODULE]: Removing outdated UserTextureServices from {0}", m_scene.RegionInfo.RegionName); + m_userTextureServices.Remove(client.AgentId); + } + m_userTextureServices.Add(client.AgentId, new UserTextureDownloadService(client, m_scene, m_queueSenders)); + } + + client.OnRequestTexture += TextureRequest; + } + + /// I'm commenting this out, and replacing it with the implementation below, which + /// may return a null value. This is necessary for avoiding race conditions + /// recreating UserTextureServices for clients that have just been closed. + /// That behavior of always returning a UserTextureServices was causing the + /// A-B-A problem (mantis #2855). + /// + ///// + ///// Does this user have a registered texture download service? + ///// + ///// + ///// + ///// Always returns true, since a service is created if one does not already exist + //private bool TryGetUserTextureService( + // IClientAPI client, out UserTextureDownloadService textureService) + //{ + // lock (m_userTextureServices) + // { + // if (m_userTextureServices.TryGetValue(client.AgentId, out textureService)) + // { + // //m_log.DebugFormat("[TEXTURE MODULE]: Found existing UserTextureServices in ", m_scene.RegionInfo.RegionName); + // return true; + // } + + // m_log.DebugFormat("[TEXTURE MODULE]: Creating new UserTextureServices in ", m_scene.RegionInfo.RegionName); + // textureService = new UserTextureDownloadService(client, m_scene, m_queueSenders); + // m_userTextureServices.Add(client.AgentId, textureService); + + // return true; + // } + //} + + /// + /// Does this user have a registered texture download service? + /// + /// + /// + /// A UserTextureDownloadService or null in the output parameter, and true or false accordingly. + private bool TryGetUserTextureService(IClientAPI client, out UserTextureDownloadService textureService) + { + lock (m_userTextureServices) + { + if (m_userTextureServices.TryGetValue(client.AgentId, out textureService)) + { + //m_log.DebugFormat("[TEXTURE MODULE]: Found existing UserTextureServices in ", m_scene.RegionInfo.RegionName); + return true; + } + + textureService = null; + return false; + } + } + + /// + /// Start the process of requesting a given texture. + /// + /// + /// + public void TextureRequest(Object sender, TextureRequestArgs e) + { + IClientAPI client = (IClientAPI)sender; + + if (e.Priority == 1016001f) // Preview + { + if (client.Scene is Scene) + { + Scene scene = (Scene)client.Scene; + + CachedUserInfo profile = scene.CommsManager.UserProfileCacheService.GetUserDetails(client.AgentId); + if (profile == null) // Deny unknown user + return; + + IInventoryService invService = scene.InventoryService; + if (invService.GetRootFolder(client.AgentId) == null) // Deny no inventory + return; + + // Diva 2009-08-13: this test doesn't make any sense to many devs + //if (profile.UserProfile.GodLevel < 200 && profile.RootFolder.FindAsset(e.RequestedAssetID) == null) // Deny if not owned + //{ + // m_log.WarnFormat("[TEXTURE]: user {0} doesn't have permissions to texture {1}"); + // return; + //} + + m_log.Debug("Texture preview"); + } + } + + UserTextureDownloadService textureService; + + if (TryGetUserTextureService(client, out textureService)) + { + textureService.HandleTextureRequest(e); + } + } + + /// + /// Entry point for the thread dedicated to processing the texture queue. + /// + public void ProcessTextureSenders() + { + ITextureSender sender = null; + + try + { + while (true) + { + sender = m_queueSenders.Dequeue(); + + if (sender.Cancel) + { + TextureSent(sender); + + sender.Cancel = false; + } + else + { + bool finished = sender.SendTexturePacket(); + if (finished) + { + TextureSent(sender); + } + else + { + m_queueSenders.Enqueue(sender); + } + } + + // Make sure that any sender we currently have can get garbage collected + sender = null; + + //m_log.InfoFormat("[TEXTURE] Texture sender queue size: {0}", m_queueSenders.Count()); + } + } + catch (Exception e) + { + // TODO: Let users in the sim and those entering it and possibly an external watchdog know what has happened + m_log.ErrorFormat( + "[TEXTURE]: Texture send thread terminating with exception. PLEASE REBOOT YOUR SIM - TEXTURES WILL NOT BE AVAILABLE UNTIL YOU DO. Exception is {0}", + e); + } + } + + /// + /// Called when the texture has finished sending. + /// + /// + private void TextureSent(ITextureSender sender) + { + sender.Sending = false; + //m_log.DebugFormat("[TEXTURE]: Removing download stat for {0}", sender.assetID); + m_scene.StatsReporter.AddPendingDownloads(-1); + } + } +} diff --git a/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureNotFoundSender.cs b/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureNotFoundSender.cs new file mode 100644 index 0000000000..ba735a71bc --- /dev/null +++ b/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureNotFoundSender.cs @@ -0,0 +1,87 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.CoreModules.Agent.TextureDownload +{ + /// + /// Sends a 'texture not found' packet back to the client + /// + public class TextureNotFoundSender : ITextureSender + { + // private static readonly log4net.ILog m_log + // = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + // private IClientAPI m_client; + // private UUID m_textureId; + + public TextureNotFoundSender(IClientAPI client, UUID textureID) + { + //m_client = client; + //m_textureId = textureID; + } + + #region ITextureSender Members + + public bool Sending + { + get { return false; } + set { } + } + + public bool Cancel + { + get { return false; } + set { } + } + + // See ITextureSender + public void UpdateRequest(int discardLevel, uint packetNumber) + { + // No need to implement since priority changes don't affect this operation + } + + // See ITextureSender + public bool SendTexturePacket() + { + // m_log.DebugFormat( + // "[TEXTURE NOT FOUND SENDER]: Informing the client that texture {0} cannot be found", + // m_textureId); + + // XXX Temporarily disabling as this appears to be causing client crashes on at least + // 1.19.0(5) of the Linden Second Life client. + // m_client.SendImageNotFound(m_textureId); + + return true; + } + + #endregion + } +} diff --git a/OpenSim/Region/CoreModules/Agent/TextureDownload/UserTextureDownloadService.cs b/OpenSim/Region/CoreModules/Agent/TextureDownload/UserTextureDownloadService.cs new file mode 100644 index 0000000000..19f0f90486 --- /dev/null +++ b/OpenSim/Region/CoreModules/Agent/TextureDownload/UserTextureDownloadService.cs @@ -0,0 +1,265 @@ +/* + * 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.Framework.Communications.Limit; +using OpenSim.Framework.Statistics; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.CoreModules.Agent.TextureDownload +{ + /// + /// This module sets up texture senders in response to client texture requests, and places them on a + /// processing queue once those senders have the appropriate data (i.e. a texture retrieved from the + /// asset cache). + /// + public class UserTextureDownloadService + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// True if the service has been closed, probably because a user with texture requests still queued + /// logged out. + /// + private bool closed; + + /// + /// We will allow the client to request the same texture n times before dropping further requests + /// + /// This number includes repeated requests for the same texture at different resolutions (which we don't + /// currently handle properly as far as I know). However, this situation should be handled in a more + /// sophisticated way. + /// +// private static readonly int MAX_ALLOWED_TEXTURE_REQUESTS = 5; + + /// + /// XXX Also going to limit requests for found textures. + /// +// private readonly IRequestLimitStrategy foundTextureLimitStrategy +// = new RepeatLimitStrategy(MAX_ALLOWED_TEXTURE_REQUESTS); + +// private readonly IClientAPI m_client; + private readonly Scene m_scene; + + /// + /// Texture Senders are placed in this queue once they have received their texture from the asset + /// cache. Another module actually invokes the send. + /// +// private readonly OpenSim.Framework.BlockingQueue m_sharedSendersQueue; + + /// + /// Holds texture senders before they have received the appropriate texture from the asset cache. + /// + private readonly Dictionary m_textureSenders = new Dictionary(); + + /// + /// We're going to limit requests for the same missing texture. + /// XXX This is really a temporary solution to deal with the situation where a client continually requests + /// the same missing textures + /// +// private readonly IRequestLimitStrategy missingTextureLimitStrategy +// = new RepeatLimitStrategy(MAX_ALLOWED_TEXTURE_REQUESTS); + + public UserTextureDownloadService( + IClientAPI client, Scene scene, OpenSim.Framework.BlockingQueue sharedQueue) + { +// m_client = client; + m_scene = scene; +// m_sharedSendersQueue = sharedQueue; + } + + /// + /// Handle a texture request. This involves creating a texture sender and placing it on the + /// previously passed in shared queue. + /// + /// + public void HandleTextureRequest(TextureRequestArgs e) + { + + //TextureSender.TextureSender textureSender; + + //TODO: should be working out the data size/ number of packets to be sent for each discard level + //if ((e.DiscardLevel >= 0) || (e.Priority != 0)) + //{ + //lock (m_textureSenders) + //{ + //if (m_textureSenders.TryGetValue(e.RequestedAssetID, out textureSender)) + //{ + // If we've received new non UUID information for this request and it hasn't dispatched + // yet, then update the request accordingly. + // textureSender.UpdateRequest(e.DiscardLevel, e.PacketNumber); + //} + //else + //{ + // m_log.DebugFormat("[TEXTURE]: Received a request for texture {0}", e.RequestedAssetID); + + //if (!foundTextureLimitStrategy.AllowRequest(e.RequestedAssetID)) + //{ + // m_log.DebugFormat( + // "[TEXTURE]: Refusing request for {0} from client {1}", + // e.RequestedAssetID, m_client.AgentId); + + //return; + //} + //else if (!missingTextureLimitStrategy.AllowRequest(e.RequestedAssetID)) + //{ + // if (missingTextureLimitStrategy.IsFirstRefusal(e.RequestedAssetID)) + // { + // if (StatsManager.SimExtraStats != null) + // StatsManager.SimExtraStats.AddBlockedMissingTextureRequest(); + + // Commenting out this message for now as it causes too much noise with other + // debug messages. + // m_log.DebugFormat( + // "[TEXTURE]: Dropping requests for notified missing texture {0} for client {1} since we have received more than {2} requests", + // e.RequestedAssetID, m_client.AgentId, MAX_ALLOWED_TEXTURE_REQUESTS); + // } + + // return; + //} + + m_scene.StatsReporter.AddPendingDownloads(1); + + //TextureSender.TextureSender requestHandler = new TextureSender.TextureSender(m_client, e.DiscardLevel, e.PacketNumber); + //m_textureSenders.Add(e.RequestedAssetID, null); + + m_scene.AssetService.Get(e.RequestedAssetID.ToString(), this, TextureReceived); + + + } + + protected void TextureReceived(string id, Object sender, AssetBase asset) + { + if (asset != null) + TextureCallback(asset.FullID, asset); + } + + /// + /// The callback for the asset cache when a texture has been retrieved. This method queues the + /// texture sender for processing. + /// + /// + /// + public void TextureCallback(UUID textureID, AssetBase texture) + { + //m_log.DebugFormat("[USER TEXTURE DOWNLOAD SERVICE]: Calling TextureCallback with {0}, texture == null is {1}", textureID, (texture == null ? true : false)); + + // There may still be texture requests pending for a logged out client + if (closed) + return; + + /* + lock (m_textureSenders) + { + TextureSender.TextureSender textureSender; + if (m_textureSenders.TryGetValue(textureID, out textureSender)) + { + // XXX It may be perfectly valid for a texture to have no data... but if we pass + // this on to the TextureSender it will blow up, so just discard for now. + // Needs investigation. + if (texture == null || texture.Data == null) + { + if (!missingTextureLimitStrategy.IsMonitoringRequests(textureID)) + { + missingTextureLimitStrategy.MonitorRequests(textureID); + + // m_log.DebugFormat( + // "[TEXTURE]: Queueing first TextureNotFoundSender for {0}, client {1}", + // textureID, m_client.AgentId); + } + + ITextureSender textureNotFoundSender = new TextureNotFoundSender(m_client, textureID); + EnqueueTextureSender(textureNotFoundSender); + } + else + { + if (!textureSender.ImageLoaded) + { + textureSender.TextureReceived(texture); + EnqueueTextureSender(textureSender); + + foundTextureLimitStrategy.MonitorRequests(textureID); + } + } + + //m_log.InfoFormat("[TEXTURE] Removing texture sender with uuid {0}", textureID); + m_textureSenders.Remove(textureID); + //m_log.InfoFormat("[TEXTURE] Current texture senders in dictionary: {0}", m_textureSenders.Count); + } + else + { + m_log.WarnFormat( + "[TEXTURE]: Got a texture uuid {0} with no sender object to handle it, this shouldn't happen", + textureID); + } + } + */ + } + + /// + /// Place a ready texture sender on the processing queue. + /// + /// +// private void EnqueueTextureSender(ITextureSender textureSender) +// { +// textureSender.Cancel = false; +// textureSender.Sending = true; +// +// if (!m_sharedSendersQueue.Contains(textureSender)) +// { +// m_sharedSendersQueue.Enqueue(textureSender); +// } +// } + + /// + /// Close this module. + /// + internal void Close() + { + closed = true; + + lock (m_textureSenders) + { + foreach (TextureSender.TextureSender textureSender in m_textureSenders.Values) + { + textureSender.Cancel = true; + } + + m_textureSenders.Clear(); + } + + // XXX: It might be possible to also remove pending texture requests from the asset cache queues, + // though this might also be more trouble than it's worth. + } + } +} diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/TextureSender.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/TextureSender.cs new file mode 100644 index 0000000000..62c5a32169 --- /dev/null +++ b/OpenSim/Region/CoreModules/Agent/TextureSender/TextureSender.cs @@ -0,0 +1,212 @@ +/* + * 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.Reflection; +using log4net; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.CoreModules.Agent.TextureSender +{ + /// + /// A TextureSender handles the process of receiving a texture requested by the client from the + /// AssetCache, and then sending that texture back to the client. + /// + public class TextureSender : ITextureSender + { + private static readonly ILog m_log + = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Records the number of times texture send has been called. + /// + public int counter = 0; + + public bool ImageLoaded = false; + + /// + /// Holds the texture asset to send. + /// + private AssetBase m_asset; + + //public UUID assetID { get { return m_asset.FullID; } } + + // private bool m_cancel = false; + + // See ITextureSender + + // private bool m_sending = false; + + /// + /// This is actually the number of extra packets required to send the texture data! We always assume + /// at least one is required. + /// + private int NumPackets = 0; + + /// + /// Holds the packet number to send next. In this case, each packet is 1000 bytes long and starts + /// at the 600th byte (0th indexed). + /// + private int PacketCounter = 0; + + private int RequestedDiscardLevel = -1; + private IClientAPI RequestUser; + private uint StartPacketNumber = 0; + + public TextureSender(IClientAPI client, int discardLevel, uint packetNumber) + { + RequestUser = client; + RequestedDiscardLevel = discardLevel; + StartPacketNumber = packetNumber; + } + + #region ITextureSender Members + + public bool Cancel + { + get { return false; } + set + { + // m_cancel = value; + } + } + + public bool Sending + { + get { return false; } + set + { + // m_sending = value; + } + } + + // See ITextureSender + public void UpdateRequest(int discardLevel, uint packetNumber) + { + RequestedDiscardLevel = discardLevel; + StartPacketNumber = packetNumber; + PacketCounter = (int)StartPacketNumber; + } + + // See ITextureSender + public bool SendTexturePacket() + { + //m_log.DebugFormat("[TEXTURE SENDER]: Sending packet for {0}", m_asset.FullID); + + SendPacket(); + counter++; + if ((NumPackets == 0) || (RequestedDiscardLevel == -1) || (PacketCounter > NumPackets) || + ((RequestedDiscardLevel > 0) && (counter > 50 + (NumPackets / (RequestedDiscardLevel + 1))))) + { + return true; + } + return false; + } + + #endregion + + /// + /// Load up the texture data to send. + /// + /// + public void TextureReceived(AssetBase asset) + { + m_asset = asset; + NumPackets = CalculateNumPackets(asset.Data.Length); + PacketCounter = (int)StartPacketNumber; + ImageLoaded = true; + } + + /// + /// Sends a texture packet to the client. + /// + private void SendPacket() + { + if (PacketCounter <= NumPackets) + { + if (PacketCounter == 0) + { + if (NumPackets == 0) + { + RequestUser.SendImageFirstPart(1, m_asset.FullID, (uint)m_asset.Data.Length, m_asset.Data, 2); + PacketCounter++; + } + else + { + byte[] ImageData1 = new byte[600]; + Array.Copy(m_asset.Data, 0, ImageData1, 0, 600); + + RequestUser.SendImageFirstPart( + (ushort)(NumPackets), m_asset.FullID, (uint)m_asset.Data.Length, ImageData1, 2); + PacketCounter++; + } + } + else + { + int size = m_asset.Data.Length - 600 - (1000 * (PacketCounter - 1)); + if (size > 1000) size = 1000; + byte[] imageData = new byte[size]; + try + { + Array.Copy(m_asset.Data, 600 + (1000 * (PacketCounter - 1)), imageData, 0, size); + } + catch (ArgumentOutOfRangeException) + { + m_log.Error("[TEXTURE SENDER]: Unable to separate texture into multiple packets: Array bounds failure on asset:" + + m_asset.ID); + return; + } + + RequestUser.SendImageNextPart((ushort)PacketCounter, m_asset.FullID, imageData); + PacketCounter++; + } + } + } + + /// + /// Calculate the number of packets that will be required to send the texture loaded into this sender + /// This is actually the number of 1000 byte packets not including an initial 600 byte packet... + /// + /// + /// + private int CalculateNumPackets(int length) + { + int numPackets = 0; + + if (length > 600) + { + //over 600 bytes so split up file + int restData = (length - 600); + int restPackets = ((restData + 999) / 1000); + numPackets = restPackets; + } + + return numPackets; + } + } +} diff --git a/OpenSim/Region/Framework/Interfaces/ITextureSender.cs b/OpenSim/Region/Framework/Interfaces/ITextureSender.cs new file mode 100644 index 0000000000..c469ae8c9c --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/ITextureSender.cs @@ -0,0 +1,58 @@ +/* + * 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. + */ + +namespace OpenSim.Region.Framework.Interfaces +{ + /// + /// Interface for an object which can send texture information to a client + /// + public interface ITextureSender + { + /// + /// Are we in the process of sending the texture? + /// + bool Sending { get; set; } + + /// + /// Has the texture send been cancelled? + /// + bool Cancel { get; set; } + + /// + /// Update the non data properties of a texture request + /// + /// + /// + void UpdateRequest(int discardLevel, uint packetNumber); + + /// + /// Send a texture packet to the client. + /// + /// True if the last packet has been sent, false otherwise. + bool SendTexturePacket(); + } +} From 99abe885c812e42229d1e1b4fa58cd0a34edf859 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 20 Oct 2009 12:30:34 -0700 Subject: [PATCH 37/61] Fixing position/rotation/collisionplane in ObjectUpdate packets for avatars --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 88faccf09f..dce9469ebb 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4285,12 +4285,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(SendAvatarData data) { - byte[] objectData = new byte[60]; - data.Position.ToBytes(objectData, 0); - //data.Velocity.ToBytes(objectData, 12); - //data.Acceleration.ToBytes(objectData, 24); - data.Rotation.ToBytes(objectData, 36); - //data.AngularVelocity.ToBytes(objectData, 48); + byte[] objectData = new byte[76]; + + Vector4.UnitW.ToBytes(objectData, 0); // TODO: Collision plane support + data.Position.ToBytes(objectData, 16); + //data.Velocity.ToBytes(objectData, 28); + //data.Acceleration.ToBytes(objectData, 40); + data.Rotation.ToBytes(objectData, 52); + //data.AngularVelocity.ToBytes(objectData, 64); ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); From d1ab11dc2a60ddab0cca214a1c165e386dbb5d43 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 20 Oct 2009 12:43:09 -0700 Subject: [PATCH 38/61] Added try/catches in the outgoing packet handler to match the one in the incoming packet handler --- .../ClientStack/LindenUDP/LLUDPServer.cs | 104 ++++++++++-------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index a6ead5e054..3881bdbc83 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -776,61 +776,75 @@ namespace OpenSim.Region.ClientStack.LindenUDP while (base.IsRunning) { - bool resendUnacked = false; - bool sendAcks = false; - bool sendPings = false; - bool packetSent = false; - - elapsedMS += Environment.TickCount - now; - - // Check for pending outgoing resends every 100ms - if (elapsedMS >= 100) + try { - resendUnacked = true; - elapsedMS -= 100; - ++elapsed100MS; - } - // Check for pending outgoing ACKs every 500ms - if (elapsed100MS >= 5) - { - sendAcks = true; - elapsed100MS = 0; - ++elapsed500MS; - } - // Send pings to clients every 5000ms - if (elapsed500MS >= 10) - { - sendPings = true; - elapsed500MS = 0; - } + bool resendUnacked = false; + bool sendAcks = false; + bool sendPings = false; + bool packetSent = false; - m_scene.ClientManager.ForEachSync( - delegate(IClientAPI client) + elapsedMS += Environment.TickCount - now; + + // Check for pending outgoing resends every 100ms + if (elapsedMS >= 100) { - if (client is LLClientView) - { - LLUDPClient udpClient = ((LLClientView)client).UDPClient; + resendUnacked = true; + elapsedMS -= 100; + ++elapsed100MS; + } + // Check for pending outgoing ACKs every 500ms + if (elapsed100MS >= 5) + { + sendAcks = true; + elapsed100MS = 0; + ++elapsed500MS; + } + // Send pings to clients every 5000ms + if (elapsed500MS >= 10) + { + sendPings = true; + elapsed500MS = 0; + } - if (udpClient.IsConnected) + m_scene.ClientManager.ForEachSync( + delegate(IClientAPI client) + { + try { - if (udpClient.DequeueOutgoing()) - packetSent = true; - if (resendUnacked) - ResendUnacked(udpClient); - if (sendAcks) + if (client is LLClientView) { - SendAcks(udpClient); - udpClient.SendPacketStats(); + LLUDPClient udpClient = ((LLClientView)client).UDPClient; + + if (udpClient.IsConnected) + { + if (udpClient.DequeueOutgoing()) + packetSent = true; + if (resendUnacked) + ResendUnacked(udpClient); + if (sendAcks) + { + SendAcks(udpClient); + udpClient.SendPacketStats(); + } + if (sendPings) + SendPing(udpClient); + } } - if (sendPings) - SendPing(udpClient); + } + catch (Exception ex) + { + m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler iteration for " + client.Name + " threw an exception: " + ex.Message, ex); } } - } - ); + ); - if (!packetSent) - Thread.Sleep(20); + if (!packetSent) + Thread.Sleep(20); + } + catch (Exception ex) + { + m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler loop threw an exception: " + ex.Message, ex); + } } } From d38f33736c371cf8c09d78ee5c42b8cc943bb1d7 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 20 Oct 2009 14:41:20 -0700 Subject: [PATCH 39/61] * Removed the throttle speed optimizations to see if it brings stability back * Changed the outgoing packet handler to use a real function instead of a closure and to track time on a per-client basis instead of a global basis --- .../ClientStack/LindenUDP/LLUDPClient.cs | 25 ++-- .../ClientStack/LindenUDP/LLUDPServer.cs | 122 ++++++++---------- 2 files changed, 72 insertions(+), 75 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 4a3a04e8fb..ec74188f7f 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -101,6 +101,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public bool IsPaused = true; /// Environment.TickCount when the last packet was received for this client public int TickLastPacketReceived; + /// Environment.TickCount of the last time the outgoing packet handler executed for this client + public int TickLastOutgoingPacketHandler; /// Timer granularity. This is set to the measured resolution of Environment.TickCount public readonly float G; @@ -320,27 +322,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP bucket.MaxBurst = total; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; - bucket.DripRate = bucket.MaxBurst = resend; + bucket.DripRate = resend; + bucket.MaxBurst = resend; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; - bucket.DripRate = bucket.MaxBurst = land; + bucket.DripRate = land; + bucket.MaxBurst = land; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; - bucket.DripRate = bucket.MaxBurst = wind; + bucket.DripRate = wind; + bucket.MaxBurst = wind; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; - bucket.DripRate = bucket.MaxBurst = cloud; + bucket.DripRate = cloud; + bucket.MaxBurst = cloud; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; - bucket.DripRate = bucket.MaxBurst = asset; + bucket.DripRate = asset; + bucket.MaxBurst = asset; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; - bucket.DripRate = task + state + texture; - bucket.MaxBurst = task + state + texture; + bucket.DripRate = task + state; + bucket.MaxBurst = task + state; bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; - bucket.DripRate = state + texture; - bucket.MaxBurst = state + texture; + bucket.DripRate = state; + bucket.MaxBurst = state; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; bucket.DripRate = texture; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 3881bdbc83..80ef95e422 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -118,6 +118,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_recvBufferSize; /// Flag to process packets asynchronously or synchronously private bool m_asyncPacketHandling; + /// Track whether or not a packet was sent in the + /// OutgoingPacketHandler loop so we know when to sleep + private bool m_packetSentLastLoop; /// The measured resolution of Environment.TickCount public float TickCountResolution { get { return m_tickCountResolution; } } @@ -745,10 +748,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP while (base.IsRunning) { - IncomingPacket incomingPacket = null; - try { + IncomingPacket incomingPacket = null; + if (packetInbox.Dequeue(100, ref incomingPacket)) Util.FireAndForget(ProcessInPacket, incomingPacket); } @@ -769,76 +772,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP // on to en-US to avoid number parsing issues Culture.SetCurrentCulture(); - int now = Environment.TickCount; - int elapsedMS = 0; - int elapsed100MS = 0; - int elapsed500MS = 0; - while (base.IsRunning) { try { - bool resendUnacked = false; - bool sendAcks = false; - bool sendPings = false; - bool packetSent = false; + m_packetSentLastLoop = false; - elapsedMS += Environment.TickCount - now; + m_scene.ClientManager.ForEachSync(ClientOutgoingPacketHandler); - // Check for pending outgoing resends every 100ms - if (elapsedMS >= 100) - { - resendUnacked = true; - elapsedMS -= 100; - ++elapsed100MS; - } - // Check for pending outgoing ACKs every 500ms - if (elapsed100MS >= 5) - { - sendAcks = true; - elapsed100MS = 0; - ++elapsed500MS; - } - // Send pings to clients every 5000ms - if (elapsed500MS >= 10) - { - sendPings = true; - elapsed500MS = 0; - } - - m_scene.ClientManager.ForEachSync( - delegate(IClientAPI client) - { - try - { - if (client is LLClientView) - { - LLUDPClient udpClient = ((LLClientView)client).UDPClient; - - if (udpClient.IsConnected) - { - if (udpClient.DequeueOutgoing()) - packetSent = true; - if (resendUnacked) - ResendUnacked(udpClient); - if (sendAcks) - { - SendAcks(udpClient); - udpClient.SendPacketStats(); - } - if (sendPings) - SendPing(udpClient); - } - } - } - catch (Exception ex) - { - m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler iteration for " + client.Name + " threw an exception: " + ex.Message, ex); - } - } - ); - - if (!packetSent) + // If no packets at all were sent, sleep to avoid chewing up CPU cycles + // when there is nothing to do + if (!m_packetSentLastLoop) Thread.Sleep(20); } catch (Exception ex) @@ -848,6 +792,52 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } + private void ClientOutgoingPacketHandler(IClientAPI client) + { + try + { + if (client is LLClientView) + { + LLUDPClient udpClient = ((LLClientView)client).UDPClient; + + int thisTick = Environment.TickCount & Int32.MaxValue; + int elapsedMS = thisTick - udpClient.TickLastOutgoingPacketHandler; + + if (udpClient.IsConnected) + { + // Check for pending outgoing resends every 100ms + if (elapsedMS >= 100) + { + ResendUnacked(udpClient); + + // Check for pending outgoing ACKs every 500ms + if (elapsedMS >= 500) + { + SendAcks(udpClient); + + // Send pings to clients every 5000ms + if (elapsedMS >= 5000) + { + SendPing(udpClient); + } + } + } + + // Dequeue any outgoing packets that are within the throttle limits + if (udpClient.DequeueOutgoing()) + m_packetSentLastLoop = true; + } + + udpClient.TickLastOutgoingPacketHandler = thisTick; + } + } + catch (Exception ex) + { + m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler iteration for " + client.Name + + " threw an exception: " + ex.Message, ex); + } + } + private void ProcessInPacket(object state) { IncomingPacket incomingPacket = (IncomingPacket)state; From 1833c6956892f1c8324ecbe0179103bff2079151 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 20 Oct 2009 15:19:19 -0700 Subject: [PATCH 40/61] * Removed the unused m_agentUpdates collection and some extra work that was being done for AgentUpdate packets * Start LLUDPClients unpaused (this variable is not being used yet) --- .../ClientStack/LindenUDP/LLUDPClient.cs | 2 +- .../Region/Framework/Scenes/ScenePresence.cs | 126 +++++------------- 2 files changed, 34 insertions(+), 94 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index ec74188f7f..bf0fda3d92 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -98,7 +98,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// True when this connection is alive, otherwise false public bool IsConnected = true; /// True when this connection is paused, otherwise false - public bool IsPaused = true; + public bool IsPaused; /// Environment.TickCount when the last packet was received for this client public int TickLastPacketReceived; /// Environment.TickCount of the last time the outgoing packet handler executed for this client diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index bdd80c6fad..d7113bf9cc 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -128,8 +128,6 @@ namespace OpenSim.Region.Framework.Scenes private bool m_setAlwaysRun; - private bool m_updatesAllowed = true; - private List m_agentUpdates = new List(); private string m_movementAnimation = "DEFAULT"; private long m_animPersistUntil = 0; private bool m_allowFalling = false; @@ -1090,34 +1088,6 @@ namespace OpenSim.Region.Framework.Scenes } - // These methods allow to queue up agent updates (like key presses) - // until all attachment scripts are running and the animations from - // AgentDataUpdate have been started. It is essential for combat - // devices, weapons and AOs that keypresses are not processed - // until scripts that are potentially interested in them are - // up and running and that animations a script knows to be running - // from before a crossing are running again - // - public void LockAgentUpdates() - { - m_updatesAllowed = false; - } - - public void UnlockAgentUpdates() - { - lock (m_agentUpdates) - { - if (m_updatesAllowed == false) - { - foreach (AgentUpdateArgs a in m_agentUpdates) - RealHandleAgentUpdate(ControllingClient, a); - m_agentUpdates.Clear(); - m_updatesAllowed = true; - } - } - } - - /// /// Callback for the Camera view block check. Gets called with the results of the camera view block test /// hitYN is true when there's something in the way. @@ -1155,30 +1125,12 @@ namespace OpenSim.Region.Framework.Scenes } } + Array m_dirControlFlags = Enum.GetValues(typeof(Dir_ControlFlags)); + /// /// This is the event handler for client movement. If a client is moving, this event is triggering. /// public void HandleAgentUpdate(IClientAPI remoteClient, AgentUpdateArgs agentData) - { - const int AGENT_UPDATE_TIMEOUT_MS = 1000 * 3; - - if (System.Threading.Monitor.TryEnter(m_agentUpdates, AGENT_UPDATE_TIMEOUT_MS)) - { - try - { - if (m_updatesAllowed) - { - RealHandleAgentUpdate(remoteClient, agentData); - return; - } - - m_agentUpdates.Add(agentData); - } - finally { System.Threading.Monitor.Exit(m_agentUpdates); } - } - } - - private void RealHandleAgentUpdate(IClientAPI remoteClient, AgentUpdateArgs agentData) { //if (m_isChildAgent) //{ @@ -1186,18 +1138,17 @@ namespace OpenSim.Region.Framework.Scenes // return; //} - - m_movementUpdateCount++; - if (m_movementUpdateCount >= int.MaxValue) - m_movementUpdateCount = 1; + m_perfMonMS = Environment.TickCount; + ++m_movementUpdateCount; + if (m_movementUpdateCount < 1) + m_movementUpdateCount = 1; // Must check for standing up even when PhysicsActor is null, // since sitting currently removes avatar from physical scene //m_log.Debug("agentPos:" + AbsolutePosition.ToString()); // This is irritating. Really. - if (!AbsolutePosition.IsFinite()) { RemoveFromPhysicalScene(); @@ -1218,19 +1169,17 @@ namespace OpenSim.Region.Framework.Scenes { m_LastFinitePos = m_pos; } - //m_physicsActor.AddForce(new PhysicsVector(999999999, 99999999, 999999999999999), true); + //m_physicsActor.AddForce(new PhysicsVector(999999999, 99999999, 999999999999999), true); //ILandObject land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y); //if (land != null) //{ - //if (land.landData.landingType == (byte)1 && land.landData.userLocation != Vector3.Zero) - //{ - // agent.startpos = land.landData.userLocation; - //} + //if (land.landData.landingType == (byte)1 && land.landData.userLocation != Vector3.Zero) + //{ + // agent.startpos = land.landData.userLocation; + //} //} - - m_perfMonMS = Environment.TickCount; uint flags = agentData.ControlFlags; Quaternion bodyRotation = agentData.BodyRotation; @@ -1253,7 +1202,7 @@ namespace OpenSim.Region.Framework.Scenes // The Agent's Draw distance setting m_DrawDistance = agentData.Far; - if ((flags & (uint) AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) != 0) + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) != 0) { StandUp(); } @@ -1261,14 +1210,13 @@ namespace OpenSim.Region.Framework.Scenes // Check if Client has camera in 'follow cam' or 'build' mode. Vector3 camdif = (Vector3.One * m_bodyRot - Vector3.One * CameraRotation); - m_followCamAuto = ((m_CameraUpAxis.Z > 0.959f && m_CameraUpAxis.Z < 0.98f) + m_followCamAuto = ((m_CameraUpAxis.Z > 0.959f && m_CameraUpAxis.Z < 0.98f) && (Math.Abs(camdif.X) < 0.4f && Math.Abs(camdif.Y) < 0.4f)) ? true : false; //m_log.DebugFormat("[FollowCam]: {0}", m_followCamAuto); // Raycast from the avatar's head to the camera to see if there's anything blocking the view if ((m_movementUpdateCount % NumMovementsBetweenRayCast) == 0 && m_scene.PhysicsScene.SupportsRayCast()) { - if (m_followCamAuto) { Vector3 headadjustment = new Vector3(0, 0, 0.3f); @@ -1276,24 +1224,18 @@ namespace OpenSim.Region.Framework.Scenes } } - m_mouseLook = (flags & (uint) AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0; - - - + m_mouseLook = (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0; m_leftButtonDown = (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN) != 0; - - lock (scriptedcontrols) { if (scriptedcontrols.Count > 0) { SendControlToScripts(flags); flags = RemoveIgnoredControls(flags, IgnoredControls); - } } - + if (PhysicsActor == null) { return; @@ -1302,7 +1244,7 @@ namespace OpenSim.Region.Framework.Scenes if (m_autopilotMoving) CheckAtSitTarget(); - if ((flags & (uint) AgentManager.ControlFlags.AGENT_CONTROL_SIT_ON_GROUND) != 0) + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_SIT_ON_GROUND) != 0) { // TODO: This doesn't prevent the user from walking yet. // Setting parent ID would fix this, if we knew what value @@ -1335,13 +1277,13 @@ namespace OpenSim.Region.Framework.Scenes PhysicsActor.Flying = false; else PhysicsActor.Flying = ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0); - + if (PhysicsActor.Flying != oldflying) { update_movementflag = true; } } - + if (q != m_bodyRot) { m_bodyRot = q; @@ -1357,15 +1299,15 @@ namespace OpenSim.Region.Framework.Scenes // use camera up angle when in mouselook and not flying or when holding the left mouse button down and not flying // this prevents 'jumping' in inappropriate situations. - if ((m_mouseLook && !m_physicsActor.Flying) || (m_leftButtonDown && !m_physicsActor.Flying)) + if ((m_mouseLook && !m_physicsActor.Flying) || (m_leftButtonDown && !m_physicsActor.Flying)) dirVectors = GetWalkDirectionVectors(); else dirVectors = Dir_Vectors; - foreach (Dir_ControlFlags DCF in Enum.GetValues(typeof (Dir_ControlFlags))) + foreach (Dir_ControlFlags DCF in m_dirControlFlags) { - if ((flags & (uint) DCF) != 0) + if ((flags & (uint)DCF) != 0) { bResetMoveToPosition = true; DCFlagKeyPressed = true; @@ -1377,18 +1319,18 @@ namespace OpenSim.Region.Framework.Scenes { // Why did I get this? } - - if ((m_movementflag & (uint) DCF) == 0) + + if ((m_movementflag & (uint)DCF) == 0) { - m_movementflag += (byte) (uint) DCF; + m_movementflag += (byte)(uint)DCF; update_movementflag = true; } } else { - if ((m_movementflag & (uint) DCF) != 0) + if ((m_movementflag & (uint)DCF) != 0) { - m_movementflag -= (byte) (uint) DCF; + m_movementflag -= (byte)(uint)DCF; update_movementflag = true; } else @@ -1479,14 +1421,12 @@ namespace OpenSim.Region.Framework.Scenes } catch (Exception) { - //Avoid system crash, can be slower but... } - } } } - + // Cause the avatar to stop flying if it's colliding // with something with the down arrow pressed. @@ -1494,8 +1434,8 @@ namespace OpenSim.Region.Framework.Scenes if (m_physicsActor != null && m_physicsActor.Flying && !m_forceFly) { // Are the landing controls requirements filled? - bool controlland = (((flags & (uint) AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0) || - ((flags & (uint) AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG) != 0)); + bool controlland = (((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0) || + ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG) != 0)); // Are the collision requirements fulfilled? bool colliding = (m_physicsActor.IsColliding == true); @@ -1508,10 +1448,10 @@ namespace OpenSim.Region.Framework.Scenes if (update_movementflag || (update_rotation && DCFlagKeyPressed)) { -// m_log.DebugFormat("{0} {1}", update_movementflag, (update_rotation && DCFlagKeyPressed)); -// m_log.DebugFormat( -// "In {0} adding velocity to {1} of {2}", m_scene.RegionInfo.RegionName, Name, agent_control_v3); - + // m_log.DebugFormat("{0} {1}", update_movementflag, (update_rotation && DCFlagKeyPressed)); + // m_log.DebugFormat( + // "In {0} adding velocity to {1} of {2}", m_scene.RegionInfo.RegionName, Name, agent_control_v3); + AddNewMovement(agent_control_v3, q); if (update_movementflag) From 45dc4e0a5442d1d03f7387164070145386a9b4e1 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 20 Oct 2009 18:19:17 -0700 Subject: [PATCH 41/61] * Added a sanity check to GetScriptAssemblies() and GetScriptStates() for the case where no scripting engine is enabled * Added TokenBucket.cs to OpenSim, with some fixes for setting a more accurate MaxBurst value and getting a more accurate Content value (by Drip()ing each get) --- .../ClientStack/LindenUDP/LLUDPClient.cs | 2 + .../ClientStack/LindenUDP/LLUDPServer.cs | 2 + .../ClientStack/LindenUDP/TokenBucket.cs | 213 ++++++++++++++++++ .../LindenUDP/UnackedPacketCollection.cs | 2 + .../Scenes/SceneObjectPartInventory.cs | 26 ++- 5 files changed, 235 insertions(+), 10 deletions(-) create mode 100644 OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index bf0fda3d92..134cfe500b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -33,6 +33,8 @@ using OpenSim.Framework; using OpenMetaverse; using OpenMetaverse.Packets; +using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket; + namespace OpenSim.Region.ClientStack.LindenUDP { #region Delegates diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 80ef95e422..952d147b65 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -39,6 +39,8 @@ using OpenSim.Framework.Statistics; using OpenSim.Region.Framework.Scenes; using OpenMetaverse; +using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket; + namespace OpenSim.Region.ClientStack.LindenUDP { /// diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs new file mode 100644 index 0000000000..0a64095779 --- /dev/null +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -0,0 +1,213 @@ +/* + * 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; + +namespace OpenSim.Region.ClientStack.LindenUDP +{ + /// + /// A hierarchical token bucket for bandwidth throttling. See + /// http://en.wikipedia.org/wiki/Token_bucket for more information + /// + 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; + + #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 + /// + public TokenBucket Parent + { + get { return parent; } + } + + /// + /// Maximum burst rate in bytes per second. This is the maximum number + /// of tokens that can accumulate in the bucket at any one time + /// + public int MaxBurst + { + get { return maxBurst; } + set { maxBurst = (value >= 0 ? value : 0); } + } + + /// + /// The speed limit of this bucket in bytes per second. This is the + /// number of tokens that are added to the bucket per second + /// + /// 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 + { + get { return tokensPerMS * 1000; } + set + { + if (value == 0) + tokensPerMS = 0; + else + { + int bpms = (int)((float)value / 1000.0f); + + if (bpms <= 0) + tokensPerMS = 1; // 1 byte/ms is the minimum granularity + else + tokensPerMS = bpms; + } + } + } + + /// + /// 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 + { + Drip(); + return content; + } + } + + #endregion Properties + + /// + /// Default constructor + /// + /// Parent bucket if this is a child bucket, or + /// null if this is a root bucket + /// Maximum size of the bucket in bytes, or + /// 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) + { + this.parent = parent; + MaxBurst = maxBurst; + DripRate = dripRate; + lastDrip = Environment.TickCount & Int32.MaxValue; + } + + /// + /// 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) + { + bool dummy; + return RemoveTokens(amount, out dummy); + } + + /// + /// Remove a given number of tokens from the bucket + /// + /// 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) + { + if (maxBurst == 0) + { + dripSucceeded = true; + return true; + } + + dripSucceeded = Drip(); + + if (content - amount >= 0) + { + if (parent != null && !parent.RemoveTokens(amount)) + return false; + + content -= amount; + return true; + } + else + { + return false; + } + } + + /// + /// Add tokens to the bucket over time. The number of tokens added each + /// call depends on the length of time that has passed since the last + /// call to Drip + /// + /// True if tokens were added to the bucket, otherwise false + private bool Drip() + { + if (tokensPerMS == 0) + { + content = maxBurst; + return true; + } + else + { + int now = Environment.TickCount & Int32.MaxValue; + int deltaMS = now - lastDrip; + + if (deltaMS <= 0) + { + if (deltaMS < 0) + lastDrip = now; + return false; + } + + int dripAmount = deltaMS * tokensPerMS; + + content = Math.Min(content + dripAmount, maxBurst); + lastDrip = now; + + return true; + } + } + } +} diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index f3242c130d..87c7df46ab 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -147,6 +147,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP int now = Environment.TickCount; foreach (OutgoingPacket packet in packets.Values) { + // TickCount of zero means a packet is in the resend queue + // but hasn't actually been sent over the wire yet if (packet.TickCount == 0) continue; diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs index 098e010833..f4ca877941 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs @@ -871,12 +871,15 @@ namespace OpenSim.Region.Framework.Scenes { foreach (IScriptModule e in engines) { - string n = e.GetAssemblyName(item.ItemID); - if (n != "") + if (e != null) { - if (!ret.Contains(n)) - ret.Add(n); - break; + string n = e.GetAssemblyName(item.ItemID); + if (n != String.Empty) + { + if (!ret.Contains(n)) + ret.Add(n); + break; + } } } } @@ -898,12 +901,15 @@ namespace OpenSim.Region.Framework.Scenes { foreach (IScriptModule e in engines) { - string n = e.GetXMLState(item.ItemID); - if (n != "") + if (e != null) { - if (!ret.ContainsKey(item.ItemID)) - ret[item.ItemID] = n; - break; + string n = e.GetXMLState(item.ItemID); + if (n != String.Empty) + { + if (!ret.ContainsKey(item.ItemID)) + ret[item.ItemID] = n; + break; + } } } } From 2a886fd76c9df972fa3096d19b37047f7eda672f Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 21 Oct 2009 02:19:45 +0100 Subject: [PATCH 42/61] Really make module port selection work. Implement port setting in LLProxyLoginModule. --- .../RegionModulesControllerPlugin.cs | 33 ++++++++++++++++--- OpenSim/Client/Linden/LLProxyLoginModule.cs | 12 +++++-- OpenSim/Framework/MainServer.cs | 4 ++- bin/config-include/GridCommon.ini.example | 4 +++ 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs index 13cb34c5b0..ddc37edd85 100644 --- a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs +++ b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs @@ -93,7 +93,7 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController { // Get the config string string moduleString = - modulesConfig.GetString(node.Id, String.Empty); + modulesConfig.GetString("Setup_" + node.Id, String.Empty); // We have a selector if (moduleString != String.Empty) @@ -121,6 +121,31 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController } else if (node.Type.GetInterface(typeof(INonSharedRegionModule).ToString()) != null) { + // Get the config string + string moduleString = + modulesConfig.GetString("Setup_" + node.Id, String.Empty); + + // We have a selector + if (moduleString != String.Empty) + { + // Allow disabling modules even if they don't have + // support for it + if (moduleString == "disabled") + continue; + + // Split off port, if present + string[] moduleParts = moduleString.Split(new char[] {'/'}, 2); + // Format is [port/][class] + string className = moduleParts[0]; + if (moduleParts.Length > 1) + className = moduleParts[1]; + + // Match the class name if given + if (className != String.Empty && + node.Type.ToString() != className) + continue; + } + m_log.DebugFormat("[REGIONMODULES]: Found non-shared region module {0}, class {1}", node.Id, node.Type); m_nonSharedModules.Add(node); } @@ -137,11 +162,11 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController // foreach (TypeExtensionNode node in m_sharedModules) { - Object[] ctorArgs = new Object[] {0}; + Object[] ctorArgs = new Object[] {(uint)0}; // Read the config again string moduleString = - modulesConfig.GetString(node.Id, String.Empty); + modulesConfig.GetString("Setup_" + node.Id, String.Empty); // Get the port number, if there is one if (moduleString != String.Empty) @@ -302,7 +327,7 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController // Read the config string moduleString = - modulesConfig.GetString(node.Id, String.Empty); + modulesConfig.GetString("Setup_" + node.Id, String.Empty); // Get the port number, if there is one if (moduleString != String.Empty) diff --git a/OpenSim/Client/Linden/LLProxyLoginModule.cs b/OpenSim/Client/Linden/LLProxyLoginModule.cs index 2da774f755..ad67c440bb 100644 --- a/OpenSim/Client/Linden/LLProxyLoginModule.cs +++ b/OpenSim/Client/Linden/LLProxyLoginModule.cs @@ -50,8 +50,16 @@ namespace OpenSim.Client.Linden /// public class LLProxyLoginModule : ISharedRegionModule { + private uint m_port = 0; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + public LLProxyLoginModule(uint port) + { + m_log.DebugFormat("[CLIENT]: LLProxyLoginModule port {0}", port); + m_port = port; + } + protected bool RegionLoginsEnabled { get @@ -148,8 +156,8 @@ namespace OpenSim.Client.Linden protected void AddHttpHandlers() { //we will add our handlers to the first scene we received, as all scenes share a http server. But will this ever change? - MainServer.Instance.AddXmlRPCHandler("expect_user", ExpectUser, false); - MainServer.Instance.AddXmlRPCHandler("logoff_user", LogOffUser, false); + MainServer.GetHttpServer(m_port).AddXmlRPCHandler("expect_user", ExpectUser, false); + MainServer.GetHttpServer(m_port).AddXmlRPCHandler("logoff_user", LogOffUser, false); } protected void AddScene(Scene scene) diff --git a/OpenSim/Framework/MainServer.cs b/OpenSim/Framework/MainServer.cs index 21033b3809..7da4893b62 100644 --- a/OpenSim/Framework/MainServer.cs +++ b/OpenSim/Framework/MainServer.cs @@ -42,8 +42,10 @@ namespace OpenSim.Framework set { instance = value; } } - public IHttpServer GetHttpServer(uint port) + public static IHttpServer GetHttpServer(uint port) { + if (port == 0) + return Instance; if (port == Instance.Port) return Instance; diff --git a/bin/config-include/GridCommon.ini.example b/bin/config-include/GridCommon.ini.example index 3bc9b6ccdb..6da0f1e8a1 100644 --- a/bin/config-include/GridCommon.ini.example +++ b/bin/config-include/GridCommon.ini.example @@ -32,3 +32,7 @@ ;Include-CenomeCache = "config-include/CenomeCache.ini" ;AssetCaching = "GlynnTuckerAssetCache" + + ;; Optionally, the port for the LLProxyLoginModule module can be changed + + ;Setup_LLProxyLoginModule = "9090/" From 93b24b5207356cfbc37d766a01f5f2e3f3a34bae Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 21 Oct 2009 03:44:40 +0100 Subject: [PATCH 43/61] Fix web map retrieval for regions configured via .ini --- OpenSim/Framework/RegionInfo.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs index a7315f51fb..c56654414a 100644 --- a/OpenSim/Framework/RegionInfo.cs +++ b/OpenSim/Framework/RegionInfo.cs @@ -978,11 +978,12 @@ namespace OpenSim.Framework public void SaveLastMapUUID(UUID mapUUID) { - if (null == configMember) return; - lastMapUUID = mapUUID; lastMapRefresh = Util.UnixTimeSinceEpoch().ToString(); + if (configMember == null) + return; + configMember.forceSetConfigurationOption("lastmap_uuid", mapUUID.ToString()); configMember.forceSetConfigurationOption("lastmap_refresh", lastMapRefresh); } From cde47c2b3d7089be556252246eb03365c1f39b54 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 00:18:35 -0700 Subject: [PATCH 44/61] Committing Jim's optimization to replace the 20ms sleep in outgoing packet handling with an interruptible wait handle --- .../ClientStack/LindenUDP/LLClientView.cs | 6 ++ .../ClientStack/LindenUDP/LLUDPClient.cs | 51 ++++++---- .../ClientStack/LindenUDP/LLUDPServer.cs | 96 +++++++++++++------ .../ClientStack/LindenUDP/TokenBucket.cs | 16 ++-- 4 files changed, 120 insertions(+), 49 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index dce9469ebb..38bbce04b0 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3320,6 +3320,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // If we received an update about our own avatar, process the avatar update priority queue immediately if (data.AgentID == m_agentId) ProcessAvatarTerseUpdates(); + else + m_udpServer.SignalOutgoingPacketHandler(); } private void ProcessAvatarTerseUpdates() @@ -3407,6 +3409,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP lock (m_primFullUpdates.SyncRoot) m_primFullUpdates.Enqueue(data.priority, objectData, data.localID); + + m_udpServer.SignalOutgoingPacketHandler(); } void ProcessPrimFullUpdates() @@ -3450,6 +3454,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP lock (m_primTerseUpdates.SyncRoot) m_primTerseUpdates.Enqueue(data.Priority, objectData, data.LocalID); + + m_udpServer.SignalOutgoingPacketHandler(); } void ProcessPrimTerseUpdates() diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 134cfe500b..b9d2c15460 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -105,9 +105,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int TickLastPacketReceived; /// Environment.TickCount of the last time the outgoing packet handler executed for this client public int TickLastOutgoingPacketHandler; + /// Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler executed for this client + public int ElapsedMSOutgoingPacketHandler; + /// Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed for this client + public int Elapsed100MSOutgoingPacketHandler; + /// Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed for this client + public int Elapsed500MSOutgoingPacketHandler; - /// Timer granularity. This is set to the measured resolution of Environment.TickCount - public readonly float G; /// Smoothed round-trip time. A smoothed average of the round-trip time for sending a /// reliable packet to the client and receiving an ACK public float SRTT; @@ -182,15 +186,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); } - // Set the granularity variable used for retransmission calculations to - // the measured resolution of Environment.TickCount - G = server.TickCountResolution; - // Default the retransmission timeout to three seconds RTO = 3000; // Initialize this to a sane value to prevent early disconnects - TickLastPacketReceived = Environment.TickCount; + TickLastPacketReceived = Environment.TickCount & Int32.MaxValue; + ElapsedMSOutgoingPacketHandler = 0; + Elapsed100MSOutgoingPacketHandler = 0; + Elapsed500MSOutgoingPacketHandler = 0; } /// @@ -391,6 +394,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // Not enough tokens in the bucket, queue this packet queue.Enqueue(packet); + m_udpServer.SignalOutgoingPacketHandler(); return true; } } @@ -407,13 +411,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// This function is only called from a synchronous loop in the /// UDPServer so we don't need to bother making this thread safe - /// True if any packets were sent, otherwise false - public bool DequeueOutgoing() + /// The minimum amount of time before the next packet + /// can be sent to this client + public int DequeueOutgoing() { OutgoingPacket packet; OpenSim.Framework.LocklessQueue queue; TokenBucket bucket; - bool packetSent = false; + int dataLength; + int minTimeout = Int32.MaxValue; //string queueDebugOutput = String.Empty; // Serious debug business @@ -428,12 +434,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP // leaving a dequeued packet still waiting to be sent out. Try to // send it again OutgoingPacket nextPacket = m_nextPackets[i]; - if (bucket.RemoveTokens(nextPacket.Buffer.DataLength)) + dataLength = nextPacket.Buffer.DataLength; + if (bucket.RemoveTokens(dataLength)) { // Send the packet m_udpServer.SendPacketFinal(nextPacket); m_nextPackets[i] = null; - packetSent = true; + minTimeout = 0; + } + else if (minTimeout != 0) + { + // Check the minimum amount of time we would have to wait before this packet can be sent out + minTimeout = Math.Min(minTimeout, ((dataLength - bucket.Content) / bucket.DripPerMS) + 1); } } else @@ -445,16 +457,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out - if (bucket.RemoveTokens(packet.Buffer.DataLength)) + dataLength = packet.Buffer.DataLength; + if (bucket.RemoveTokens(dataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); - packetSent = true; + minTimeout = 0; } else { // Save the dequeued packet for the next iteration m_nextPackets[i] = packet; + + if (minTimeout != 0) + { + // Check the minimum amount of time we would have to wait before this packet can be sent out + minTimeout = Math.Min(minTimeout, ((dataLength - bucket.Content) / bucket.DripPerMS) + 1); + } } // If the queue is empty after this dequeue, fire the queue @@ -473,7 +492,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business - return packetSent; + return minTimeout; } /// @@ -504,7 +523,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } // Always round retransmission timeout up to two seconds - RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR))); + RTO = Math.Max(2000, (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR))); //m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + // RTTVAR + " based on new RTT of " + r + "ms"); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 952d147b65..7d5c11e421 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -96,6 +96,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + /// The measured resolution of Environment.TickCount + public readonly float TickCountResolution; + /// Handlers for incoming packets //PacketEventDictionary packetEvents = new PacketEventDictionary(); /// Incoming packets that are awaiting handling @@ -112,20 +115,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP private Scene m_scene; /// The X/Y coordinates of the scene this UDP server is attached to private Location m_location; - /// The measured resolution of Environment.TickCount - private float m_tickCountResolution; /// The size of the receive buffer for the UDP socket. This value /// is passed up to the operating system and used in the system networking /// stack. Use zero to leave this value as the default private int m_recvBufferSize; /// Flag to process packets asynchronously or synchronously private bool m_asyncPacketHandling; - /// Track whether or not a packet was sent in the + /// Track the minimum amount of time needed to send the next packet in the /// OutgoingPacketHandler loop so we know when to sleep - private bool m_packetSentLastLoop; + private int m_minTimeout = Int32.MaxValue; + /// EventWaitHandle to signify the outgoing packet handler thread that + /// there is more work to do + private EventWaitHandle m_outgoingWaitHandle; - /// The measured resolution of Environment.TickCount - public float TickCountResolution { get { return m_tickCountResolution; } } public Socket Server { get { return null; } } public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) @@ -134,16 +136,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Environment.TickCount Measurement // Measure the resolution of Environment.TickCount - m_tickCountResolution = 0f; + TickCountResolution = 0f; for (int i = 0; i < 5; i++) { int start = Environment.TickCount; int now = start; while (now == start) now = Environment.TickCount; - m_tickCountResolution += (float)(now - start) * 0.2f; + TickCountResolution += (float)(now - start) * 0.2f; } m_log.Info("[LLUDPSERVER]: Average Environment.TickCount resolution: " + TickCountResolution + "ms"); + TickCountResolution = (float)Math.Ceiling(TickCountResolution); #endregion Environment.TickCount Measurement @@ -171,6 +174,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP base.Start(m_recvBufferSize, m_asyncPacketHandling); + m_outgoingWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); + // Start the incoming packet processing thread Thread incomingThread = new Thread(IncomingPacketHandler); incomingThread.Name = "Incoming Packets (" + m_scene.RegionInfo.RegionName + ")"; @@ -185,6 +190,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP { m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName); base.Stop(); + + m_outgoingWaitHandle.Close(); } public void AddScene(IScene scene) @@ -768,6 +775,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP packetInbox.Clear(); } + public bool SignalOutgoingPacketHandler() + { + return m_outgoingWaitHandle.Set(); + } + private void OutgoingPacketHandler() { // Set this culture for the thread that outgoing packets are sent @@ -778,14 +790,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP { try { - m_packetSentLastLoop = false; + m_minTimeout = Int32.MaxValue; + // Handle outgoing packets, resends, acknowledgements, and pings for each + // client. m_minTimeout will be set to 0 if more packets are waiting in the + // queues with bandwidth to spare, or the number of milliseconds we need to + // wait before at least one packet can be sent to a client m_scene.ClientManager.ForEachSync(ClientOutgoingPacketHandler); - // If no packets at all were sent, sleep to avoid chewing up CPU cycles - // when there is nothing to do - if (!m_packetSentLastLoop) - Thread.Sleep(20); + // Can't wait for a negative amount of time, and put a 100ms ceiling on our + // maximum wait time + m_minTimeout = Utils.Clamp(m_minTimeout, 0, 100); + + if (m_minTimeout > 0) + { + // Don't bother waiting for a shorter interval than our TickCountResolution + // since the token buckets wouldn't update anyways + m_minTimeout = Math.Max(m_minTimeout, (int)TickCountResolution); + + // Wait for someone to signal that packets are ready to be sent, or for our + // sleep interval to expire + m_outgoingWaitHandle.WaitOne(m_minTimeout); + } } catch (Exception ex) { @@ -802,32 +828,48 @@ namespace OpenSim.Region.ClientStack.LindenUDP { LLUDPClient udpClient = ((LLClientView)client).UDPClient; + // Update ElapsedMSOutgoingPacketHandler int thisTick = Environment.TickCount & Int32.MaxValue; - int elapsedMS = thisTick - udpClient.TickLastOutgoingPacketHandler; + if (udpClient.TickLastOutgoingPacketHandler > thisTick) + udpClient.ElapsedMSOutgoingPacketHandler += ((Int32.MaxValue - udpClient.TickLastOutgoingPacketHandler) + thisTick); + else + udpClient.ElapsedMSOutgoingPacketHandler += (thisTick - udpClient.TickLastOutgoingPacketHandler); if (udpClient.IsConnected) { // Check for pending outgoing resends every 100ms - if (elapsedMS >= 100) + if (udpClient.ElapsedMSOutgoingPacketHandler >= 100) { ResendUnacked(udpClient); + udpClient.ElapsedMSOutgoingPacketHandler -= 100; + udpClient.Elapsed100MSOutgoingPacketHandler += 1; + } - // Check for pending outgoing ACKs every 500ms - if (elapsedMS >= 500) - { - SendAcks(udpClient); + // Check for pending outgoing ACKs every 500ms + if (udpClient.Elapsed100MSOutgoingPacketHandler >= 5) + { + SendAcks(udpClient); + udpClient.Elapsed100MSOutgoingPacketHandler -= 5; + udpClient.Elapsed500MSOutgoingPacketHandler += 1; + } - // Send pings to clients every 5000ms - if (elapsedMS >= 5000) - { - SendPing(udpClient); - } - } + // Send pings to clients every 5000ms + if (udpClient.Elapsed500MSOutgoingPacketHandler >= 10) + { + SendPing(udpClient); + udpClient.Elapsed500MSOutgoingPacketHandler -= 10; } // Dequeue any outgoing packets that are within the throttle limits - if (udpClient.DequeueOutgoing()) - m_packetSentLastLoop = true; + // and get the minimum time we would have to sleep before this client + // could send a packet out + int minTimeoutThisLoop = udpClient.DequeueOutgoing(); + + // Although this is not thread safe, it is cheaper than locking and the + // worst that will happen is we sleep for slightly longer than the + // minimum necessary interval + if (minTimeoutThisLoop < m_minTimeout) + m_minTimeout = minTimeoutThisLoop; } udpClient.TickLastOutgoingPacketHandler = thisTick; diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 0a64095779..bdbd2848c6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -97,6 +97,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } + /// + /// The speed limit of this bucket in bytes per millisecond + /// + public int DripPerMS + { + get { return tokensPerMS; } + } + /// /// The number of bytes that can be sent at this moment. This is the /// current number of tokens in the bucket @@ -106,11 +114,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public int Content { - get - { - Drip(); - return content; - } + get { return content; } } #endregion Properties @@ -182,7 +186,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// call to Drip /// /// True if tokens were added to the bucket, otherwise false - private bool Drip() + public bool Drip() { if (tokensPerMS == 0) { From c0c845aea462ea7b15580c72f5513e5e1ef02030 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 01:07:40 -0700 Subject: [PATCH 45/61] Fixed the way OnQueueEmpty is called to prevent simultaneous calls for the same category --- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index b9d2c15460..ca9925c4ae 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -541,7 +541,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (throttleIndex > 0) { if (!m_onQueueEmptyRunning[throttleIndex]) + { + m_onQueueEmptyRunning[throttleIndex] = true; Util.FireAndForget(FireQueueEmpty, throttleIndex); + } } } @@ -559,14 +562,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (callback != null) { - if (!m_onQueueEmptyRunning[i]) - { - m_onQueueEmptyRunning[i] = true; - try { callback(type); } - catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } - m_onQueueEmptyRunning[i] = false; - } + try { callback(type); } + catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } } + + m_onQueueEmptyRunning[i] = false; } } } From 8dd15fd5a590e059038d6438a305264cad3918b7 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 21 Oct 2009 18:45:37 +0100 Subject: [PATCH 46/61] Patch by mcortez: Remove lock from scene presence updating in groups module --- .../Avatar/XmlRpcGroups/GroupsModule.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs index b2091995e9..b2544fac73 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs @@ -1244,18 +1244,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Updating scene title for {0} with title: {1}", AgentID, Title); ScenePresence presence = null; - lock (m_sceneList) - { - foreach (Scene scene in m_sceneList) - { - presence = scene.GetScenePresence(AgentID); - if (presence != null) - { - presence.Grouptitle = Title; - // FixMe: Ter suggests a "Schedule" method that I can't find. - presence.SendFullUpdateToAllClients(); - } + foreach (Scene scene in m_sceneList) + { + presence = scene.GetScenePresence(AgentID); + if (presence != null) + { + presence.Grouptitle = Title; + + // FixMe: Ter suggests a "Schedule" method that I can't find. + presence.SendFullUpdateToAllClients(); } } } From 9178537e9414478f0a9bd84bb5e106b2f15640c3 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 11:59:48 -0700 Subject: [PATCH 47/61] * Replaced the UnackedPacketCollection with a lockless implementation. The tiny amount of time spent in the locks turned into a lot of time when the rest of the LLUDP implementation went lockless * Changed the timer tracking numbers for each client to not have "memory". It will no longer queue up calls to functions like ResendUnacked * Reverted Jim's WaitHandle code. Although it was technically more correct, it exhibited the exact same behavior as the old code but spent more cycles. The 20ms has been replaced with the minimum amount of time before a token bucket could receive a drip, and an else { sleep(0); } was added to make sure the outgoing packet handler always yields at least a minimum amount --- .../ClientStack/LindenUDP/LLClientView.cs | 11 +- .../ClientStack/LindenUDP/LLUDPClient.cs | 33 +--- .../ClientStack/LindenUDP/LLUDPServer.cs | 143 ++++++----------- .../LindenUDP/UnackedPacketCollection.cs | 151 +++++++++--------- 4 files changed, 128 insertions(+), 210 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 38bbce04b0..997f38cea6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -1228,10 +1228,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck); pc.Header.Reliable = false; - OutgoingPacket oldestPacket = m_udpClient.NeedAcks.GetOldest(); - pc.PingID.PingID = seq; - pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0; + // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit + pc.PingID.OldestUnacked = 0; OutPacket(pc, ThrottleOutPacketType.Unknown); } @@ -3320,8 +3319,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // If we received an update about our own avatar, process the avatar update priority queue immediately if (data.AgentID == m_agentId) ProcessAvatarTerseUpdates(); - else - m_udpServer.SignalOutgoingPacketHandler(); } private void ProcessAvatarTerseUpdates() @@ -3409,8 +3406,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP lock (m_primFullUpdates.SyncRoot) m_primFullUpdates.Enqueue(data.priority, objectData, data.localID); - - m_udpServer.SignalOutgoingPacketHandler(); } void ProcessPrimFullUpdates() @@ -3454,8 +3449,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP lock (m_primTerseUpdates.SyncRoot) m_primTerseUpdates.Enqueue(data.Priority, objectData, data.LocalID); - - m_udpServer.SignalOutgoingPacketHandler(); } void ProcessPrimTerseUpdates() diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index ca9925c4ae..458e78daa0 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -202,7 +202,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void Shutdown() { IsConnected = false; - NeedAcks.Clear(); for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) { m_packetOutboxes[i].Clear(); @@ -394,7 +393,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // Not enough tokens in the bucket, queue this packet queue.Enqueue(packet); - m_udpServer.SignalOutgoingPacketHandler(); return true; } } @@ -411,15 +409,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// This function is only called from a synchronous loop in the /// UDPServer so we don't need to bother making this thread safe - /// The minimum amount of time before the next packet - /// can be sent to this client - public int DequeueOutgoing() + /// True if any packets were sent, otherwise false + public bool DequeueOutgoing() { OutgoingPacket packet; OpenSim.Framework.LocklessQueue queue; TokenBucket bucket; - int dataLength; - int minTimeout = Int32.MaxValue; + bool packetSent = false; //string queueDebugOutput = String.Empty; // Serious debug business @@ -434,18 +430,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP // leaving a dequeued packet still waiting to be sent out. Try to // send it again OutgoingPacket nextPacket = m_nextPackets[i]; - dataLength = nextPacket.Buffer.DataLength; - if (bucket.RemoveTokens(dataLength)) + if (bucket.RemoveTokens(nextPacket.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(nextPacket); m_nextPackets[i] = null; - minTimeout = 0; - } - else if (minTimeout != 0) - { - // Check the minimum amount of time we would have to wait before this packet can be sent out - minTimeout = Math.Min(minTimeout, ((dataLength - bucket.Content) / bucket.DripPerMS) + 1); + packetSent = true; } } else @@ -457,23 +447,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out - dataLength = packet.Buffer.DataLength; - if (bucket.RemoveTokens(dataLength)) + if (bucket.RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); - minTimeout = 0; + packetSent = true; } else { // Save the dequeued packet for the next iteration m_nextPackets[i] = packet; - - if (minTimeout != 0) - { - // Check the minimum amount of time we would have to wait before this packet can be sent out - minTimeout = Math.Min(minTimeout, ((dataLength - bucket.Content) / bucket.DripPerMS) + 1); - } } // If the queue is empty after this dequeue, fire the queue @@ -492,7 +475,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business - return minTimeout; + return packetSent; } /// diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 7d5c11e421..a8ce102305 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -121,12 +121,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_recvBufferSize; /// Flag to process packets asynchronously or synchronously private bool m_asyncPacketHandling; - /// Track the minimum amount of time needed to send the next packet in the - /// OutgoingPacketHandler loop so we know when to sleep - private int m_minTimeout = Int32.MaxValue; - /// EventWaitHandle to signify the outgoing packet handler thread that - /// there is more work to do - private EventWaitHandle m_outgoingWaitHandle; + /// Tracks whether or not a packet was sent each round so we know + /// whether or not to sleep + private bool m_packetSent; public Socket Server { get { return null; } } @@ -174,8 +171,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP base.Start(m_recvBufferSize, m_asyncPacketHandling); - m_outgoingWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); - // Start the incoming packet processing thread Thread incomingThread = new Thread(IncomingPacketHandler); incomingThread.Name = "Incoming Packets (" + m_scene.RegionInfo.RegionName + ")"; @@ -190,8 +185,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP { m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName); base.Stop(); - - m_outgoingWaitHandle.Close(); } public void AddScene(IScene scene) @@ -374,10 +367,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck); pc.Header.Reliable = false; - OutgoingPacket oldestPacket = udpClient.NeedAcks.GetOldest(); - pc.PingID.PingID = (byte)udpClient.CurrentPingSequence++; - pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0; + // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit + pc.PingID.OldestUnacked = 0; SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false); } @@ -397,39 +389,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP return; } - if (udpClient.NeedAcks.Count > 0) + // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO + List expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO); + + if (expiredPackets != null) { - // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO - List expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO); + m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO); - if (expiredPackets != null) + // Resend packets + for (int i = 0; i < expiredPackets.Count; i++) { - m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID); + OutgoingPacket outgoingPacket = expiredPackets[i]; - // Resend packets - for (int i = 0; i < expiredPackets.Count; i++) - { - OutgoingPacket outgoingPacket = expiredPackets[i]; + //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed", + // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount); - //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed", - // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount); + // Set the resent flag + outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); + outgoingPacket.Category = ThrottleOutPacketType.Resend; - // Set the resent flag - outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); - outgoingPacket.Category = ThrottleOutPacketType.Resend; + // The TickCount will be set to the current time when the packet + // is actually sent out again + outgoingPacket.TickCount = 0; - // The TickCount will be set to the current time when the packet - // is actually sent out again - outgoingPacket.TickCount = 0; + // Bump up the resend count on this packet + Interlocked.Increment(ref outgoingPacket.ResendCount); + //Interlocked.Increment(ref Stats.ResentPackets); - // Bump up the resend count on this packet - Interlocked.Increment(ref outgoingPacket.ResendCount); - //Interlocked.Increment(ref Stats.ResentPackets); - - // Requeue or resend the packet - if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) - SendPacketFinal(outgoingPacket); - } + // Requeue or resend the packet + if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) + SendPacketFinal(outgoingPacket); } } } @@ -577,11 +566,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Handle appended ACKs if (packet.Header.AppendedAcks && packet.Header.AckList != null) { - lock (udpClient.NeedAcks.SyncRoot) - { - for (int i = 0; i < packet.Header.AckList.Length; i++) - AcknowledgePacket(udpClient, packet.Header.AckList[i], now, packet.Header.Resent); - } + for (int i = 0; i < packet.Header.AckList.Length; i++) + udpClient.NeedAcks.Remove(packet.Header.AckList[i], now, packet.Header.Resent); } // Handle PacketAck packets @@ -589,11 +575,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP { PacketAckPacket ackPacket = (PacketAckPacket)packet; - lock (udpClient.NeedAcks.SyncRoot) - { - for (int i = 0; i < ackPacket.Packets.Length; i++) - AcknowledgePacket(udpClient, ackPacket.Packets[i].ID, now, packet.Header.Resent); - } + for (int i = 0; i < ackPacket.Packets.Length; i++) + udpClient.NeedAcks.Remove(ackPacket.Packets[i].ID, now, packet.Header.Resent); // We don't need to do anything else with PacketAck packets return; @@ -734,21 +717,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP client.Close(); } - private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) - { - OutgoingPacket ackedPacket; - if (client.NeedAcks.RemoveUnsafe(ack, out ackedPacket) && !fromResend) - { - // Update stats - Interlocked.Add(ref client.UnackedBytes, -ackedPacket.Buffer.DataLength); - - // Calculate the round-trip time for this packet and its ACK - int rtt = currentTime - ackedPacket.TickCount; - if (rtt > 0) - client.UpdateRoundTrip(rtt); - } - } - private void IncomingPacketHandler() { // Set this culture for the thread that incoming packets are received @@ -775,11 +743,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP packetInbox.Clear(); } - public bool SignalOutgoingPacketHandler() - { - return m_outgoingWaitHandle.Set(); - } - private void OutgoingPacketHandler() { // Set this culture for the thread that outgoing packets are sent @@ -790,28 +753,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP { try { - m_minTimeout = Int32.MaxValue; + m_packetSent = false; // Handle outgoing packets, resends, acknowledgements, and pings for each - // client. m_minTimeout will be set to 0 if more packets are waiting in the - // queues with bandwidth to spare, or the number of milliseconds we need to - // wait before at least one packet can be sent to a client + // client. m_packetSent will be set to true if a packet is sent m_scene.ClientManager.ForEachSync(ClientOutgoingPacketHandler); - // Can't wait for a negative amount of time, and put a 100ms ceiling on our - // maximum wait time - m_minTimeout = Utils.Clamp(m_minTimeout, 0, 100); - - if (m_minTimeout > 0) - { - // Don't bother waiting for a shorter interval than our TickCountResolution - // since the token buckets wouldn't update anyways - m_minTimeout = Math.Max(m_minTimeout, (int)TickCountResolution); - - // Wait for someone to signal that packets are ready to be sent, or for our - // sleep interval to expire - m_outgoingWaitHandle.WaitOne(m_minTimeout); - } + // If a packet was sent, only do a minimum length context switch to allow + // other parts of the code to do work. If nothing was sent, sleep for the + // minimum amount of time before a token bucket could get more tokens + if (m_packetSent) + Thread.Sleep(0); + else + Thread.Sleep((int)TickCountResolution); } catch (Exception ex) { @@ -841,7 +795,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (udpClient.ElapsedMSOutgoingPacketHandler >= 100) { ResendUnacked(udpClient); - udpClient.ElapsedMSOutgoingPacketHandler -= 100; + udpClient.ElapsedMSOutgoingPacketHandler = 0; udpClient.Elapsed100MSOutgoingPacketHandler += 1; } @@ -849,7 +803,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (udpClient.Elapsed100MSOutgoingPacketHandler >= 5) { SendAcks(udpClient); - udpClient.Elapsed100MSOutgoingPacketHandler -= 5; + udpClient.Elapsed100MSOutgoingPacketHandler = 0; udpClient.Elapsed500MSOutgoingPacketHandler += 1; } @@ -857,19 +811,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (udpClient.Elapsed500MSOutgoingPacketHandler >= 10) { SendPing(udpClient); - udpClient.Elapsed500MSOutgoingPacketHandler -= 10; + udpClient.Elapsed500MSOutgoingPacketHandler = 0; } // Dequeue any outgoing packets that are within the throttle limits - // and get the minimum time we would have to sleep before this client - // could send a packet out - int minTimeoutThisLoop = udpClient.DequeueOutgoing(); - - // Although this is not thread safe, it is cheaper than locking and the - // worst that will happen is we sleep for slightly longer than the - // minimum necessary interval - if (minTimeoutThisLoop < m_minTimeout) - m_minTimeout = minTimeoutThisLoop; + if (udpClient.DequeueOutgoing()) + m_packetSent = true; } udpClient.TickLastOutgoingPacketHandler = thisTick; diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index 87c7df46ab..12f0c0a8b5 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -37,22 +37,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public sealed class UnackedPacketCollection { - /// Synchronization primitive. A lock must be acquired on this - /// object before calling any of the unsafe methods - public object SyncRoot = new object(); + /// + /// Holds information about a pending acknowledgement + /// + private struct PendingAck + { + /// Sequence number of the packet to remove + public uint SequenceNumber; + /// Environment.TickCount value when the remove was queued. + /// This is used to update round-trip times for packets + public int RemoveTime; + /// Whether or not this acknowledgement was attached to a + /// resent packet. If so, round-trip time will not be calculated + public bool FromResend; + + public PendingAck(uint sequenceNumber, int currentTime, bool fromResend) + { + SequenceNumber = sequenceNumber; + RemoveTime = currentTime; + FromResend = fromResend; + } + } /// Holds the actual unacked packet data, sorted by sequence number - private SortedDictionary packets = new SortedDictionary(); - - /// Gets the total number of unacked packets - public int Count { get { return packets.Count; } } - - /// - /// Default constructor - /// - public UnackedPacketCollection() - { - } + private SortedDictionary m_packets = new SortedDictionary(); + /// Holds packets that need to be added to the unacknowledged list + private LocklessQueue m_pendingAdds = new LocklessQueue(); + /// Holds information about pending acknowledgements + private LocklessQueue m_pendingRemoves = new LocklessQueue(); /// /// Add an unacked packet to the collection @@ -60,74 +72,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Packet that is awaiting acknowledgement /// True if the packet was successfully added, false if the /// packet already existed in the collection - public bool Add(OutgoingPacket packet) + /// This does not immediately add the ACK to the collection, + /// it only queues it so it can be added in a thread-safe way later + public void Add(OutgoingPacket packet) { - lock (SyncRoot) - { - if (!packets.ContainsKey(packet.SequenceNumber)) - { - packets.Add(packet.SequenceNumber, packet); - return true; - } - return false; - } + m_pendingAdds.Enqueue(packet); } /// - /// Removes a packet from the collection without attempting to obtain a - /// lock first + /// Marks a packet as acknowledged /// - /// Sequence number of the packet to remove - /// True if the packet was found and removed, otherwise false - public bool RemoveUnsafe(uint sequenceNumber) + /// Sequence number of the packet to + /// acknowledge + /// Current value of Environment.TickCount + public void Remove(uint sequenceNumber, int currentTime, bool fromResend) { - return packets.Remove(sequenceNumber); - } - - /// - /// Removes a packet from the collection without attempting to obtain a - /// lock first - /// - /// Sequence number of the packet to remove - /// Returns the removed packet - /// True if the packet was found and removed, otherwise false - public bool RemoveUnsafe(uint sequenceNumber, out OutgoingPacket packet) - { - if (packets.TryGetValue(sequenceNumber, out packet)) - { - packets.Remove(sequenceNumber); - return true; - } - - return false; - } - - /// - /// Removes all elements from the collection - /// - public void Clear() - { - lock (SyncRoot) - packets.Clear(); - } - - /// - /// Gets the packet with the lowest sequence number - /// - /// The packet with the lowest sequence number, or null if the - /// collection is empty - public OutgoingPacket GetOldest() - { - lock (SyncRoot) - { - using (SortedDictionary.ValueCollection.Enumerator e = packets.Values.GetEnumerator()) - { - if (e.MoveNext()) - return e.Current; - else - return null; - } - } + m_pendingRemoves.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend)); } /// @@ -138,14 +98,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// packet is considered expired /// A list of all expired packets according to the given /// expiration timeout + /// This function is not thread safe, and cannot be called + /// multiple times concurrently public List GetExpiredPackets(int timeoutMS) { + ProcessQueues(); + List expiredPackets = null; - lock (SyncRoot) + if (m_packets.Count > 0) { int now = Environment.TickCount; - foreach (OutgoingPacket packet in packets.Values) + + foreach (OutgoingPacket packet in m_packets.Values) { // TickCount of zero means a packet is in the resend queue // but hasn't actually been sent over the wire yet @@ -167,5 +132,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP return expiredPackets; } + + private void ProcessQueues() + { + // Process all the pending adds + OutgoingPacket pendingAdd; + while (m_pendingAdds.Dequeue(out pendingAdd)) + m_packets[pendingAdd.SequenceNumber] = pendingAdd; + + // Process all the pending removes, including updating statistics and round-trip times + PendingAck pendingRemove; + OutgoingPacket ackedPacket; + while (m_pendingRemoves.Dequeue(out pendingRemove)) + { + if (m_packets.TryGetValue(pendingRemove.SequenceNumber, out ackedPacket)) + { + m_packets.Remove(pendingRemove.SequenceNumber); + + // Update stats + System.Threading.Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); + + if (!pendingRemove.FromResend) + { + // Calculate the round-trip time for this packet and its ACK + int rtt = pendingRemove.RemoveTime - ackedPacket.TickCount; + if (rtt > 0) + ackedPacket.Client.UpdateRoundTrip(rtt); + } + } + } + } } } From d88bb83136752fadb1c2e3b64641acc2b3b6dd30 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 21 Oct 2009 20:47:24 +0100 Subject: [PATCH 48/61] Fix llParticleSystem to accept LSL variables and LSL constants in place of the named constants for the rule selector. Information provided by Snowcrash --- .../Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 224b3ccaaa..9a7089013b 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -5844,7 +5844,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api for (int i = 0; i < rules.Length; i += 2) { - switch ((int)rules.Data[i]) + switch (Convert.ToInt32(rules.Data[i])) { case (int)ScriptBaseClass.PSYS_PART_FLAGS: prules.PartDataFlags = (Primitive.ParticleSystem.ParticleDataFlags)(uint)rules.GetLSLIntegerItem(i + 1); From 7ee422a344ff22cf988aea2355628d2dee831983 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 13:47:16 -0700 Subject: [PATCH 49/61] * Handle UseCircuitCode packets asynchronously. Adding an agent to a scene can take several seconds, and was blocking up packet handling in the meantime * Clamp retransmission timeout values between three and 10 seconds * Log outgoing time for a packet right after it is sent instead of well before * Loop through the entire UnackedPacketCollection when looking for expired packets --- .../ClientStack/LindenUDP/LLUDPClient.cs | 7 ++- .../ClientStack/LindenUDP/LLUDPServer.cs | 47 +++++++++++++++---- .../LindenUDP/UnackedPacketCollection.cs | 8 ++-- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 458e78daa0..a43197d9bf 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -505,8 +505,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r; } - // Always round retransmission timeout up to two seconds - RTO = Math.Max(2000, (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR))); + RTO = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR)); + + // Clamp the retransmission timeout to manageable values + RTO = Utils.Clamp(RTO, 3000, 10000); + //m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + // RTTVAR + " based on new RTT of " + r + "ms"); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index a8ce102305..209c0e0712 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -381,7 +381,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Disconnect an agent if no packets are received for some time //FIXME: Make 60 an .ini setting - if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60) + if ((Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > 1000 * 60) { m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); @@ -439,9 +439,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (!udpClient.IsConnected) return; - // Keep track of when this packet was sent out (right now) - outgoingPacket.TickCount = Environment.TickCount; - #region ACK Appending int dataLength = buffer.DataLength; @@ -494,6 +491,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Put the UDP payload on the wire AsyncBeginSend(buffer); + + // Keep track of when this packet was sent out (right now) + outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; } protected override void PacketReceived(UDPPacketBuffer buffer) @@ -536,7 +536,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP // UseCircuitCode handling if (packet.Type == PacketType.UseCircuitCode) { - AddNewClient((UseCircuitCodePacket)packet, (IPEndPoint)buffer.RemoteEndPoint); + Util.FireAndForget( + delegate(object o) + { + IPEndPoint remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint; + + // Begin the process of adding the client to the simulator + AddNewClient((UseCircuitCodePacket)packet, remoteEndPoint); + + // Acknowledge the UseCircuitCode packet + SendAckImmediate(remoteEndPoint, packet.Header.Sequence); + } + ); + return; } // Determine which agent this packet came from @@ -558,11 +570,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Stats tracking Interlocked.Increment(ref udpClient.PacketsReceived); - #region ACK Receiving - - int now = Environment.TickCount; + int now = Environment.TickCount & Int32.MaxValue; udpClient.TickLastPacketReceived = now; + #region ACK Receiving + // Handle appended ACKs if (packet.Header.AppendedAcks && packet.Header.AckList != null) { @@ -650,6 +662,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP { } + private void SendAckImmediate(IPEndPoint remoteEndpoint, uint sequenceNumber) + { + PacketAckPacket ack = new PacketAckPacket(); + ack.Header.Reliable = false; + ack.Packets = new PacketAckPacket.PacketsBlock[1]; + ack.Packets[0] = new PacketAckPacket.PacketsBlock(); + ack.Packets[0].ID = sequenceNumber; + + byte[] packetData = ack.ToBytes(); + int length = packetData.Length; + + UDPPacketBuffer buffer = new UDPPacketBuffer(remoteEndpoint, length); + buffer.DataLength = length; + + Buffer.BlockCopy(packetData, 0, buffer.Data, 0, length); + + AsyncBeginSend(buffer); + } + private bool IsClientAuthorized(UseCircuitCodePacket useCircuitCode, out AuthenticateResponse sessionInfo) { UUID agentID = useCircuitCode.CircuitCode.ID; diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index 12f0c0a8b5..bd5fe1cab4 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -85,6 +85,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Sequence number of the packet to /// acknowledge /// Current value of Environment.TickCount + /// This does not immediately acknowledge the packet, it only + /// queues the ack so it can be handled in a thread-safe way later public void Remove(uint sequenceNumber, int currentTime, bool fromResend) { m_pendingRemoves.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend)); @@ -108,7 +110,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_packets.Count > 0) { - int now = Environment.TickCount; + int now = Environment.TickCount & Int32.MaxValue; foreach (OutgoingPacket packet in m_packets.Values) { @@ -123,10 +125,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP expiredPackets = new List(); expiredPackets.Add(packet); } - else + /*else { break; - } + }*/ } } From bb4da417adac6189c4d999857d43ca7eb858e3c4 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 14:03:49 -0700 Subject: [PATCH 50/61] Removing the Sleep(0) call from the OutgoingPacketHandler --- OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 209c0e0712..be6f7ef4e0 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -790,12 +790,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP // client. m_packetSent will be set to true if a packet is sent m_scene.ClientManager.ForEachSync(ClientOutgoingPacketHandler); - // If a packet was sent, only do a minimum length context switch to allow - // other parts of the code to do work. If nothing was sent, sleep for the - // minimum amount of time before a token bucket could get more tokens - if (m_packetSent) - Thread.Sleep(0); - else + // If nothing was sent, sleep for the minimum amount of time before a + // token bucket could get more tokens + if (!m_packetSent) Thread.Sleep((int)TickCountResolution); } catch (Exception ex) From 62f1bfd136df7a68e9f829acc1db22354772bad3 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 14:25:22 -0700 Subject: [PATCH 51/61] Testing out a hack to identify the source of the high cpu usage --- OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index a43197d9bf..de67ca745b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -552,6 +552,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } } + // HACK: Try spending some extra time here to slow down OnQueueEmpty calls + System.Threading.Thread.Sleep(100); + m_onQueueEmptyRunning[i] = false; } } From 2752a3525c6c57470024d0dddfd69d593abc4594 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 15:22:23 -0700 Subject: [PATCH 52/61] * Changed the timing calculations for sending resends/acks/pings from per-client back to per-scene * Testing a fix from Jim to make the cpu usage fix cleaner --- .../ClientStack/LindenUDP/LLClientView.cs | 6 +- .../ClientStack/LindenUDP/LLUDPClient.cs | 13 +-- .../ClientStack/LindenUDP/LLUDPServer.cs | 85 +++++++++++++------ 3 files changed, 63 insertions(+), 41 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 997f38cea6..91afa4d2e7 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3558,7 +3558,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP ProcessTextureRequests(); break; case ThrottleOutPacketType.Task: - if (Monitor.TryEnter(m_avatarTerseUpdates.SyncRoot, 1)) + if (Monitor.TryEnter(m_avatarTerseUpdates.SyncRoot)) { try { @@ -3573,7 +3573,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } break; case ThrottleOutPacketType.State: - if (Monitor.TryEnter(m_primFullUpdates.SyncRoot, 1)) + if (Monitor.TryEnter(m_primFullUpdates.SyncRoot)) { try { @@ -3586,7 +3586,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP finally { Monitor.Exit(m_primFullUpdates.SyncRoot); } } - if (Monitor.TryEnter(m_primTerseUpdates.SyncRoot, 1)) + if (Monitor.TryEnter(m_primTerseUpdates.SyncRoot)) { try { diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index de67ca745b..0a090b4452 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -103,14 +103,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public bool IsPaused; /// Environment.TickCount when the last packet was received for this client public int TickLastPacketReceived; - /// Environment.TickCount of the last time the outgoing packet handler executed for this client - public int TickLastOutgoingPacketHandler; - /// Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler executed for this client - public int ElapsedMSOutgoingPacketHandler; - /// Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed for this client - public int Elapsed100MSOutgoingPacketHandler; - /// Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed for this client - public int Elapsed500MSOutgoingPacketHandler; /// Smoothed round-trip time. A smoothed average of the round-trip time for sending a /// reliable packet to the client and receiving an ACK @@ -191,9 +183,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Initialize this to a sane value to prevent early disconnects TickLastPacketReceived = Environment.TickCount & Int32.MaxValue; - ElapsedMSOutgoingPacketHandler = 0; - Elapsed100MSOutgoingPacketHandler = 0; - Elapsed500MSOutgoingPacketHandler = 0; } /// @@ -553,7 +542,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } // HACK: Try spending some extra time here to slow down OnQueueEmpty calls - System.Threading.Thread.Sleep(100); + //System.Threading.Thread.Sleep(100); m_onQueueEmptyRunning[i] = false; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index be6f7ef4e0..4bdf132dae 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -125,6 +125,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// whether or not to sleep private bool m_packetSent; + /// Environment.TickCount of the last time the outgoing packet handler executed + private int m_tickLastOutgoingPacketHandler; + /// Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped + private int m_elapsedMSOutgoingPacketHandler; + /// Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed + private int m_elapsed100MSOutgoingPacketHandler; + /// Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed + private int m_elapsed500MSOutgoingPacketHandler; + + /// Flag to signal when clients should check for resends + private bool m_resendUnacked; + /// Flag to signal when clients should send ACKs + private bool m_sendAcks; + /// Flag to signal when clients should send pings + private bool m_sendPing; + public Socket Server { get { return null; } } public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) @@ -786,6 +802,46 @@ namespace OpenSim.Region.ClientStack.LindenUDP { m_packetSent = false; + #region Update Timers + + m_resendUnacked = false; + m_sendAcks = false; + 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; + + // Check for pending outgoing resends every 100ms + if (m_elapsedMSOutgoingPacketHandler >= 100) + { + m_resendUnacked = true; + m_elapsedMSOutgoingPacketHandler = 0; + m_elapsed100MSOutgoingPacketHandler += 1; + } + + // Check for pending outgoing ACKs every 500ms + if (m_elapsed100MSOutgoingPacketHandler >= 5) + { + m_sendAcks = true; + m_elapsed100MSOutgoingPacketHandler = 0; + m_elapsed500MSOutgoingPacketHandler += 1; + } + + // Send pings to clients every 5000ms + if (m_elapsed500MSOutgoingPacketHandler >= 10) + { + m_sendPing = true; + m_elapsed500MSOutgoingPacketHandler = 0; + } + + #endregion Update Timers + // Handle outgoing packets, resends, acknowledgements, and pings for each // client. m_packetSent will be set to true if a packet is sent m_scene.ClientManager.ForEachSync(ClientOutgoingPacketHandler); @@ -810,44 +866,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP { LLUDPClient udpClient = ((LLClientView)client).UDPClient; - // Update ElapsedMSOutgoingPacketHandler - int thisTick = Environment.TickCount & Int32.MaxValue; - if (udpClient.TickLastOutgoingPacketHandler > thisTick) - udpClient.ElapsedMSOutgoingPacketHandler += ((Int32.MaxValue - udpClient.TickLastOutgoingPacketHandler) + thisTick); - else - udpClient.ElapsedMSOutgoingPacketHandler += (thisTick - udpClient.TickLastOutgoingPacketHandler); - if (udpClient.IsConnected) { - // Check for pending outgoing resends every 100ms - if (udpClient.ElapsedMSOutgoingPacketHandler >= 100) - { + if (m_resendUnacked) ResendUnacked(udpClient); - udpClient.ElapsedMSOutgoingPacketHandler = 0; - udpClient.Elapsed100MSOutgoingPacketHandler += 1; - } - // Check for pending outgoing ACKs every 500ms - if (udpClient.Elapsed100MSOutgoingPacketHandler >= 5) - { + if (m_sendAcks) SendAcks(udpClient); - udpClient.Elapsed100MSOutgoingPacketHandler = 0; - udpClient.Elapsed500MSOutgoingPacketHandler += 1; - } - // Send pings to clients every 5000ms - if (udpClient.Elapsed500MSOutgoingPacketHandler >= 10) - { + if (m_sendPing) SendPing(udpClient); - udpClient.Elapsed500MSOutgoingPacketHandler = 0; - } // Dequeue any outgoing packets that are within the throttle limits if (udpClient.DequeueOutgoing()) m_packetSent = true; } - - udpClient.TickLastOutgoingPacketHandler = thisTick; } } catch (Exception ex) From b06f258319f088bcf6658880ed371ef313bac3b6 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 16:21:08 -0700 Subject: [PATCH 53/61] * FireQueueEmpty now checks if a measurable amount of time has passed, and if not it sleeps for a small amount of time. This throttles OnQueueEmpty calls where there is no callback or the callback is doing very little work * Changed HandleQueueEmpty()'s Monitor.TryEnter() calls to locks. We want to take our time in this function and do all the work necessary, since returning too fast will induce a sleep anyways --- .../ClientStack/LindenUDP/LLClientView.cs | 42 +++++-------------- .../ClientStack/LindenUDP/LLUDPClient.cs | 8 +++- .../LindenUDP/UnackedPacketCollection.cs | 4 -- 3 files changed, 16 insertions(+), 38 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 91afa4d2e7..0bb7a712c6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3558,45 +3558,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP ProcessTextureRequests(); break; case ThrottleOutPacketType.Task: - if (Monitor.TryEnter(m_avatarTerseUpdates.SyncRoot)) + lock (m_avatarTerseUpdates.SyncRoot) { - try - { - if (m_avatarTerseUpdates.Count > 0) - { - - ProcessAvatarTerseUpdates(); - return; - } - } - finally { Monitor.Exit(m_avatarTerseUpdates.SyncRoot); } + if (m_avatarTerseUpdates.Count > 0) + ProcessAvatarTerseUpdates(); } break; case ThrottleOutPacketType.State: - if (Monitor.TryEnter(m_primFullUpdates.SyncRoot)) + lock (m_primFullUpdates.SyncRoot) { - try - { - if (m_primFullUpdates.Count > 0) - { - ProcessPrimFullUpdates(); - return; - } - } - finally { Monitor.Exit(m_primFullUpdates.SyncRoot); } + if (m_primFullUpdates.Count > 0) + ProcessPrimFullUpdates(); } - if (Monitor.TryEnter(m_primTerseUpdates.SyncRoot)) + lock (m_primTerseUpdates.SyncRoot) { - try - { - if (m_primTerseUpdates.Count > 0) - { - ProcessPrimTerseUpdates(); - return; - } - } - finally { Monitor.Exit(m_primTerseUpdates.SyncRoot); } + if (m_primTerseUpdates.Count > 0) + ProcessPrimTerseUpdates(); } break; } @@ -10344,7 +10322,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { if (lookup.Heap.ContainsHandle(lookup.Handle)) lookup.Heap[lookup.Handle] = - new MinHeapItem(priority, item.Value, item.LocalID); + new MinHeapItem(priority, item.Value, item.LocalID, this.m_comparison); } else { diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 0a090b4452..71f4c47967 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -535,14 +535,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP ThrottleOutPacketType type = (ThrottleOutPacketType)i; QueueEmpty callback = OnQueueEmpty; + int start = Environment.TickCount; + if (callback != null) { try { callback(type); } catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } } - // HACK: Try spending some extra time here to slow down OnQueueEmpty calls - //System.Threading.Thread.Sleep(100); + // Make sure all queue empty calls take at least a measurable amount of time, + // otherwise we'll peg a CPU trying to fire these too fast + if (Environment.TickCount == start) + System.Threading.Thread.Sleep((int)m_udpServer.TickCountResolution); m_onQueueEmptyRunning[i] = false; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index bd5fe1cab4..016712f8ae 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -125,10 +125,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP expiredPackets = new List(); expiredPackets.Add(packet); } - /*else - { - break; - }*/ } } From 4e04f6b3a5a875c7d8820c679bcbcdcfba1227bf Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 17:02:55 -0700 Subject: [PATCH 54/61] * Clarified what FireQueueEmpty is doing with a MIN_CALLBACK_MS constant and upped it to 30ms * Removed the unused PacketSent() function * Switched UnackedPacketCollection from a SortedDictionary to a Dictionary now that the sorting is no longer needed. Big performance improvement for ResendUnacked() --- OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 11 +++++++---- OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 4 ---- .../Region/ClientStack/LindenUDP/OpenSimUDPBase.cs | 9 --------- .../ClientStack/LindenUDP/UnackedPacketCollection.cs | 2 +- 4 files changed, 8 insertions(+), 18 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 71f4c47967..2d86a40d2a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -531,11 +531,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// as an object to match the WaitCallback delegate signature private void FireQueueEmpty(object o) { + const int MIN_CALLBACK_MS = 30; + int i = (int)o; ThrottleOutPacketType type = (ThrottleOutPacketType)i; QueueEmpty callback = OnQueueEmpty; - int start = Environment.TickCount; + int start = Environment.TickCount & Int32.MaxValue; if (callback != null) { @@ -543,10 +545,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } } - // Make sure all queue empty calls take at least a measurable amount of time, + // Make sure all queue empty calls take at least some amount of time, // otherwise we'll peg a CPU trying to fire these too fast - if (Environment.TickCount == start) - System.Threading.Thread.Sleep((int)m_udpServer.TickCountResolution); + int elapsedMS = (Environment.TickCount & Int32.MaxValue) - start; + if (elapsedMS < MIN_CALLBACK_MS) + System.Threading.Thread.Sleep(MIN_CALLBACK_MS - elapsedMS); m_onQueueEmptyRunning[i] = false; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 4bdf132dae..40d3771fbc 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -674,10 +674,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); } - protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent) - { - } - private void SendAckImmediate(IPEndPoint remoteEndpoint, uint sequenceNumber) { PacketAckPacket ack = new PacketAckPacket(); diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs index d16837d6b4..552cc4a773 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs @@ -45,13 +45,6 @@ namespace OpenMetaverse /// /// Incoming packet buffer protected abstract void PacketReceived(UDPPacketBuffer buffer); - - /// - /// This method is called when an outgoing packet is sent - /// - /// Outgoing packet buffer - /// Number of bytes written to the wire - protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent); /// UDP port to bind to in server mode protected int m_udpPort; @@ -279,8 +272,6 @@ namespace OpenMetaverse { UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; int bytesSent = m_udpSocket.EndSendTo(result); - - PacketSent(buf, bytesSent); } catch (SocketException) { } catch (ObjectDisposedException) { } diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index 016712f8ae..3e2e81c704 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -60,7 +60,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } /// Holds the actual unacked packet data, sorted by sequence number - private SortedDictionary m_packets = new SortedDictionary(); + private Dictionary m_packets = new Dictionary(); /// Holds packets that need to be added to the unacknowledged list private LocklessQueue m_pendingAdds = new LocklessQueue(); /// Holds information about pending acknowledgements From 9ec55df9880194e6e004fdde96ebdf1a196f3fca Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 22 Oct 2009 01:51:27 +0100 Subject: [PATCH 55/61] Mark new version --- OpenSim/Framework/Servers/VersionInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Framework/Servers/VersionInfo.cs b/OpenSim/Framework/Servers/VersionInfo.cs index 9f98310282..812abd17bb 100644 --- a/OpenSim/Framework/Servers/VersionInfo.cs +++ b/OpenSim/Framework/Servers/VersionInfo.cs @@ -29,7 +29,7 @@ namespace OpenSim { public class VersionInfo { - private const string VERSION_NUMBER = "0.6.8"; + private const string VERSION_NUMBER = "0.6.8-mel_t"; private const Flavour VERSION_FLAVOUR = Flavour.Dev; public enum Flavour From 6492640e72776d9f0a015e6a719c8cef28ccb7e3 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 18:03:41 -0700 Subject: [PATCH 56/61] * Change the OnQueueEmpty signature to send the flags of the queues that are empty instead of firing once per empty queue * Change the OnQueueEmpty firing to use a minimum time until next fire instead of a sleep * Set OutgoingPacket.TickCount = 0 earlier to avoid extra resends when things are running slowly (inside a profiler, for example) --- OpenSim/Framework/ThrottleOutPacketType.cs | 12 ++ .../ClientStack/LindenUDP/LLClientView.cs | 50 ++++---- .../ClientStack/LindenUDP/LLUDPClient.cs | 110 ++++++++++++------ .../ClientStack/LindenUDP/LLUDPServer.cs | 4 - .../LindenUDP/UnackedPacketCollection.cs | 5 + 5 files changed, 120 insertions(+), 61 deletions(-) diff --git a/OpenSim/Framework/ThrottleOutPacketType.cs b/OpenSim/Framework/ThrottleOutPacketType.cs index e21ff329f1..d56231a397 100644 --- a/OpenSim/Framework/ThrottleOutPacketType.cs +++ b/OpenSim/Framework/ThrottleOutPacketType.cs @@ -51,4 +51,16 @@ namespace OpenSim.Framework /// This is a sub-category of Task State = 7, } + + [Flags] + public enum ThrottleOutPacketTypeFlags + { + Land = 1 << 0, + Wind = 1 << 1, + Cloud = 1 << 2, + Task = 1 << 3, + Texture = 1 << 4, + Asset = 1 << 5, + State = 1 << 6, + } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 0bb7a712c6..432fee7dbd 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3550,33 +3550,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(attach, ThrottleOutPacketType.Task); } - void HandleQueueEmpty(ThrottleOutPacketType queue) + void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) { - switch (queue) + if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) { - case ThrottleOutPacketType.Texture: - ProcessTextureRequests(); - break; - case ThrottleOutPacketType.Task: - lock (m_avatarTerseUpdates.SyncRoot) - { - if (m_avatarTerseUpdates.Count > 0) - ProcessAvatarTerseUpdates(); - } - break; - case ThrottleOutPacketType.State: - lock (m_primFullUpdates.SyncRoot) - { - if (m_primFullUpdates.Count > 0) - ProcessPrimFullUpdates(); - } + lock (m_avatarTerseUpdates.SyncRoot) + { + if (m_avatarTerseUpdates.Count > 0) + ProcessAvatarTerseUpdates(); + } + } - lock (m_primTerseUpdates.SyncRoot) - { - if (m_primTerseUpdates.Count > 0) - ProcessPrimTerseUpdates(); - } - break; + if ((categories & ThrottleOutPacketTypeFlags.State) != 0) + { + lock (m_primFullUpdates.SyncRoot) + { + if (m_primFullUpdates.Count > 0) + ProcessPrimFullUpdates(); + } + + lock (m_primTerseUpdates.SyncRoot) + { + if (m_primTerseUpdates.Count > 0) + ProcessPrimTerseUpdates(); + } + } + + if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) + { + ProcessTextureRequests(); } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 2d86a40d2a..a9bc7d2d49 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -50,11 +50,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// are waiting on ACKs for public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes); /// - /// Fired when the queue for a packet category is empty. This event can be - /// hooked to put more data on the empty queue + /// Fired when the queue for one or more packet categories is empty. This + /// event can be hooked to put more data on the empty queues /// - /// Category of the packet queue that is empty - public delegate void QueueEmpty(ThrottleOutPacketType category); + /// Categories of the packet queues that are empty + public delegate void QueueEmpty(ThrottleOutPacketTypeFlags categories); #endregion Delegates @@ -128,6 +128,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_packetsReceivedReported; /// Total number of sent packets that we have reported to the OnPacketStats event(s) private int m_packetsSentReported; + /// Holds the Environment.TickCount value of when the next OnQueueEmpty can be fired + private int m_nextOnQueueEmpty = 1; /// Throttle bucket for this agent's connection private readonly TokenBucket m_throttle; @@ -140,9 +142,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// 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]; - /// Flags to prevent queue empty callbacks from stacking up on - /// top of each other - private readonly bool[] m_onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT]; /// A reference to the LLUDPServer that is managing this client private readonly LLUDPServer m_udpServer; @@ -405,6 +404,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.LocklessQueue queue; TokenBucket bucket; bool packetSent = false; + ThrottleOutPacketTypeFlags emptyCategories = 0; //string queueDebugOutput = String.Empty; // Serious debug business @@ -452,17 +452,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP // empty callback now so it has a chance to fill before we // get back here if (queue.Count == 0) - BeginFireQueueEmpty(i); + emptyCategories |= CategoryToFlag(i); } else { // No packets in this queue. Fire the queue empty callback // if it has not been called recently - BeginFireQueueEmpty(i); + emptyCategories |= CategoryToFlag(i); } } } + if (emptyCategories != 0) + BeginFireQueueEmpty(emptyCategories); + //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business return packetSent; } @@ -509,49 +512,90 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// Throttle category to fire the callback /// for - private void BeginFireQueueEmpty(int throttleIndex) + private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) { - // Unknown is -1 and Resend is 0. Make sure we are only firing the - // callback for categories other than those - if (throttleIndex > 0) + if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) { - if (!m_onQueueEmptyRunning[throttleIndex]) - { - m_onQueueEmptyRunning[throttleIndex] = true; - Util.FireAndForget(FireQueueEmpty, throttleIndex); - } + // Use a value of 0 to signal that FireQueueEmpty is running + m_nextOnQueueEmpty = 0; + // Asynchronously run the callback + Util.FireAndForget(FireQueueEmpty, categories); } } /// - /// Checks to see if this queue empty callback is already running, - /// then firing the event + /// Fires the OnQueueEmpty callback and sets the minimum time that it + /// can be called again /// - /// Throttle category to fire the callback for, stored - /// as an object to match the WaitCallback delegate signature + /// Throttle categories to fire the callback for, + /// stored as an object to match the WaitCallback delegate + /// signature private void FireQueueEmpty(object o) { const int MIN_CALLBACK_MS = 30; - int i = (int)o; - ThrottleOutPacketType type = (ThrottleOutPacketType)i; + ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; QueueEmpty callback = OnQueueEmpty; - + int start = Environment.TickCount & Int32.MaxValue; if (callback != null) { - try { callback(type); } - catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } + try { callback(categories); } + catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } } - // Make sure all queue empty calls take at least some amount of time, - // otherwise we'll peg a CPU trying to fire these too fast - int elapsedMS = (Environment.TickCount & Int32.MaxValue) - start; - if (elapsedMS < MIN_CALLBACK_MS) - System.Threading.Thread.Sleep(MIN_CALLBACK_MS - elapsedMS); + m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; + if (m_nextOnQueueEmpty == 0) + m_nextOnQueueEmpty = 1; + } - m_onQueueEmptyRunning[i] = false; + /// + /// Converts a integer to a + /// flag value + /// + /// Throttle category to convert + /// Flag representation of the throttle category + private static ThrottleOutPacketTypeFlags CategoryToFlag(int i) + { + ThrottleOutPacketType category = (ThrottleOutPacketType)i; + + /* + * Land = 1, + /// Wind data + Wind = 2, + /// Cloud data + Cloud = 3, + /// Any packets that do not fit into the other throttles + Task = 4, + /// Texture assets + Texture = 5, + /// Non-texture assets + Asset = 6, + /// Avatar and primitive data + /// This is a sub-category of Task + State = 7, + */ + + switch (category) + { + case ThrottleOutPacketType.Land: + return ThrottleOutPacketTypeFlags.Land; + case ThrottleOutPacketType.Wind: + return ThrottleOutPacketTypeFlags.Wind; + case ThrottleOutPacketType.Cloud: + return ThrottleOutPacketTypeFlags.Cloud; + case ThrottleOutPacketType.Task: + return ThrottleOutPacketTypeFlags.Task; + case ThrottleOutPacketType.Texture: + return ThrottleOutPacketTypeFlags.Texture; + case ThrottleOutPacketType.Asset: + return ThrottleOutPacketTypeFlags.Asset; + case ThrottleOutPacketType.State: + return ThrottleOutPacketTypeFlags.State; + default: + return 0; + } } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 40d3771fbc..a9f4b2cf36 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -424,10 +424,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); outgoingPacket.Category = ThrottleOutPacketType.Resend; - // The TickCount will be set to the current time when the packet - // is actually sent out again - outgoingPacket.TickCount = 0; - // Bump up the resend count on this packet Interlocked.Increment(ref outgoingPacket.ResendCount); //Interlocked.Increment(ref Stats.ResentPackets); diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index 3e2e81c704..e43f7cf57a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -123,6 +123,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP { if (expiredPackets == null) expiredPackets = new List(); + + // The TickCount will be set to the current time when the packet + // is actually sent out again + packet.TickCount = 0; + expiredPackets.Add(packet); } } From 8a336c6860d66b9fbba6922c32e7a57fd355c57e Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 22 Oct 2009 02:28:53 +0100 Subject: [PATCH 57/61] Add MaxPoolThreads in startup to limit the size of the thread pool used for FireAndForget. This lets us limit concurrency to make OpenSim play nice --- OpenSim/Framework/Util.cs | 34 +++++++++++++------- OpenSim/Region/Application/OpenSim.cs | 2 ++ bin/OpenSim.ini.example | 3 ++ prebuild.xml | 45 ++++++++++++++------------- 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index d5ae3b7ebd..9dfb75ecfd 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -41,12 +41,14 @@ using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Xml; +using System.Threading; using log4net; using Nini.Config; using Nwc.XmlRpc; using BclExtras; using OpenMetaverse; using OpenMetaverse.StructuredData; +using Amib.Threading; namespace OpenSim.Framework { @@ -55,6 +57,8 @@ namespace OpenSim.Framework /// public class Util { + private static SmartThreadPool m_ThreadPool = null; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static uint nextXferID = 5000; @@ -1293,22 +1297,28 @@ namespace OpenSim.Framework System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, null); } - public static void FireAndForget(System.Threading.WaitCallback callback, object obj) + public static void SetMaxThreads(int maxThreads) { - //FireAndForgetWrapper wrapper = Singleton.GetInstance(); - //wrapper.FireAndForget(callback, obj); - System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, obj); + STPStartInfo startInfo = new STPStartInfo(); + startInfo.IdleTimeout = 2000; // 2 seconds + startInfo.MaxWorkerThreads = maxThreads; + startInfo.MinWorkerThreads = 5; + startInfo.StackSize = 524288; + startInfo.ThreadPriority = ThreadPriority.Normal; + + startInfo.StartSuspended = false; + + m_ThreadPool = new SmartThreadPool(startInfo); } - /*private static void EndFireAndForget(IAsyncResult ar) + public static void FireAndForget(System.Threading.WaitCallback callback, object obj) { - System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; - - try { callback.EndInvoke(ar); } - catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); } - - ar.AsyncWaitHandle.Close(); - }*/ + m_ThreadPool.QueueWorkItem(new WorkItemCallback(delegate(object o) + { + callback(o); + return null; + }), obj); + } #endregion FireAndForget Threading Pattern } diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 143dd2a2a0..42e2a1ea27 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -67,6 +67,8 @@ namespace OpenSim IConfig startupConfig = m_config.Source.Configs["Startup"]; + Util.SetMaxThreads(startupConfig.GetInt("MaxPoolThreads", 30)); + if (startupConfig != null) { m_startupCommandsFile = startupConfig.GetString("startup_console_commands_file", "startup_commands.txt"); diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 2d56f4eaab..92e6d91f10 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -42,6 +42,9 @@ ; Set this to the DLL containing the client stack to use. clientstack_plugin="OpenSim.Region.ClientStack.LindenUDP.dll" + ; Max threads to allocate on the FireAndForget pool + MaxPoolThreads = 30 + ; ## ; ## REGIONS ; ## diff --git a/prebuild.xml b/prebuild.xml index e17da9ad65..81f907deef 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -117,6 +117,28 @@ + + + + ../../bin/ + + + + + ../../bin/ + + + + ../../bin/ + + + + + + + + + @@ -145,6 +167,7 @@ + @@ -2387,28 +2410,6 @@ - - - - ../../bin/ - - - - - ../../bin/ - - - - ../../bin/ - - - - - - - - - From 8ce4fd7234bd460652f6159a3b7a21d2bebee05d Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 22 Oct 2009 04:02:26 +0100 Subject: [PATCH 58/61] Reduce the default pool threads to 15 (from 30) and the minimum from 5 to 2 --- OpenSim/Framework/Util.cs | 2 +- OpenSim/Region/Application/OpenSim.cs | 2 +- bin/OpenSim.ini.example | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 9dfb75ecfd..7dde6ddcdf 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1302,7 +1302,7 @@ namespace OpenSim.Framework STPStartInfo startInfo = new STPStartInfo(); startInfo.IdleTimeout = 2000; // 2 seconds startInfo.MaxWorkerThreads = maxThreads; - startInfo.MinWorkerThreads = 5; + startInfo.MinWorkerThreads = 2; startInfo.StackSize = 524288; startInfo.ThreadPriority = ThreadPriority.Normal; diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 42e2a1ea27..c9cec75c8c 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -67,7 +67,7 @@ namespace OpenSim IConfig startupConfig = m_config.Source.Configs["Startup"]; - Util.SetMaxThreads(startupConfig.GetInt("MaxPoolThreads", 30)); + Util.SetMaxThreads(startupConfig.GetInt("MaxPoolThreads", 15)); if (startupConfig != null) { diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 92e6d91f10..25d3f39205 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -43,7 +43,7 @@ clientstack_plugin="OpenSim.Region.ClientStack.LindenUDP.dll" ; Max threads to allocate on the FireAndForget pool - MaxPoolThreads = 30 + MaxPoolThreads = 15 ; ## ; ## REGIONS From 624af66c35cdd203b6210a1932c737cb21e2b05d Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 22 Oct 2009 05:42:34 +0100 Subject: [PATCH 59/61] Make the LSL scripting delays take full effect. To tune, tweat the ScriptDelayFactor in config --- .../Shared/Api/Implementation/LSL_Api.cs | 105 ++++++++---------- 1 file changed, 49 insertions(+), 56 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index a94b4e4795..6e17639e6d 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -133,13 +133,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return lease; } - protected virtual void ConditionalScriptSleep(int delay) - { - // Uncomment to get SL compatibility! - // - // ScriptSleep(delay); - } - protected virtual void ScriptSleep(int delay) { delay = (int)((float)delay * m_ScriptDelayFactor); @@ -1682,7 +1675,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); SetTexture(m_host, texture, face); - ConditionalScriptSleep(200); + ScriptSleep(200); } public void llSetLinkTexture(int linknumber, string texture, int face) @@ -1694,7 +1687,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api foreach (SceneObjectPart part in parts) SetTexture(part, texture, face); - ConditionalScriptSleep(200); + ScriptSleep(200); } protected void SetTexture(SceneObjectPart part, string texture, int face) @@ -1739,7 +1732,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); ScaleTexture(m_host, u, v, face); - ConditionalScriptSleep(200); + ScriptSleep(200); } protected void ScaleTexture(SceneObjectPart part, double u, double v, int face) @@ -1775,7 +1768,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); OffsetTexture(m_host, u, v, face); - ConditionalScriptSleep(200); + ScriptSleep(200); } protected void OffsetTexture(SceneObjectPart part, double u, double v, int face) @@ -1811,7 +1804,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); RotateTexture(m_host, rotation, face); - ConditionalScriptSleep(200); + ScriptSleep(200); } protected void RotateTexture(SceneObjectPart part, double rotation, int face) @@ -2283,7 +2276,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); m_host.PreloadSound(sound); - ConditionalScriptSleep(1000); + ScriptSleep(1000); } /// @@ -2575,28 +2568,28 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); Deprecated("llMakeExplosion"); - ConditionalScriptSleep(100); + ScriptSleep(100); } public void llMakeFountain(int particles, double scale, double vel, double lifetime, double arc, int bounce, string texture, LSL_Vector offset, double bounce_offset) { m_host.AddScriptLPS(1); Deprecated("llMakeFountain"); - ConditionalScriptSleep(100); + ScriptSleep(100); } public void llMakeSmoke(int particles, double scale, double vel, double lifetime, double arc, string texture, LSL_Vector offset) { m_host.AddScriptLPS(1); Deprecated("llMakeSmoke"); - ConditionalScriptSleep(100); + ScriptSleep(100); } public void llMakeFire(int particles, double scale, double vel, double lifetime, double arc, string texture, LSL_Vector offset) { m_host.AddScriptLPS(1); Deprecated("llMakeFire"); - ConditionalScriptSleep(100); + ScriptSleep(100); } public void llRezAtRoot(string inventory, LSL_Vector pos, LSL_Vector vel, LSL_Rotation rot, int param) @@ -2655,7 +2648,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } // Variable script delay? (see (http://wiki.secondlife.com/wiki/LSL_Delay) ScriptSleep((int)((groupmass * velmag) / 10)); - ConditionalScriptSleep(100); + ScriptSleep(100); return; } } @@ -2941,7 +2934,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } emailModule.SendEmail(m_host.UUID, address, subject, message); - ConditionalScriptSleep(20000); + ScriptSleep(20000); } public void llGetNextEmail(string address, string subject) @@ -3745,7 +3738,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // destination is an object World.MoveTaskInventoryItem(destId, m_host, objId); } - ConditionalScriptSleep(3000); + ScriptSleep(3000); } public void llRemoveInventory(string name) @@ -3846,7 +3839,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api AsyncCommands. DataserverPlugin.DataserverReply(rq.ToString(), reply); - ConditionalScriptSleep(100); + ScriptSleep(100); return tid.ToString(); } @@ -3884,11 +3877,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api reply); }); - ConditionalScriptSleep(1000); + ScriptSleep(1000); return tid.ToString(); } } - ConditionalScriptSleep(1000); + ScriptSleep(1000); return String.Empty; } @@ -3916,7 +3909,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - ConditionalScriptSleep(5000); + ScriptSleep(5000); } public void llTextBox(string avatar, string message, int chat_channel) @@ -5376,7 +5369,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); m_host.AdjustSoundGain(volume); - ConditionalScriptSleep(100); + ScriptSleep(100); } public void llSetSoundQueueing(int queue) @@ -5459,7 +5452,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api World.TeleportClientHome(agentId, presence.ControllingClient); } } - ConditionalScriptSleep(5000); + ScriptSleep(5000); } public LSL_List llParseString2List(string str, LSL_List separators, LSL_List in_spacers) @@ -6151,7 +6144,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api land.ParcelAccessList.Add(entry); } } - ConditionalScriptSleep(100); + ScriptSleep(100); } public void llSetTouchText(string text) @@ -6248,7 +6241,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api av, m_host.Name, m_host.UUID, m_host.OwnerID, message, new UUID("00000000-0000-2222-3333-100000001000"), chat_channel, buts); - ConditionalScriptSleep(1000); + ScriptSleep(1000); } public void llVolumeDetect(int detect) @@ -6273,7 +6266,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); // Report an error as it does in SL ShoutError("Deprecated. Please use llRemoteLoadScriptPin instead."); - ConditionalScriptSleep(3000); + ScriptSleep(3000); } public void llSetRemoteScriptAccessPin(int pin) @@ -6359,14 +6352,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_ScriptEngine.PostScriptEvent(m_itemID, new EventParams("remote_data", resobj, new DetectParams[0])); } - ConditionalScriptSleep(1000); + ScriptSleep(1000); } public LSL_String llSendRemoteData(string channel, string dest, int idata, string sdata) { m_host.AddScriptLPS(1); IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface(); - ConditionalScriptSleep(3000); + ScriptSleep(3000); return (xmlrpcMod.SendRemoteData(m_localID, m_itemID, channel, dest, idata, sdata)).ToString(); } @@ -6375,7 +6368,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface(); xmlrpcMod.RemoteDataReply(channel, message_id, sdata, idata); - ConditionalScriptSleep(3000); + ScriptSleep(3000); } public void llCloseRemoteDataChannel(string channel) @@ -6383,7 +6376,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface(); xmlrpcMod.CloseXMLRPCChannel((UUID)channel); - ConditionalScriptSleep(1000); + ScriptSleep(1000); } public LSL_String llMD5String(string src, int nonce) @@ -7114,7 +7107,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); Deprecated("llXorBase64Strings"); - ConditionalScriptSleep(300); + ScriptSleep(300); return String.Empty; } @@ -7162,7 +7155,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api land.SetMusicUrl(url); - ConditionalScriptSleep(2000); + ScriptSleep(2000); } public LSL_Vector llGetRootPosition() @@ -8207,7 +8200,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case 5: // DATA_SIM_POS if (info == null) { - ConditionalScriptSleep(1000); + ScriptSleep(1000); return UUID.Zero.ToString(); } reply = new LSL_Vector( @@ -8224,7 +8217,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case 7: // DATA_SIM_RATING if (info == null) { - ConditionalScriptSleep(1000); + ScriptSleep(1000); return UUID.Zero.ToString(); } int access = info.Maturity; @@ -8243,7 +8236,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api reply = "OpenSim"; break; default: - ConditionalScriptSleep(1000); + ScriptSleep(1000); return UUID.Zero.ToString(); // Raise no event } UUID rq = UUID.Random(); @@ -8254,7 +8247,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api AsyncCommands. DataserverPlugin.DataserverReply(rq.ToString(), reply); - ConditionalScriptSleep(1000); + ScriptSleep(1000); return tid.ToString(); } catch(Exception) @@ -8398,7 +8391,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api dm.SendUrlToUser( new UUID(avatar_id), m_host.Name, m_host.UUID, m_host.OwnerID, false, message, url); - ConditionalScriptSleep(10000); + ScriptSleep(10000); } public void llParcelMediaCommandList(LSL_List commandList) @@ -8634,7 +8627,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api time); } } - ConditionalScriptSleep(2000); + ScriptSleep(2000); } public LSL_List llParcelMediaQuery(LSL_List aList) @@ -8672,7 +8665,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } - ConditionalScriptSleep(2000); + ScriptSleep(2000); return list; } @@ -8681,7 +8674,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); Int64 tmp = 0; Math.DivRem(Convert.ToInt64(Math.Pow(a, b)), c, out tmp); - ConditionalScriptSleep(1000); + ScriptSleep(1000); return Convert.ToInt32(tmp); } @@ -8785,7 +8778,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public void llSetPrimURL(string url) { m_host.AddScriptLPS(1); - ConditionalScriptSleep(2000); + ScriptSleep(2000); } /// @@ -8796,7 +8789,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); ShoutError("llRefreshPrimURL - not yet supported"); - ConditionalScriptSleep(20000); + ScriptSleep(20000); } public LSL_String llEscapeURL(string url) @@ -8838,7 +8831,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api new Vector3((float)pos.x, (float)pos.y, (float)pos.z), new Vector3((float)lookAt.x, (float)lookAt.y, (float)lookAt.z)); } - ConditionalScriptSleep(1000); + ScriptSleep(1000); } public void llAddToLandBanList(string avatar, double hours) @@ -8857,7 +8850,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api land.ParcelAccessList.Add(entry); } } - ConditionalScriptSleep(100); + ScriptSleep(100); } public void llRemoveFromLandPassList(string avatar) @@ -8879,7 +8872,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - ConditionalScriptSleep(100); + ScriptSleep(100); } public void llRemoveFromLandBanList(string avatar) @@ -8901,7 +8894,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - ConditionalScriptSleep(100); + ScriptSleep(100); } public void llSetCameraParams(LSL_List rules) @@ -9163,7 +9156,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - ConditionalScriptSleep(100); + ScriptSleep(100); } public void llResetLandPassList() @@ -9180,7 +9173,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - ConditionalScriptSleep(100); + ScriptSleep(100); } public LSL_Integer llGetParcelPrimCount(LSL_Vector pos, int category, int sim_wide) @@ -9259,7 +9252,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ret.Add(detectedParams.Value); } } - ConditionalScriptSleep(2000); + ScriptSleep(2000); return ret; } @@ -9517,7 +9510,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api AsyncCommands. DataserverPlugin.DataserverReply(assetID.ToString(), NotecardCache.GetLines(assetID).ToString()); - ConditionalScriptSleep(100); + ScriptSleep(100); return tid.ToString(); } @@ -9539,7 +9532,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api NotecardCache.GetLines(id).ToString()); }); - ConditionalScriptSleep(100); + ScriptSleep(100); return tid.ToString(); } @@ -9578,7 +9571,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { AsyncCommands.DataserverPlugin.DataserverReply(assetID.ToString(), NotecardCache.GetLine(assetID, line, m_notecardLineReadCharsMax)); - ConditionalScriptSleep(100); + ScriptSleep(100); return tid.ToString(); } @@ -9599,7 +9592,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api NotecardCache.GetLine(id, line, m_notecardLineReadCharsMax)); }); - ConditionalScriptSleep(100); + ScriptSleep(100); return tid.ToString(); } } From 32ccd5bb40447ea4d96f1181cf73edff3645a55a Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 23:03:18 -0700 Subject: [PATCH 60/61] * Changed the misc. methods calling ThreadPool.UnsafeQueueUserWorkItem() to Util.FireAndForget() * Changed Util.FireAndForget() to use any of five different methods set with async_call_method in the [Startup] section of OpenSim.ini. Look at the example config for possible values --- .../Framework/Communications/RestClient.cs | 2 +- OpenSim/Framework/Parallel.cs | 6 +- OpenSim/Framework/Util.cs | 70 ++++++++++++++----- .../MessagingServer.Modules/MessageService.cs | 3 +- OpenSim/Region/Application/OpenSim.cs | 9 ++- .../ClientStack/LindenUDP/LLClientView.cs | 6 +- .../CoreModules/Asset/FlotsamAssetCache.cs | 8 +-- .../World/WorldMap/WorldMapModule.cs | 2 +- bin/OpenSim.ini.example | 9 +++ 9 files changed, 79 insertions(+), 36 deletions(-) diff --git a/OpenSim/Framework/Communications/RestClient.cs b/OpenSim/Framework/Communications/RestClient.cs index 317b3a0946..97b3b60cce 100644 --- a/OpenSim/Framework/Communications/RestClient.cs +++ b/OpenSim/Framework/Communications/RestClient.cs @@ -403,7 +403,7 @@ namespace OpenSim.Framework.Communications /// In case, we are invoked asynchroneously this object will keep track of the state /// AsyncResult ar = new AsyncResult(callback, state); - ThreadPool.UnsafeQueueUserWorkItem(RequestHelper, ar); + Util.FireAndForget(RequestHelper, ar); return ar; } diff --git a/OpenSim/Framework/Parallel.cs b/OpenSim/Framework/Parallel.cs index ab2e108353..70eecdcdac 100644 --- a/OpenSim/Framework/Parallel.cs +++ b/OpenSim/Framework/Parallel.cs @@ -66,7 +66,7 @@ namespace OpenSim.Framework for (int i = 0; i < threadCount; i++) { - ThreadPool.UnsafeQueueUserWorkItem( + Util.FireAndForget( delegate(object o) { int threadIndex = (int)o; @@ -122,7 +122,7 @@ namespace OpenSim.Framework for (int i = 0; i < threadCount; i++) { - ThreadPool.UnsafeQueueUserWorkItem( + Util.FireAndForget( delegate(object o) { int threadIndex = (int)o; @@ -178,7 +178,7 @@ namespace OpenSim.Framework for (int i = 0; i < threadCount; i++) { - ThreadPool.UnsafeQueueUserWorkItem( + Util.FireAndForget( delegate(object o) { int threadIndex = (int)o; diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index d5ae3b7ebd..02f6d12839 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -50,6 +50,18 @@ using OpenMetaverse.StructuredData; namespace OpenSim.Framework { + /// + /// The method used by Util.FireAndForget for asynchronously firing events + /// + public enum FireAndForgetMethod + { + UnsafeQueueUserWorkItem, + QueueUserWorkItem, + BeginInvoke, + SmartThreadPool, + Thread, + } + /// /// Miscellaneous utility functions /// @@ -70,7 +82,9 @@ namespace OpenSim.Framework public static readonly Regex UUIDPattern = new Regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); - + + public static FireAndForgetMethod FireAndForgetMethod = FireAndForgetMethod.UnsafeQueueUserWorkItem; + /// /// Linear interpolates B<->C using percent A /// @@ -1273,7 +1287,7 @@ namespace OpenSim.Framework /// /// Created to work around a limitation in Mono with nested delegates /// - /*private class FireAndForgetWrapper + private class FireAndForgetWrapper { public void FireAndForget(System.Threading.WaitCallback callback) { @@ -1284,32 +1298,50 @@ namespace OpenSim.Framework { callback.BeginInvoke(obj, EndFireAndForget, callback); } - }*/ + + private static void EndFireAndForget(IAsyncResult ar) + { + System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; + + try { callback.EndInvoke(ar); } + catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); } + + ar.AsyncWaitHandle.Close(); + } + } public static void FireAndForget(System.Threading.WaitCallback callback) { - //FireAndForgetWrapper wrapper = Singleton.GetInstance(); - //wrapper.FireAndForget(callback); - System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, null); + FireAndForget(callback, null); } public static void FireAndForget(System.Threading.WaitCallback callback, object obj) { - //FireAndForgetWrapper wrapper = Singleton.GetInstance(); - //wrapper.FireAndForget(callback, obj); - System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, obj); + switch (FireAndForgetMethod) + { + case FireAndForgetMethod.UnsafeQueueUserWorkItem: + System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, obj); + break; + case FireAndForgetMethod.QueueUserWorkItem: + System.Threading.ThreadPool.QueueUserWorkItem(callback, obj); + break; + case FireAndForgetMethod.BeginInvoke: + FireAndForgetWrapper wrapper = Singleton.GetInstance(); + wrapper.FireAndForget(callback, obj); + break; + case FireAndForgetMethod.SmartThreadPool: + Amib.Threading.SmartThreadPool stp = Singleton.GetInstance(); + stp.QueueWorkItem(delegate(object o) { callback(o); return null; }, obj); + break; + case FireAndForgetMethod.Thread: + System.Threading.Thread thread = new System.Threading.Thread(delegate(object o) { callback(o); }); + thread.Start(obj); + break; + default: + throw new NotImplementedException(); + } } - /*private static void EndFireAndForget(IAsyncResult ar) - { - System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; - - try { callback.EndInvoke(ar); } - catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); } - - ar.AsyncWaitHandle.Close(); - }*/ - #endregion FireAndForget Threading Pattern } } diff --git a/OpenSim/Grid/MessagingServer.Modules/MessageService.cs b/OpenSim/Grid/MessagingServer.Modules/MessageService.cs index 49f0fa8e09..8ad1e9cdde 100644 --- a/OpenSim/Grid/MessagingServer.Modules/MessageService.cs +++ b/OpenSim/Grid/MessagingServer.Modules/MessageService.cs @@ -148,8 +148,7 @@ namespace OpenSim.Grid.MessagingServer.Modules friendlistupdater.presence2 = receiver; friendlistupdater.OnGetRegionData += m_regionModule.GetRegionInfo; friendlistupdater.OnDone += PresenceUpdateDone; - WaitCallback cb = new WaitCallback(friendlistupdater.go); - ThreadPool.UnsafeQueueUserWorkItem(cb, null); + Util.FireAndForget(friendlistupdater.go); } else { diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 143dd2a2a0..5be1816c04 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -90,10 +90,17 @@ namespace OpenSim appender.File = fileName; appender.ActivateOptions(); } - m_log.InfoFormat("[LOGGING] Logging started to file {0}", appender.File); + m_log.InfoFormat("[LOGGING]: Logging started to file {0}", appender.File); } } + + string asyncCallMethodStr = startupConfig.GetString("async_call_method", String.Empty); + FireAndForgetMethod asyncCallMethod; + if (!String.IsNullOrEmpty(asyncCallMethodStr) && Utils.EnumTryParse(asyncCallMethodStr, out asyncCallMethod)) + Util.FireAndForgetMethod = asyncCallMethod; } + + m_log.Info("[OPENSIM MAIN]: Using async_call_method " + Util.FireAndForgetMethod); } /// diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 432fee7dbd..0ba76ecd70 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -791,7 +791,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// heightmap public virtual void SendLayerData(float[] map) { - ThreadPool.UnsafeQueueUserWorkItem(DoSendLayerData, map); + Util.FireAndForget(DoSendLayerData, map); } /// @@ -931,7 +931,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// 16x16 array of wind speeds public virtual void SendWindData(Vector2[] windSpeeds) { - ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(DoSendWindData), (object)windSpeeds); + Util.FireAndForget(DoSendWindData, windSpeeds); } /// @@ -940,7 +940,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// 16x16 array of cloud densities public virtual void SendCloudData(float[] cloudDensity) { - ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(DoSendCloudData), (object)cloudDensity); + Util.FireAndForget(DoSendCloudData, cloudDensity); } /// diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index cbdca16e74..c6af8067a4 100644 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs @@ -302,12 +302,8 @@ namespace Flotsam.RegionModules.AssetCache } - ThreadPool.UnsafeQueueUserWorkItem( - delegate - { - WriteFileCache(filename, asset); - }, null - ); + Util.FireAndForget( + delegate { WriteFileCache(filename, asset); }); } } catch (Exception e) diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs index 126058454c..4e4008431f 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs @@ -1095,7 +1095,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap // The reason is so we don't cause the thread to freeze waiting // for the 1 second it costs to start a thread manually. if (!threadrunning) - ThreadPool.UnsafeQueueUserWorkItem(this.StartThread, null); + Util.FireAndForget(this.StartThread); lock (m_rootAgents) { diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 2d56f4eaab..35b08f95d4 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -31,6 +31,15 @@ ; To run a script every few minutes, set the script filename here ; timer_Script = "filename" + + ; ## + ; ## SYSTEM + ; ## + + ; Sets the method that OpenSim will use to fire asynchronous + ; events. Valid values are UnsafeQueueUserWorkItem, + ; QueueUserWorkItem, BeginInvoke, SmartThreadPool, and Thread + async_call_method = UnsafeQueueUserWorkItem ; ## ; ## CLIENTS From 1e71e3f910c2e249d50afe52b00e26450f29110f Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 22 Oct 2009 07:03:40 +0100 Subject: [PATCH 61/61] Remove the "mel_t" from version string --- OpenSim/Framework/Servers/VersionInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Framework/Servers/VersionInfo.cs b/OpenSim/Framework/Servers/VersionInfo.cs index 812abd17bb..9f98310282 100644 --- a/OpenSim/Framework/Servers/VersionInfo.cs +++ b/OpenSim/Framework/Servers/VersionInfo.cs @@ -29,7 +29,7 @@ namespace OpenSim { public class VersionInfo { - private const string VERSION_NUMBER = "0.6.8-mel_t"; + private const string VERSION_NUMBER = "0.6.8"; private const Flavour VERSION_FLAVOUR = Flavour.Dev; public enum Flavour