From 5ee53b32ac140a66cd73d6c351b07c4084cabe7d Mon Sep 17 00:00:00 2001 From: cinder Date: Tue, 27 May 2025 14:16:03 -0500 Subject: [PATCH] ObjectsAvatars and ObjectsPrimitives are now ConcurrentDictionary to avoid a rather nasty locking bottleneck. --- LibreMetaverse/AgentManager.cs | 9 +- LibreMetaverse/AgentManagerMovement.cs | 2 +- LibreMetaverse/AvatarManager.cs | 74 ++-- LibreMetaverse/ObjectManager.cs | 374 +++++++++--------- LibreMetaverse/Simulator.cs | 51 +-- .../TestClient/Commands/Agent/BotsCommand.cs | 23 +- .../TestClient/Commands/Agent/TouchCommand.cs | 34 +- .../TestClient/Commands/Agent/WhoCommand.cs | 17 +- .../Commands/Appearance/AttachmentsCommand.cs | 14 +- .../Commands/Appearance/AvatarInfoCommand.cs | 11 +- .../Commands/Inventory/DumpOutfitCommand.cs | 9 +- .../Inventory/ObjectInventoryCommand.cs | 71 ++-- .../Commands/Inventory/TaskRunningCommand.cs | 22 +- .../Commands/Movement/FollowCommand.cs | 18 +- .../Commands/Movement/SitCommand.cs | 36 +- .../Commands/Movement/SitOnCommand.cs | 13 +- .../Commands/Prims/ChangePermsCommand.cs | 27 +- .../Commands/Prims/DeRezObjectCommand.cs | 46 +-- .../Commands/Prims/ExportCommand.cs | 174 ++++---- .../Commands/Prims/ExportParticlesCommand.cs | 205 +++++----- .../Commands/Prims/FindObjectsCommand.cs | 35 +- .../Commands/Prims/FindTextureCommand.cs | 43 +- .../Commands/Prims/PrimInfoCommand.cs | 115 +++--- .../Commands/Prims/PrimRegexCommand.cs | 64 +-- .../Commands/System/SetMasterKeyCommand.cs | 19 +- 25 files changed, 747 insertions(+), 759 deletions(-) diff --git a/LibreMetaverse/AgentManager.cs b/LibreMetaverse/AgentManager.cs index e50f6b32..66125b98 100644 --- a/LibreMetaverse/AgentManager.cs +++ b/LibreMetaverse/AgentManager.cs @@ -1,6 +1,6 @@ /* * Copyright (c) 2006-2016, openmetaverse.co - * Copyright (c) 2019-2024, Sjofn LLC + * Copyright (c) 2019-2025, Sjofn LLC * All rights reserved. * * - Redistribution and use in source and binary forms, with or without @@ -1398,8 +1398,7 @@ namespace OpenMetaverse // go up the hierarchy trying to find the root prim while (p != null && p.ParentID != 0) { - Avatar av; - if (Client.Network.CurrentSim.ObjectsAvatars.TryGetValue(p.ParentID, out av)) + if (Client.Network.CurrentSim.ObjectsAvatars.TryGetValue(p.ParentID, out var av)) { p = av; fullPosition += p.Position; @@ -1437,8 +1436,8 @@ namespace OpenMetaverse { if (sittingOn != 0) { - Primitive parent; - if (Client.Network.CurrentSim != null && Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(sittingOn, out parent)) + if (Client.Network.CurrentSim != null + && Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(sittingOn, out var parent)) { return relativeRotation * parent.Rotation; } diff --git a/LibreMetaverse/AgentManagerMovement.cs b/LibreMetaverse/AgentManagerMovement.cs index d885952c..fed22d44 100644 --- a/LibreMetaverse/AgentManagerMovement.cs +++ b/LibreMetaverse/AgentManagerMovement.cs @@ -532,7 +532,7 @@ namespace OpenMetaverse { if (!Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(Client.Self.SittingOn, out var parent)) { - Logger.Log("Attempted TurnToward but parent prim is not in dictionary", Helpers.LogLevel.Warning, Client); + Logger.Log("Attempted TurnToward but parent prim is not found", Helpers.LogLevel.Warning, Client); return false; } diff --git a/LibreMetaverse/AvatarManager.cs b/LibreMetaverse/AvatarManager.cs index ee68cf6a..7591d7d6 100644 --- a/LibreMetaverse/AvatarManager.cs +++ b/LibreMetaverse/AvatarManager.cs @@ -1,6 +1,6 @@ /* * Copyright (c) 2006-2016, openmetaverse.co - * Copyright (c) 2021-2022, Sjofn LLC. + * Copyright (c) 2021-2025, Sjofn LLC. * All rights reserved. * * - Redistribution and use in source and binary forms, with or without @@ -581,11 +581,11 @@ namespace OpenMetaverse /// Callback giving results when fetching AgentProfile /// /// If the request was successful - /// AgentProfile result + /// AgentProfile result public delegate void AgentProfileCallback(bool success, AgentProfileMessage profile); #endregion Delegates - private GridClient Client; + private readonly GridClient Client; /// /// Represents other avatars @@ -1036,10 +1036,13 @@ namespace OpenMetaverse signaledAnimations.Add(animation); } - Avatar avatar = e.Simulator.ObjectsAvatars.Find(avi => avi.ID == data.Sender.ID); - if (avatar != null) + var kvp = e.Simulator.ObjectsAvatars.SingleOrDefault( + avi => avi.Value.ID == data.Sender.ID); + if (kvp.Value != null) { - avatar.Animations = signaledAnimations; + var av = kvp.Value; + av.Animations = signaledAnimations; + e.Simulator.ObjectsAvatars.TryUpdate(kvp.Key, av, kvp.Value); } else { @@ -1092,9 +1095,11 @@ namespace OpenMetaverse { if (appearance.AttachmentBlock != null && appearance.AttachmentBlock.Length > 0) { - Avatar av = simulator.ObjectsAvatars.Find((Avatar a) => a.ID == appearance.Sender.ID); - if (av != null) + var kv = simulator.ObjectsAvatars.SingleOrDefault( + a => a.Value.ID == appearance.Sender.ID); + if (kv.Value != null) { + var av = kv.Value; av.Attachments = new List(); foreach (var block in appearance.AttachmentBlock) { @@ -1106,37 +1111,42 @@ namespace OpenMetaverse } childCount = av.ChildCount = av.Attachments.Count; + + simulator.ObjectsAvatars.TryUpdate(kv.Key, av, kv.Value); } } } - if (appearance.Sender.ID != Client.Self.AgentID) // We need to ignore this for avatar self-appearance. - // The data in this packet is incorrect, and only the - // mesh bake CAP response can be treated as fully reliable. + // We need to ignore this for avatar self-appearance. + // The data in this packet is incorrect, and only the + // mesh bake CAP response can be treated as fully reliable. + if (appearance.Sender.ID == Client.Self.AgentID) { return; } + + var kvp = simulator.ObjectsAvatars.SingleOrDefault( + a => a.Value.ID == appearance.Sender.ID); + if (kvp.Value != null) { - Avatar av = simulator.ObjectsAvatars.Find((Avatar a) => a.ID == appearance.Sender.ID); - if (av != null) - { - av.Textures = textureEntry; - av.VisualParameters = visualParams.ToArray(); - av.AppearanceVersion = appearanceVersion; - av.COFVersion = COFVersion; - av.AppearanceFlags = appearanceFlags; - av.HoverHeight = hoverHeight; + var av = kvp.Value; + av.Textures = textureEntry; + av.VisualParameters = visualParams.ToArray(); + av.AppearanceVersion = appearanceVersion; + av.COFVersion = COFVersion; + av.AppearanceFlags = appearanceFlags; + av.HoverHeight = hoverHeight; - } - - OnAvatarAppearance(new AvatarAppearanceEventArgs(simulator, - appearance.Sender.ID, - appearance.Sender.IsTrial, - defaultTexture, - faceTextures, - visualParams, - appearanceVersion, - COFVersion, - appearanceFlags, - childCount)); + simulator.ObjectsAvatars.TryUpdate(kvp.Key, av, kvp.Value); } + + OnAvatarAppearance(new AvatarAppearanceEventArgs(simulator, + appearance.Sender.ID, + appearance.Sender.IsTrial, + defaultTexture, + faceTextures, + visualParams, + appearanceVersion, + COFVersion, + appearanceFlags, + childCount)); } } diff --git a/LibreMetaverse/ObjectManager.cs b/LibreMetaverse/ObjectManager.cs index 77dd92fa..b206fdfc 100644 --- a/LibreMetaverse/ObjectManager.cs +++ b/LibreMetaverse/ObjectManager.cs @@ -2015,11 +2015,18 @@ namespace OpenMetaverse { if (Client.Settings.OBJECT_TRACKING) { - Primitive prim = sim.ObjectsPrimitives.Find(p => p.ID == primID); - if (prim != null) + var kvp = sim.ObjectsPrimitives.SingleOrDefault( + p => p.Value.ID == primID); + if (kvp.Value != null) { - prim.MediaVersion = response.Version; - prim.FaceMedia = response.FaceMedia; + Primitive prim = kvp.Value; + if (prim != null) + { + prim.MediaVersion = response.Version; + prim.FaceMedia = response.FaceMedia; + } + + sim.ObjectsPrimitives.TryUpdate(kvp.Key, prim, kvp.Value); } } @@ -2481,9 +2488,7 @@ namespace OpenMetaverse case PCode.NewTree: case PCode.Prim: - bool isNewObject; - lock (simulator.ObjectsPrimitives.Dictionary) - isNewObject = !simulator.ObjectsPrimitives.ContainsKey(block.ID); + bool isNewObject = !simulator.ObjectsPrimitives.ContainsKey(block.ID); Primitive prim = GetPrimitive(simulator, block.ID, block.FullID); @@ -2587,9 +2592,7 @@ namespace OpenMetaverse #region Avatar case PCode.Avatar: - bool isNewAvatar; - lock (simulator.ObjectsAvatars.Dictionary) - isNewAvatar = !simulator.ObjectsAvatars.ContainsKey(block.ID); + bool isNewAvatar = !simulator.ObjectsAvatars.ContainsKey(block.ID); // Update some internals if this is our avatar if (block.FullID == Client.Self.AgentID && simulator == Client.Network.CurrentSim) @@ -2878,9 +2881,7 @@ namespace OpenMetaverse #endregion Relevance check - bool isNew; - lock (simulator.ObjectsPrimitives.Dictionary) - isNew = !simulator.ObjectsPrimitives.ContainsKey(LocalID); + bool isNew = !simulator.ObjectsPrimitives.ContainsKey(LocalID); Primitive prim = GetPrimitive(simulator, LocalID, FullID); @@ -3113,10 +3114,11 @@ namespace OpenMetaverse foreach (var odb in update.ObjectData) { uint localID = odb.ID; + uint crc = odb.CRC; if (cachedPrimitives) { - if (!simulator.DataPool.NeedsRequest(localID, odb.CRC)) + if (!simulator.DataPool.NeedsRequest(localID, crc)) { continue; } @@ -3147,71 +3149,75 @@ namespace OpenMetaverse } OnKillObjects(new KillObjectsEventArgs(e.Simulator, killed)); + List removeAvatars = new List(); + List removePrims = new List(); - lock (simulator.ObjectsPrimitives.Dictionary) + if (Client.Settings.OBJECT_TRACKING) { - List removeAvatars = new List(); - List removePrims = new List(); - - if (Client.Settings.OBJECT_TRACKING) + foreach (var odb in kill.ObjectData) { - uint localID; - foreach (var odb in kill.ObjectData) + var localID = odb.ID; + + if (simulator.ObjectsPrimitives.ContainsKey(localID)) { - localID = odb.ID; + removePrims.Add(localID); + } - if (simulator.ObjectsPrimitives.Dictionary.ContainsKey(localID)) - removePrims.Add(localID); - - foreach (var prim in simulator.ObjectsPrimitives.Dictionary.Where(prim => prim.Value.ParentID == localID)) + foreach (var prim in simulator.ObjectsPrimitives) + { + if (prim.Value.ParentID == localID) { OnKillObject(new KillObjectEventArgs(simulator, prim.Key)); removePrims.Add(prim.Key); } } } + } - if (Client.Settings.AVATAR_TRACKING) + if (Client.Settings.AVATAR_TRACKING) + { + uint localID; + foreach (var odb in kill.ObjectData) { - lock (simulator.ObjectsAvatars.Dictionary) + localID = odb.ID; + + if (simulator.ObjectsAvatars.ContainsKey(localID)) { - uint localID; - foreach (var odb in kill.ObjectData) - { - localID = odb.ID; + removeAvatars.Add(localID); + } - if (simulator.ObjectsAvatars.Dictionary.ContainsKey(localID)) - removeAvatars.Add(localID); + List rootPrims = new List(); - List rootPrims = new List(); + foreach (var prim in simulator.ObjectsPrimitives + .Where(prim => prim.Value.ParentID == localID)) + { + OnKillObject(new KillObjectEventArgs(simulator, prim.Key)); + removePrims.Add(prim.Key); + rootPrims.Add(prim.Key); + } - foreach (var prim in simulator.ObjectsPrimitives.Dictionary.Where(prim => prim.Value.ParentID == localID)) - { - OnKillObject(new KillObjectEventArgs(simulator, prim.Key)); - removePrims.Add(prim.Key); - rootPrims.Add(prim.Key); - } - - foreach (var prim in simulator.ObjectsPrimitives.Dictionary.Where(prim => rootPrims.Contains(prim.Value.ParentID))) - { - OnKillObject(new KillObjectEventArgs(simulator, prim.Key)); - removePrims.Add(prim.Key); - } - } - - //Do the actual removing outside the loops but still inside the lock. - //This safely prevents the collection from being modified during a loop. - foreach (uint removeID in removeAvatars) - simulator.ObjectsAvatars.Dictionary.Remove(removeID); + foreach (var prim in simulator.ObjectsPrimitives + .Where(prim => rootPrims.Contains(prim.Value.ParentID))) + { + OnKillObject(new KillObjectEventArgs(simulator, prim.Key)); + removePrims.Add(prim.Key); } } - if (Client.Settings.CACHE_PRIMITIVES) + foreach (uint removeID in removeAvatars) { - simulator.DataPool.ReleasePrims(removePrims); + simulator.ObjectsAvatars.TryRemove(removeID, out _); } - foreach (uint removeID in removePrims) - simulator.ObjectsPrimitives.Dictionary.Remove(removeID); + } + + if (Client.Settings.CACHE_PRIMITIVES) + { + simulator.DataPool.ReleasePrims(removePrims); + } + + foreach (uint removeID in removePrims) + { + simulator.ObjectsPrimitives.TryRemove(removeID, out _); } } @@ -3262,17 +3268,19 @@ namespace OpenMetaverse if (Client.Settings.OBJECT_TRACKING) { - Primitive findPrim = simulator.ObjectsPrimitives.Find( - prim => prim.ID == props.ObjectID); - - if (findPrim != null) + if (simulator.UUIDToLocalID.TryGetValue(props.ObjectID, out var localID)) { - OnObjectPropertiesUpdated(new ObjectPropertiesUpdatedEventArgs(simulator, findPrim, props)); - - lock (simulator.ObjectsPrimitives.Dictionary) + if (simulator.ObjectsPrimitives.TryGetValue(localID, out var findPrim)) { - if (simulator.ObjectsPrimitives.Dictionary.ContainsKey(findPrim.LocalID)) - simulator.ObjectsPrimitives.Dictionary[findPrim.LocalID].Properties = props; + if (findPrim != null) + { + OnObjectPropertiesUpdated(new ObjectPropertiesUpdatedEventArgs(simulator, findPrim, props)); + + if (simulator.ObjectsPrimitives.TryGetValue(findPrim.LocalID, out var primitive)) + { + primitive.Properties = props; + } + } } } } @@ -3312,18 +3320,21 @@ namespace OpenMetaverse if (Client.Settings.OBJECT_TRACKING) { - Primitive findPrim = simulator.ObjectsPrimitives.Find( - prim => prim.ID == op.ObjectData.ObjectID); - - if (findPrim != null) + if (simulator.UUIDToLocalID.TryGetValue(props.ObjectID, out var localID)) { - lock (simulator.ObjectsPrimitives.Dictionary) + if (simulator.ObjectsPrimitives.TryGetValue(localID, out var findPrim)) { - if (simulator.ObjectsPrimitives.Dictionary.ContainsKey(findPrim.LocalID)) + if (findPrim != null) { - if (simulator.ObjectsPrimitives.Dictionary[findPrim.LocalID].Properties == null) - simulator.ObjectsPrimitives.Dictionary[findPrim.LocalID].Properties = new Primitive.ObjectProperties(); - simulator.ObjectsPrimitives.Dictionary[findPrim.LocalID].Properties.SetFamilyProperties(props); + if (simulator.ObjectsPrimitives.TryGetValue(findPrim.LocalID, out var prim)) + { + if (prim.Properties == null) + { + prim.Properties = new Primitive.ObjectProperties(); + } + + prim.Properties.SetFamilyProperties(props); + } } } } @@ -3370,12 +3381,9 @@ namespace OpenMetaverse { foreach (var prop in msg.ObjectPhysicsProperties) { - lock (simulator.ObjectsPrimitives.Dictionary) + if (simulator.ObjectsPrimitives.TryGetValue(prop.LocalID, out var primitive)) { - if (simulator.ObjectsPrimitives.Dictionary.TryGetValue(prop.LocalID, out var primitive)) - { - primitive.PhysicsProps = prop; - } + primitive.PhysicsProps = prop; } } } @@ -3620,40 +3628,37 @@ namespace OpenMetaverse { if (Client.Settings.OBJECT_TRACKING) { - lock (simulator.ObjectsPrimitives.Dictionary) + if (simulator.ObjectsPrimitives.TryGetValue(localID, out var prim)) { - if (simulator.ObjectsPrimitives.Dictionary.TryGetValue(localID, out var prim)) - { - return prim; - } - else - { - if (!createIfMissing) { return null; } - if (Client.Settings.CACHE_PRIMITIVES) - { - prim = simulator.DataPool.MakePrimitive(localID); - } - else - { - prim = new Primitive - { - LocalID = localID, - RegionHandle = simulator.Handle - }; - } - prim.ActiveClients++; - prim.ID = fullID; - - simulator.ObjectsPrimitives.Dictionary[localID] = prim; - - return prim; - } + return prim; } + + if (!createIfMissing) {return null;} + + if (Client.Settings.CACHE_PRIMITIVES) + { + prim = simulator.DataPool.MakePrimitive(localID); + } + else + { + prim = new Primitive + { + LocalID = localID, + RegionHandle = simulator.Handle + }; + } + + prim.ActiveClients++; + prim.ID = fullID; + + prim = simulator.ObjectsPrimitives.GetOrAdd(localID, prim); + + simulator.UUIDToLocalID.AddOrUpdate(prim.ID, prim.LocalID, (uuid, u) => prim.LocalID); + + return prim; } - else - { - return new Primitive(); - } + + return new Primitive(); } /// @@ -3665,31 +3670,27 @@ namespace OpenMetaverse /// protected Avatar GetAvatar(Simulator simulator, uint localID, UUID fullID) { - if (Client.Settings.AVATAR_TRACKING) - { - lock (simulator.ObjectsAvatars.Dictionary) - { - if (simulator.ObjectsAvatars.Dictionary.TryGetValue(localID, out var avatar)) - { - return avatar; - } - - avatar = new Avatar - { - LocalID = localID, - ID = fullID, - RegionHandle = simulator.Handle - }; - - simulator.ObjectsAvatars.Dictionary[localID] = avatar; - - return avatar; - } - } - else + if (!Client.Settings.AVATAR_TRACKING) { return new Avatar(); } + + if (simulator.ObjectsAvatars.TryGetValue(localID, out var avatar)) + { + return avatar; + } + + avatar = new Avatar + { + LocalID = localID, + ID = fullID, + RegionHandle = simulator.Handle + }; + + simulator.ObjectsAvatars[localID] = avatar; + + return avatar; + } #endregion Object Tracking Link @@ -3712,68 +3713,62 @@ namespace OpenMetaverse float adjSeconds = seconds * sim.Stats.Dilation; // Iterate through all of this region's avatars - sim.ObjectsAvatars.ForEach( - delegate(Avatar avatar) + foreach (var avatar in sim.ObjectsAvatars) + { + #region Linear Motion + if (avatar.Value.Acceleration != Vector3.Zero) { - #region Linear Motion + avatar.Value.Velocity += avatar.Value.Acceleration * adjSeconds; + } + + if (avatar.Value.Velocity != Vector3.Zero) + { + avatar.Value.Position += (avatar.Value.Velocity) * adjSeconds; + } + #endregion Linear Motion + } + + // Iterate through all the simulator's primitives + + foreach (var prim in sim.ObjectsPrimitives) + { + if (prim.Value.Joint == JointType.Invalid) + { + Vector3 angVel = prim.Value.AngularVelocity; + float omega = angVel.LengthSquared(); + + if (omega > 0.00001f) + { + omega = (float)Math.Sqrt(omega); + float angle = omega * adjSeconds; + angVel *= 1.0f / omega; + Quaternion dQ = Quaternion.CreateFromAxisAngle(angVel, angle); + + prim.Value.Rotation *= dQ; + } + // Only do movement interpolation (extrapolation) when there is a non-zero velocity but // no acceleration - if (avatar.Acceleration != Vector3.Zero && avatar.Velocity == Vector3.Zero) + if (prim.Value.Acceleration != Vector3.Zero && prim.Value.Velocity == Vector3.Zero) { - avatar.Position += (avatar.Velocity + avatar.Acceleration * - (0.5f * (adjSeconds - HAVOK_TIMESTEP))) * adjSeconds; - avatar.Velocity += avatar.Acceleration * adjSeconds; + prim.Value.Position += (prim.Value.Velocity + prim.Value.Acceleration * + (0.5f * (adjSeconds - HAVOK_TIMESTEP))) * adjSeconds; + prim.Value.Velocity += prim.Value.Acceleration * adjSeconds; } - #endregion Linear Motion } - ); - - // Iterate through all of this region's primitives - sim.ObjectsPrimitives.ForEach( - delegate(Primitive prim) + else if (prim.Value.Joint == JointType.Hinge) { - if (prim.Joint == JointType.Invalid) - { - #region Angular Velocity - Vector3 angVel = prim.AngularVelocity; - float omega = angVel.LengthSquared(); - - if (omega > 0.00001f) - { - omega = (float)Math.Sqrt(omega); - float angle = omega * adjSeconds; - angVel *= 1.0f / omega; - Quaternion dQ = Quaternion.CreateFromAxisAngle(angVel, angle); - - prim.Rotation *= dQ; - } - #endregion Angular Velocity - - #region Linear Motion - // Only do movement interpolation (extrapolation) when there is a non-zero velocity but - // no acceleration - if (prim.Acceleration != Vector3.Zero && prim.Velocity == Vector3.Zero) - { - prim.Position += (prim.Velocity + prim.Acceleration * - (0.5f * (adjSeconds - HAVOK_TIMESTEP))) * adjSeconds; - prim.Velocity += prim.Acceleration * adjSeconds; - } - #endregion Linear Motion - } - else if (prim.Joint == JointType.Hinge) - { - //FIXME: Hinge movement extrapolation - } - else if (prim.Joint == JointType.Point) - { - //FIXME: Point movement extrapolation - } - else - { - Logger.Log("Unhandled joint type " + prim.Joint, Helpers.LogLevel.Warning, Client); - } + //FIXME: Hinge movement extrapolation } - ); + else if (prim.Value.Joint == JointType.Point) + { + //FIXME: Point movement extrapolation + } + else + { + Logger.Log($"Unhandled joint type {prim.Value.Joint}", Helpers.LogLevel.Warning, Client); + } + } } // Make sure the last interpolated time is always updated @@ -4026,7 +4021,7 @@ namespace OpenMetaverse public Primitive Prim { get; } /// - /// Construct a new instance of the ObjectPropertiesUpdatedEvenrArgs class + /// Construct a new instance of the ObjectPropertiesUpdatedEventArgs class /// /// The simulator the object is located /// The Primitive @@ -4063,7 +4058,8 @@ namespace OpenMetaverse } } - /// Provides primitive data containing updated location, velocity, rotation, textures for the event + /// Provides primitive data containing updated location, velocity, rotation, textures for the + /// event /// The event occurs when the simulator sends updated location, velocity, rotation, etc /// public class TerseObjectUpdateEventArgs : EventArgs diff --git a/LibreMetaverse/Simulator.cs b/LibreMetaverse/Simulator.cs index 5514dfad..09603d9a 100644 --- a/LibreMetaverse/Simulator.cs +++ b/LibreMetaverse/Simulator.cs @@ -1,6 +1,6 @@ /* * Copyright (c) 2006-2016, openmetaverse.co - * Copyright (c) 2022, Sjofn LLC. + * Copyright (c) 2022-2025, Sjofn LLC. * All rights reserved. * * - Redistribution and use in source and binary forms, with or without @@ -186,7 +186,7 @@ namespace OpenMetaverse /// /// Outgoing bytes per second /// - /// It would be nice to have this claculated on the fly, but + /// It would be nice to have this calculated on the fly, but /// this is far, far easier public int OutgoingBPS; /// Time last ping was sent @@ -271,7 +271,7 @@ namespace OpenMetaverse public uint SizeY; /// The current version of software this simulator is running public string SimVersion = string.Empty; - /// Human readable name given to the simulator + /// Human-readable name given to the simulator public string Name = string.Empty; /// A 64x64 grid of parcel coloring values. The values stored /// in this array are of the type @@ -383,18 +383,19 @@ namespace OpenMetaverse /// /// A thread-safe dictionary containing avatars in a simulator /// - public LockingDictionary ObjectsAvatars = new LockingDictionary(); + public ConcurrentDictionary ObjectsAvatars = new ConcurrentDictionary(); /// /// A thread-safe dictionary containing primitives in a simulator /// - public LockingDictionary ObjectsPrimitives = new LockingDictionary(); + public ConcurrentDictionary ObjectsPrimitives = new ConcurrentDictionary(); /// /// A thread-safe dictionary which can be used to find the local ID of a specified UUID. /// public ConcurrentDictionary UUIDToLocalID = new ConcurrentDictionary(); + public readonly TerrainPatch[] Terrain; public readonly Vector2[] WindSpeeds; @@ -463,16 +464,20 @@ namespace OpenMetaverse #region Properties /// The IP address and port of the server - public IPEndPoint IPEndPoint { get { return remoteEndPoint; } } + public IPEndPoint IPEndPoint => remoteEndPoint; + /// Whether there is a working connection to the simulator or /// not - public bool Connected { get { return connected; } } + public bool Connected => connected; + /// Coarse locations of avatars in this simulator - public LockingDictionary AvatarPositions { get { return avatarPositions; } } + public LockingDictionary AvatarPositions => avatarPositions; + /// AvatarPositions key representing TrackAgent target - public UUID PreyID { get { return preyID; } } + public UUID PreyID => preyID; + /// Indicates if UDP connection to the sim is fully established - public bool HandshakeComplete { get { return handshakeComplete; } } + public bool HandshakeComplete => handshakeComplete; #endregion Properties @@ -499,10 +504,12 @@ namespace OpenMetaverse /// Indicates if UDP connection to the sim is fully established internal bool handshakeComplete; - private NetworkManager Network; - private Queue InBytes, OutBytes; + private readonly NetworkManager Network; + private readonly Queue InBytes; + private readonly Queue OutBytes; + // ACKs that are queued up to be sent to the simulator - private ConcurrentQueue PendingAcks = new ConcurrentQueue(); + private readonly ConcurrentQueue PendingAcks = new ConcurrentQueue(); private Timer AckTimer; private Timer PingTimer; private Timer StatsTimer; @@ -552,7 +559,7 @@ namespace OpenMetaverse internal bool _DownloadingParcelMap = false; - private ManualResetEvent GotUseCircuitCodeAck = new ManualResetEvent(false); + private readonly ManualResetEvent GotUseCircuitCodeAck = new ManualResetEvent(false); #endregion Internal/Private Members /// @@ -1384,11 +1391,11 @@ namespace OpenMetaverse public sealed class IncomingPacketIDCollection { - readonly uint[] _items; - HashSet hashSet; - int first; - int next; - int capacity; + private readonly uint[] _items; + private readonly HashSet hashSet; + private int first; + private int next; + private readonly int capacity; public IncomingPacketIDCollection(int capacity) { @@ -1519,8 +1526,7 @@ namespace OpenMetaverse var dict = PrimCache; lock (dict) { - Primitive prim; - if (!dict.TryGetValue(localID, out prim) || prim.IsAttachment) + if (!dict.TryGetValue(localID, out var prim) || prim.IsAttachment) { dict[localID] = prim = new Primitive { RegionHandle = Handle, LocalID = localID }; } @@ -1544,8 +1550,7 @@ namespace OpenMetaverse { foreach (var u in removePrims) { - Primitive prim; - if (PrimCache.TryGetValue(u, out prim)) prim.ActiveClients--; + if (PrimCache.TryGetValue(u, out var prim)) prim.ActiveClients--; } } } diff --git a/Programs/examples/TestClient/Commands/Agent/BotsCommand.cs b/Programs/examples/TestClient/Commands/Agent/BotsCommand.cs index e61f8f9b..4390593f 100644 --- a/Programs/examples/TestClient/Commands/Agent/BotsCommand.cs +++ b/Programs/examples/TestClient/Commands/Agent/BotsCommand.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace OpenMetaverse.TestClient @@ -48,22 +49,14 @@ namespace OpenMetaverse.TestClient lock (Client.Network.Simulators) { - foreach (var sim in Client.Network.Simulators) + foreach (var av in from sim in Client.Network.Simulators + from kvp in sim.ObjectsAvatars + where kvp.Value != null select kvp.Value into av + where !m_AgentList.ContainsKey(av.ID) select av) { - sim.ObjectsAvatars.ForEach( - delegate(Avatar av) - { - lock (m_AgentList) - { - if (!m_AgentList.ContainsKey(av.ID)) - { - result.AppendLine(); - result.AppendFormat("{0} (Group: {1}, Location: {2}, UUID: {3} LocalID: {4}) Is Probably a bot", - av.Name, av.GroupName, av.Position, av.ID, av.LocalID); - } - } - } - ); + result.AppendLine(); + result.AppendFormat("{0} (Group: {1}, Location: {2}, UUID: {3} LocalID: {4}) Is Probably a bot", + av.Name, av.GroupName, av.Position, av.ID, av.LocalID); } } diff --git a/Programs/examples/TestClient/Commands/Agent/TouchCommand.cs b/Programs/examples/TestClient/Commands/Agent/TouchCommand.cs index 3497d18c..70edcffa 100644 --- a/Programs/examples/TestClient/Commands/Agent/TouchCommand.cs +++ b/Programs/examples/TestClient/Commands/Agent/TouchCommand.cs @@ -1,3 +1,5 @@ +using System.Linq; + namespace OpenMetaverse.TestClient { public class TouchCommand: Command @@ -11,25 +13,25 @@ namespace OpenMetaverse.TestClient public override string Execute(string[] args, UUID fromAgentID) { - UUID target; - if (args.Length != 1) - return "Usage: touch UUID"; - - if (UUID.TryParse(args[0], out target)) { - Primitive targetPrim = Client.Network.CurrentSim.ObjectsPrimitives.Find( - prim => prim.ID == target - ); - - if (targetPrim != null) - { - Client.Self.Touch(targetPrim.LocalID); - return "Touched prim " + targetPrim.LocalID; - } + return "Usage: touch UUID"; } - return "Couldn't find a prim to touch with UUID " + args[0]; - } + if (!UUID.TryParse(args[0], out var target)) + { + return $"{args[0]} is not a valid UUID"; + } + var targetPrim = Client.Network.CurrentSim.ObjectsPrimitives.FirstOrDefault(prim => prim.Value.ID == target); + + if (targetPrim.Value == null) + { + return $"Couldn't find an object to touch with UUID {args[0]}"; + } + + Client.Self.Touch(targetPrim.Value.LocalID); + return $"Touched object {targetPrim.Value.LocalID}"; + + } } } diff --git a/Programs/examples/TestClient/Commands/Agent/WhoCommand.cs b/Programs/examples/TestClient/Commands/Agent/WhoCommand.cs index 2be3e1e1..736892b9 100644 --- a/Programs/examples/TestClient/Commands/Agent/WhoCommand.cs +++ b/Programs/examples/TestClient/Commands/Agent/WhoCommand.cs @@ -1,3 +1,4 @@ +using System.Linq; using System.Text; namespace OpenMetaverse.TestClient @@ -17,18 +18,12 @@ namespace OpenMetaverse.TestClient lock (Client.Network.Simulators) { - foreach (var sim - in Client.Network.Simulators) + foreach (var av in from sim in Client.Network.Simulators + from kvp in sim.ObjectsAvatars where kvp.Value != null select kvp.Value) { - sim -.ObjectsAvatars.ForEach( - delegate(Avatar av) - { - result.AppendLine(); - result.AppendFormat("{0} (Group: {1}, Location: {2}, UUID: {3} LocalID: {4})", - av.Name, av.GroupName, av.Position, av.ID, av.LocalID); - } - ); + result.AppendLine(); + result.AppendFormat("{0} (Group: {1}, Location: {2}, UUID: {3} LocalID: {4})", + av.Name, av.GroupName, av.Position, av.ID, av.LocalID); } } diff --git a/Programs/examples/TestClient/Commands/Appearance/AttachmentsCommand.cs b/Programs/examples/TestClient/Commands/Appearance/AttachmentsCommand.cs index b87a4a70..5cacde39 100644 --- a/Programs/examples/TestClient/Commands/Appearance/AttachmentsCommand.cs +++ b/Programs/examples/TestClient/Commands/Appearance/AttachmentsCommand.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Linq; namespace OpenMetaverse.TestClient { @@ -15,20 +14,19 @@ namespace OpenMetaverse.TestClient public override string Execute(string[] args, UUID fromAgentID) { - List attachments = Client.Network.CurrentSim.ObjectsPrimitives.FindAll( - prim => prim.ParentID == Client.Self.LocalID - ); + var attachments = (from kvp in Client.Network.CurrentSim.ObjectsPrimitives + where kvp.Value != null where kvp.Value.ParentID == Client.Self.LocalID select kvp.Value).ToList(); foreach (var prim in attachments) { - AttachmentPoint point = StateToAttachmentPoint(prim.PrimData.State); + var point = StateToAttachmentPoint(prim.PrimData.State); - // TODO: Fetch properties for the objects with missing property sets so we can show names + // TODO: Fetch properties for the objects with missing property sets, so we can show names Logger.Log($"[Attachment @ {point}] LocalID: {prim.LocalID} UUID: {prim.ID} Offset: {prim.Position}", Helpers.LogLevel.Info, Client); } - return "Found " + attachments.Count + " attachments"; + return $"Found {attachments.Count} attachments"; } public static AttachmentPoint StateToAttachmentPoint(uint state) diff --git a/Programs/examples/TestClient/Commands/Appearance/AvatarInfoCommand.cs b/Programs/examples/TestClient/Commands/Appearance/AvatarInfoCommand.cs index 2dbf1852..85091533 100644 --- a/Programs/examples/TestClient/Commands/Appearance/AvatarInfoCommand.cs +++ b/Programs/examples/TestClient/Commands/Appearance/AvatarInfoCommand.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Text; namespace OpenMetaverse.TestClient.Commands.Appearance @@ -19,12 +20,12 @@ namespace OpenMetaverse.TestClient.Commands.Appearance string targetName = $"{args[0]} {args[1]}"; - Avatar foundAv = Client.Network.CurrentSim.ObjectsAvatars.Find( - avatar => (avatar.Name == targetName) - ); + var kvp = Client.Network.CurrentSim.ObjectsAvatars.SingleOrDefault( + avatar => (avatar.Value.Name == targetName)); - if (foundAv != null) + if (kvp.Value != null) { + var foundAv = kvp.Value; StringBuilder output = new StringBuilder(); output.AppendFormat("{0} ({1})", targetName, foundAv.ID); @@ -46,7 +47,7 @@ namespace OpenMetaverse.TestClient.Commands.Appearance } else { - return "No nearby avatar with the name " + targetName; + return $"No nearby avatar named {targetName}"; } } } diff --git a/Programs/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs b/Programs/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs index 7da5b64a..ed472f08 100644 --- a/Programs/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs @@ -29,6 +29,7 @@ using System; using System.Text; using System.IO; using System.Collections.Generic; +using System.Linq; using CoreJ2K; using OpenMetaverse.Assets; using OpenMetaverse.Imaging; @@ -64,12 +65,12 @@ namespace OpenMetaverse.TestClient { foreach (var sim in Client.Network.Simulators) { - Avatar targetAv = sim.ObjectsAvatars.Find( - avatar => avatar.ID == target - ); + var kvp = sim.ObjectsAvatars.FirstOrDefault( + avatar => avatar.Value.ID == target); - if (targetAv != null) + if (kvp.Value != null) { + var targetAv = kvp.Value; StringBuilder output = new StringBuilder("Downloading "); lock (OutfitAssets) OutfitAssets.Clear(); diff --git a/Programs/examples/TestClient/Commands/Inventory/ObjectInventoryCommand.cs b/Programs/examples/TestClient/Commands/Inventory/ObjectInventoryCommand.cs index eaad0269..50346b48 100644 --- a/Programs/examples/TestClient/Commands/Inventory/ObjectInventoryCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/ObjectInventoryCommand.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using System.Linq; namespace OpenMetaverse.TestClient { @@ -15,44 +15,47 @@ namespace OpenMetaverse.TestClient public override string Execute(string[] args, UUID fromAgentID) { if (args.Length != 1) - return "Usage: objectinventory [objectID]"; - - uint objectLocalID; - UUID objectID; - if (!UUID.TryParse(args[0], out objectID)) - return "Usage: objectinventory [objectID]"; - - Primitive found = Client.Network.CurrentSim.ObjectsPrimitives.Find(prim => prim.ID == objectID); - if (found != null) - objectLocalID = found.LocalID; - else - return "Couldn't find prim " + objectID; - - List items = Client.Inventory.GetTaskInventory(objectID, objectLocalID, TimeSpan.FromSeconds(30)); - - if (items != null) { - string result = string.Empty; + return "Usage: objectinventory [objectID]"; + } - foreach (var i in items) + if (!UUID.TryParse(args[0], out var objectID)) + { + return "Usage: objectinventory [objectID]"; + } + + var found = Client.Network.CurrentSim.ObjectsPrimitives.FirstOrDefault(prim => prim.Value.ID == objectID); + if (found.Value == null) + { + return $"Could not find ${objectID} object"; + } + + var objectLocalID = found.Value.LocalID; + + var items = Client.Inventory.GetTaskInventory(objectID, objectLocalID, TimeSpan.FromSeconds(30)); + + if (items == null) + { + return $"Failed to download task inventory for {objectLocalID}"; + } + + var result = string.Empty; + + foreach (var i in items) + { + if (i is InventoryFolder) { - if (i is InventoryFolder) - { - result += $"[Folder] Name: {i.Name}" + Environment.NewLine; - } - else - { - InventoryItem item = (InventoryItem)i; - result += $"[Item] Name: {item.Name} Desc: {item.Description} Type: {item.AssetType}" + Environment.NewLine; - } + result += $"[Folder] Name: {i.Name}" + Environment.NewLine; } + else + { + InventoryItem item = (InventoryItem)i; + result += $"[Item] Name: {item.Name} Desc: {item.Description} Type: {item.AssetType}" + Environment.NewLine; + } + } + + return result; - return result; - } - else - { - return "Failed to download task inventory for " + objectLocalID; - } } } } diff --git a/Programs/examples/TestClient/Commands/Inventory/TaskRunningCommand.cs b/Programs/examples/TestClient/Commands/Inventory/TaskRunningCommand.cs index c2861563..e9725fd8 100644 --- a/Programs/examples/TestClient/Commands/Inventory/TaskRunningCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/TaskRunningCommand.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; namespace OpenMetaverse.TestClient @@ -16,19 +17,22 @@ namespace OpenMetaverse.TestClient public override string Execute(string[] args, UUID fromAgentID) { if (args.Length != 1) + { return "Usage: taskrunning objectID [[scriptName] true|false]"; + } - uint objectLocalID; - UUID objectID; - - if (!UUID.TryParse(args[0], out objectID)) + if (!UUID.TryParse(args[0], out var objectID)) + { return "Usage: taskrunning objectID [[scriptName] true|false]"; + } - Primitive found = Client.Network.CurrentSim.ObjectsPrimitives.Find(prim => prim.ID == objectID); - if (found != null) - objectLocalID = found.LocalID; - else - return $"Couldn't find prim {objectID}"; + var found = Client.Network.CurrentSim.ObjectsPrimitives.FirstOrDefault(prim => prim.Value.ID == objectID); + if (found.Value == null) + { + return $"Couldn't find object {objectID}"; + } + + var objectLocalID = found.Value.LocalID; List items = Client.Inventory.GetTaskInventory(objectID, objectLocalID, TimeSpan.FromSeconds(30)); diff --git a/Programs/examples/TestClient/Commands/Movement/FollowCommand.cs b/Programs/examples/TestClient/Commands/Movement/FollowCommand.cs index 730a0ba3..a46f8a22 100644 --- a/Programs/examples/TestClient/Commands/Movement/FollowCommand.cs +++ b/Programs/examples/TestClient/Commands/Movement/FollowCommand.cs @@ -43,18 +43,14 @@ namespace OpenMetaverse.TestClient { lock (Client.Network.Simulators) { - foreach (var sim in Client.Network.Simulators) + foreach (var target in Client.Network.Simulators + .Select(sim => sim.ObjectsAvatars + .FirstOrDefault(avatar => avatar.Value.Name == name)) + .Where(target => target.Value != null)) { - Avatar target = sim.ObjectsAvatars.Find( - avatar => avatar.Name == name - ); - - if (target != null) - { - targetLocalID = target.LocalID; - Active = true; - return true; - } + targetLocalID = target.Value.LocalID; + Active = true; + return true; } } diff --git a/Programs/examples/TestClient/Commands/Movement/SitCommand.cs b/Programs/examples/TestClient/Commands/Movement/SitCommand.cs index 41c35eec..06d969ed 100644 --- a/Programs/examples/TestClient/Commands/Movement/SitCommand.cs +++ b/Programs/examples/TestClient/Commands/Movement/SitCommand.cs @@ -16,30 +16,28 @@ namespace OpenMetaverse.TestClient Primitive closest = null; double closestDistance = double.MaxValue; - Client.Network.CurrentSim.ObjectsPrimitives.ForEach( - delegate(Primitive prim) - { - float distance = Vector3.Distance(Client.Self.SimPosition, prim.Position); - - if (closest == null || distance < closestDistance) - { - closest = prim; - closestDistance = distance; - } - } - ); - - if (closest != null) + foreach (var kvp in Client.Network.CurrentSim.ObjectsPrimitives) { - Client.Self.RequestSit(closest.ID, Vector3.Zero); - Client.Self.Sit(); + if (kvp.Value == null) { continue; } - return "Sat on " + closest.ID + " (" + closest.LocalID + "). Distance: " + closestDistance; + var prim = kvp.Value; + var distance = Vector3.Distance(Client.Self.SimPosition, prim.Position); + if (closest == null || distance < closestDistance) + { + closest = prim; + closestDistance = distance; + } } - else + + if (closest == null) { return "Couldn't find a nearby prim to sit on"; } - } + Client.Self.RequestSit(closest.ID, Vector3.Zero); + Client.Self.Sit(); + + return $"Sat on {closest.ID} ({closest.LocalID}). Distance: {closestDistance}"; + + } } } diff --git a/Programs/examples/TestClient/Commands/Movement/SitOnCommand.cs b/Programs/examples/TestClient/Commands/Movement/SitOnCommand.cs index e1c16735..8ccb3bb0 100644 --- a/Programs/examples/TestClient/Commands/Movement/SitOnCommand.cs +++ b/Programs/examples/TestClient/Commands/Movement/SitOnCommand.cs @@ -1,3 +1,5 @@ +using System.Linq; + namespace OpenMetaverse.TestClient { public class SitOnCommand : Command @@ -14,16 +16,13 @@ namespace OpenMetaverse.TestClient if (args.Length != 1) return "Usage: siton UUID"; - UUID target; - - if (UUID.TryParse(args[0], out target)) + if (UUID.TryParse(args[0], out var target)) { - Primitive targetPrim = Client.Network.CurrentSim.ObjectsPrimitives.Find( - prim => prim.ID == target - ); + var kvp = Client.Network.CurrentSim.ObjectsPrimitives.FirstOrDefault(prim => prim.Value.ID == target); - if (targetPrim != null) + if (kvp.Value != null) { + var targetPrim = kvp.Value; Client.Self.RequestSit(targetPrim.ID, Vector3.Zero); Client.Self.Sit(); return "Requested to sit on prim " + targetPrim.ID + diff --git a/Programs/examples/TestClient/Commands/Prims/ChangePermsCommand.cs b/Programs/examples/TestClient/Commands/Prims/ChangePermsCommand.cs index 0f22e0d9..398c567a 100644 --- a/Programs/examples/TestClient/Commands/Prims/ChangePermsCommand.cs +++ b/Programs/examples/TestClient/Commands/Prims/ChangePermsCommand.cs @@ -7,9 +7,9 @@ namespace OpenMetaverse.TestClient { public class ChangePermsCommand : Command { - AutoResetEvent GotPermissionsEvent = new AutoResetEvent(false); - Dictionary Objects = new Dictionary(); - PermissionMask Perms = PermissionMask.None; + private readonly AutoResetEvent GotPermissionsEvent = new AutoResetEvent(false); + private readonly Dictionary Objects = new Dictionary(); + private PermissionMask Perms = PermissionMask.None; private bool PermsSent; private int PermCount; @@ -24,7 +24,6 @@ namespace OpenMetaverse.TestClient public override string Execute(string[] args, UUID fromAgentID) { - UUID rootID; var localIDs = new List(); // Reset class-wide variables @@ -36,7 +35,7 @@ namespace OpenMetaverse.TestClient if (args.Length < 1 || args.Length > 4) return "Usage prim-uuid [copy] [mod] [xfer]"; - if (!UUID.TryParse(args[0], out rootID)) + if (!UUID.TryParse(args[0], out var rootID)) return "Usage prim-uuid [copy] [mod] [xfer]"; for (int i = 1; i < args.Length; i++) @@ -60,13 +59,15 @@ namespace OpenMetaverse.TestClient Logger.DebugLog($"Using PermissionMask: {Perms}", Client); // Find the requested prim - var rootPrim = Client.Network.CurrentSim.ObjectsPrimitives.Find(prim => prim.ID == rootID); - if (rootPrim == null) + var reqkvp = Client.Network.CurrentSim.ObjectsPrimitives + .FirstOrDefault(prim => prim.Value.ID == rootID); + if (reqkvp.Value == null) { - return $"Cannot find requested prim {rootID}"; + return $"Cannot find requested object {rootID}"; } - Logger.DebugLog($"Found requested prim {rootPrim.ID}", Client); + var rootPrim = reqkvp.Value; + Logger.DebugLog($"Found requested object {rootPrim.ID}", Client); if (rootPrim.ParentID != 0) { @@ -79,8 +80,10 @@ namespace OpenMetaverse.TestClient Logger.DebugLog($"Set root prim to {rootPrim.ID}", Client); } - // Find, find all the child objects linked to this root - var childPrims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll(prim => prim.ParentID == rootPrim.LocalID); + // Find all the child primitives linked to the root + var childPrims = (from kvp + in Client.Network.CurrentSim.ObjectsPrimitives where kvp.Value != null + select kvp.Value into child where child.ParentID == rootPrim.LocalID select child).ToList(); // Build a dictionary of primitives for referencing later Objects[rootPrim.ID] = rootPrim; @@ -140,7 +143,7 @@ namespace OpenMetaverse.TestClient return $"Set permissions to {Perms} on {localIDs.Count} objects and {taskItems} inventory items"; } - void Objects_OnObjectProperties(object sender, ObjectPropertiesEventArgs e) + private void Objects_OnObjectProperties(object sender, ObjectPropertiesEventArgs e) { if (!PermsSent) { return; } diff --git a/Programs/examples/TestClient/Commands/Prims/DeRezObjectCommand.cs b/Programs/examples/TestClient/Commands/Prims/DeRezObjectCommand.cs index c1ca8af5..1336cbdf 100644 --- a/Programs/examples/TestClient/Commands/Prims/DeRezObjectCommand.cs +++ b/Programs/examples/TestClient/Commands/Prims/DeRezObjectCommand.cs @@ -1,4 +1,6 @@ -namespace OpenMetaverse.TestClient +using System.Linq; + +namespace OpenMetaverse.TestClient { public class DeRezCommand : Command { @@ -11,34 +13,28 @@ public override string Execute(string[] args, UUID fromAgentID) { - UUID primID; - if (args.Length != 1) - return "Usage: derez [prim-uuid]"; - - if (UUID.TryParse(args[0], out primID)) - { - Primitive target = Client.Network.CurrentSim.ObjectsPrimitives.Find( - prim => prim.ID == primID - ); - - if (target != null) - { - uint objectLocalID = target.LocalID; - Client.Inventory.RequestDeRezToInventory(objectLocalID, DeRezDestination.AgentInventoryTake, - Client.Inventory.FindFolderForType(FolderType.Trash), - UUID.Random()); - return "removing " + target; - } - else - { - return "Could not find prim " + primID; - } - } - else { return "Usage: derez [prim-uuid]"; } + + if (!UUID.TryParse(args[0], out var primID)) + { + return $"{args[0]} is not a valid UUID"; + } + + var kvp = Client.Network.CurrentSim.ObjectsPrimitives.FirstOrDefault(prim => prim.Value.ID == primID); + if (kvp.Value == null) + { + return $"Could not find object {primID}"; + } + var target = kvp.Value; + var objectLocalID = target.LocalID; + Client.Inventory.RequestDeRezToInventory(objectLocalID, DeRezDestination.AgentInventoryTake, + Client.Inventory.FindFolderForType(FolderType.Trash), + UUID.Random()); + return $"Removing {target}"; + } } } diff --git a/Programs/examples/TestClient/Commands/Prims/ExportCommand.cs b/Programs/examples/TestClient/Commands/Prims/ExportCommand.cs index ab693f95..80eba860 100644 --- a/Programs/examples/TestClient/Commands/Prims/ExportCommand.cs +++ b/Programs/examples/TestClient/Commands/Prims/ExportCommand.cs @@ -10,14 +10,14 @@ namespace OpenMetaverse.TestClient { public class ExportCommand : Command { - List Textures = new List(); - AutoResetEvent GotPermissionsEvent = new AutoResetEvent(false); - Primitive.ObjectProperties Properties; - bool GotPermissions = false; - UUID SelectedObject = UUID.Zero; + private readonly List Textures = new List(); + private readonly AutoResetEvent GotPermissionsEvent = new AutoResetEvent(false); + private Primitive.ObjectProperties Properties; + private bool GotPermissions = false; + private UUID SelectedObject = UUID.Zero; - Dictionary PrimsWaiting = new Dictionary(); - AutoResetEvent AllPropertiesReceived = new AutoResetEvent(false); + private readonly Dictionary PrimsWaiting = new Dictionary(); + private readonly AutoResetEvent AllPropertiesReceived = new AutoResetEvent(false); public ExportCommand(TestClient testClient) { @@ -29,9 +29,9 @@ namespace OpenMetaverse.TestClient Name = "export"; Description = "Exports an object to an xml file. Usage: export uuid outputfile.xml"; Category = CommandCategory.Objects; - } + } - void Avatars_ViewerEffectPointAt(object sender, ViewerEffectPointAtEventArgs e) + private void Avatars_ViewerEffectPointAt(object sender, ViewerEffectPointAtEventArgs e) { if (e.SourceID == Client.MasterKey) { @@ -46,7 +46,6 @@ namespace OpenMetaverse.TestClient return "Usage: export uuid outputfile.xml"; UUID id; - uint localid; string file; if (args.Length == 2) @@ -61,105 +60,100 @@ namespace OpenMetaverse.TestClient id = SelectedObject; } - var exportPrim = Client.Network.CurrentSim.ObjectsPrimitives.Find( - prim => prim.ID == id - ); + var kvp = Client.Network.CurrentSim.ObjectsPrimitives.FirstOrDefault( + prim => prim.Value.ID == id); - if (exportPrim != null) + if (kvp.Value == null) { - localid = exportPrim.ParentID != 0 ? exportPrim.ParentID : exportPrim.LocalID; + return $"Couldn't find UUID {id} in the objects currently indexed in the current simulator"; + } - // Check for export permission first - Client.Objects.RequestObjectPropertiesFamily(Client.Network.CurrentSim, id); - GotPermissionsEvent.WaitOne(TimeSpan.FromSeconds(20), false); + var exportPrim = kvp.Value; + var localId = exportPrim.ParentID != 0 ? exportPrim.ParentID : exportPrim.LocalID; - if (!GotPermissions) + // Check for export permission first + Client.Objects.RequestObjectPropertiesFamily(Client.Network.CurrentSim, id); + GotPermissionsEvent.WaitOne(TimeSpan.FromSeconds(20), false); + + if (!GotPermissions) + { + return "Couldn't fetch permissions for the requested object, try again"; + } + + GotPermissions = false; + + if (Properties.OwnerID != Client.Self.AgentID && + Properties.OwnerID != Client.MasterKey) + { + return "That object is owned by " + Properties.OwnerID + ", we don't have permission " + + "to export it"; + } + + var prims = (from kvprim in Client.Network.CurrentSim.ObjectsPrimitives + where kvprim.Value != null select kvprim.Value into prim + where prim.LocalID == localId || prim.ParentID == localId select prim).ToList(); + + bool complete = RequestObjectProperties(prims, 250); + + if (!complete) + { + Logger.Log("Warning: Unable to retrieve full properties for:", Helpers.LogLevel.Warning, Client); + foreach (UUID uuid in PrimsWaiting.Keys) + Logger.Log(uuid.ToString(), Helpers.LogLevel.Warning, Client); + } + + string output = OSDParser.SerializeLLSDXmlString(Helpers.PrimListToOSD(prims)); + try { File.WriteAllText(file, output); } + catch (Exception e) { return e.Message; } + + Logger.Log("Exported " + prims.Count + " prims to " + file, Helpers.LogLevel.Info, Client); + + // Create a list of all the textures to download + List textureRequests = new List(); + + lock (Textures) + { + foreach (var prim in prims) { - return "Couldn't fetch permissions for the requested object, try again"; - } - else - { - GotPermissions = false; - if (Properties.OwnerID != Client.Self.AgentID && - Properties.OwnerID != Client.MasterKey) + if (prim.Textures.DefaultTexture.TextureID != Primitive.TextureEntry.WHITE_TEXTURE && + !Textures.Contains(prim.Textures.DefaultTexture.TextureID)) { - return "That object is owned by " + Properties.OwnerID + ", we don't have permission " + - "to export it"; + Textures.Add(prim.Textures.DefaultTexture.TextureID); } - } - List prims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll( - prim => (prim.LocalID == localid || prim.ParentID == localid) - ); - - bool complete = RequestObjectProperties(prims, 250); - - if (!complete) - { - Logger.Log("Warning: Unable to retrieve full properties for:", Helpers.LogLevel.Warning, Client); - foreach (UUID uuid in PrimsWaiting.Keys) - Logger.Log(uuid.ToString(), Helpers.LogLevel.Warning, Client); - } - - string output = OSDParser.SerializeLLSDXmlString(Helpers.PrimListToOSD(prims)); - try { File.WriteAllText(file, output); } - catch (Exception e) { return e.Message; } - - Logger.Log("Exported " + prims.Count + " prims to " + file, Helpers.LogLevel.Info, Client); - - // Create a list of all of the textures to download - List textureRequests = new List(); - - lock (Textures) - { - foreach (var prim in prims) + foreach (var face in prim.Textures.FaceTextures) { - if (prim.Textures.DefaultTexture.TextureID != Primitive.TextureEntry.WHITE_TEXTURE && - !Textures.Contains(prim.Textures.DefaultTexture.TextureID)) + if (face != null && + face.TextureID != Primitive.TextureEntry.WHITE_TEXTURE && + !Textures.Contains(face.TextureID)) { - Textures.Add(prim.Textures.DefaultTexture.TextureID); - } - - foreach (var face in prim.Textures.FaceTextures) - { - if (face != null && - face.TextureID != Primitive.TextureEntry.WHITE_TEXTURE && - !Textures.Contains(face.TextureID)) - { - Textures.Add(face.TextureID); - } - } - - if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero && !Textures.Contains(prim.Sculpt.SculptTexture)) - { - Textures.Add(prim.Sculpt.SculptTexture); + Textures.Add(face.TextureID); } } - // Create a request list from all of the images - textureRequests.AddRange(Textures.Select(t => new ImageRequest(t, ImageType.Normal, 1013000.0f, 0))); + if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero && !Textures.Contains(prim.Sculpt.SculptTexture)) + { + Textures.Add(prim.Sculpt.SculptTexture); + } } - // Download all of the textures in the export list - foreach (ImageRequest request in textureRequests) - { - Client.Assets.RequestImage(request.ImageID, request.Type, Assets_OnImageReceived); - } - - return "XML exported, began downloading " + Textures.Count + " textures"; + // Create a request list from all the images + textureRequests.AddRange(Textures.Select(t => new ImageRequest(t, ImageType.Normal, 1013000.0f, 0))); } - else + + // Download all the textures in the export list + foreach (var request in textureRequests) { - return "Couldn't find UUID " + id + " in the " + - Client.Network.CurrentSim.ObjectsPrimitives.Count + - "objects currently indexed in the current simulator"; + Client.Assets.RequestImage(request.ImageID, request.Type, Assets_OnImageReceived); } + + return $"XML exported, downloading {Textures.Count} textures"; } private bool RequestObjectProperties(List objects, int msPerRequest) { // Create an array of the local IDs of all the prims we are requesting properties for - uint[] localids = new uint[objects.Count]; + uint[] localIds = new uint[objects.Count]; lock (PrimsWaiting) { @@ -167,12 +161,12 @@ namespace OpenMetaverse.TestClient for (int i = 0; i < objects.Count; ++i) { - localids[i] = objects[i].LocalID; + localIds[i] = objects[i].LocalID; PrimsWaiting.Add(objects[i].ID, objects[i]); } } - Client.Objects.SelectObjects(Client.Network.CurrentSim, localids); + Client.Objects.SelectObjects(Client.Network.CurrentSim, localIds); return AllPropertiesReceived.WaitOne(2000 + msPerRequest * objects.Count, false); } @@ -202,7 +196,7 @@ namespace OpenMetaverse.TestClient } } - void Objects_OnObjectPropertiesFamily(object sender, ObjectPropertiesFamilyEventArgs e) + private void Objects_OnObjectPropertiesFamily(object sender, ObjectPropertiesFamilyEventArgs e) { Properties = new Primitive.ObjectProperties(); Properties.SetFamilyProperties(e.Properties); @@ -210,7 +204,7 @@ namespace OpenMetaverse.TestClient GotPermissionsEvent.Set(); } - void Objects_OnObjectProperties(object sender, ObjectPropertiesEventArgs e) + private void Objects_OnObjectProperties(object sender, ObjectPropertiesEventArgs e) { lock (PrimsWaiting) { diff --git a/Programs/examples/TestClient/Commands/Prims/ExportParticlesCommand.cs b/Programs/examples/TestClient/Commands/Prims/ExportParticlesCommand.cs index a339eb25..c48f3d34 100644 --- a/Programs/examples/TestClient/Commands/Prims/ExportParticlesCommand.cs +++ b/Programs/examples/TestClient/Commands/Prims/ExportParticlesCommand.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Text; namespace OpenMetaverse.TestClient @@ -15,121 +16,119 @@ namespace OpenMetaverse.TestClient public override string Execute(string[] args, UUID fromAgentID) { if (args.Length != 1) + { return "Usage: exportparticles [prim-uuid]"; + } - UUID id; - if (!UUID.TryParse(args[0], out id)) + if (!UUID.TryParse(args[0], out var id)) + { return "Usage: exportparticles [prim-uuid]"; + } lock (Client.Network.Simulators) { - foreach (var sim in Client.Network.Simulators) + foreach (var exportPrim in from sim in Client.Network.Simulators + select sim.ObjectsPrimitives.FirstOrDefault( + prim => prim.Value.ID == id) + into kvp where kvp.Value != null select kvp.Value) { - Primitive exportPrim = sim.ObjectsPrimitives.Find( - prim => prim.ID == id - ); - - if (exportPrim != null) + if (exportPrim.ParticleSys.CRC == 0) { - if (exportPrim.ParticleSys.CRC != 0) - { - StringBuilder lsl = new StringBuilder(); - - #region Particle System to LSL - - lsl.Append("default" + Environment.NewLine); - lsl.Append("{" + Environment.NewLine); - lsl.Append(" state_entry()" + Environment.NewLine); - lsl.Append(" {" + Environment.NewLine); - lsl.Append(" llParticleSystem([" + Environment.NewLine); - - lsl.Append(" PSYS_PART_FLAGS, 0"); - - if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.InterpColor) != 0) - lsl.Append(" | PSYS_PART_INTERP_COLOR_MASK"); - if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.InterpScale) != 0) - lsl.Append(" | PSYS_PART_INTERP_SCALE_MASK"); - if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Bounce) != 0) - lsl.Append(" | PSYS_PART_BOUNCE_MASK"); - if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Wind) != 0) - lsl.Append(" | PSYS_PART_WIND_MASK"); - if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.FollowSrc) != 0) - lsl.Append(" | PSYS_PART_FOLLOW_SRC_MASK"); - if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.FollowVelocity) != 0) - lsl.Append(" | PSYS_PART_FOLLOW_VELOCITY_MASK"); - if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.TargetPos) != 0) - lsl.Append(" | PSYS_PART_TARGET_POS_MASK"); - if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.TargetLinear) != 0) - lsl.Append(" | PSYS_PART_TARGET_LINEAR_MASK"); - if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Emissive) != 0) - lsl.Append(" | PSYS_PART_EMISSIVE_MASK"); - - lsl.Append(","); lsl.Append(Environment.NewLine); - lsl.Append(" PSYS_SRC_PATTERN, 0"); - - if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Drop) != 0) - lsl.Append(" | PSYS_SRC_PATTERN_DROP"); - if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Explode) != 0) - lsl.Append(" | PSYS_SRC_PATTERN_EXPLODE"); - if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Angle) != 0) - lsl.Append(" | PSYS_SRC_PATTERN_ANGLE"); - if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.AngleCone) != 0) - lsl.Append(" | PSYS_SRC_PATTERN_ANGLE_CONE"); - if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.AngleConeEmpty) != 0) - lsl.Append(" | PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY"); - - lsl.Append("," + Environment.NewLine); - - lsl.Append(" PSYS_PART_START_ALPHA, " + - $"{exportPrim.ParticleSys.PartStartColor.A:0.00000}" + "," + Environment.NewLine); - lsl.Append(" PSYS_PART_END_ALPHA, " + - $"{exportPrim.ParticleSys.PartEndColor.A:0.00000}" + "," + Environment.NewLine); - lsl.Append(" PSYS_PART_START_COLOR, " + exportPrim.ParticleSys.PartStartColor.ToRGBString() + "," + Environment.NewLine); - lsl.Append(" PSYS_PART_END_COLOR, " + exportPrim.ParticleSys.PartEndColor.ToRGBString() + "," + Environment.NewLine); - lsl.Append(" PSYS_PART_START_SCALE, <" + - $"{exportPrim.ParticleSys.PartStartScaleX:0.00000}" + ", " + - $"{exportPrim.ParticleSys.PartStartScaleY:0.00000}" + ", 0>, " + Environment.NewLine); - lsl.Append(" PSYS_PART_END_SCALE, <" + - $"{exportPrim.ParticleSys.PartEndScaleX:0.00000}" + ", " + - $"{exportPrim.ParticleSys.PartEndScaleY:0.00000}" + ", 0>, " + Environment.NewLine); - lsl.Append(" PSYS_PART_MAX_AGE, " + $"{exportPrim.ParticleSys.PartMaxAge:0.00000}" + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_MAX_AGE, " + $"{exportPrim.ParticleSys.MaxAge:0.00000}" + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_ACCEL, " + exportPrim.ParticleSys.PartAcceleration + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_BURST_PART_COUNT, " + - $"{exportPrim.ParticleSys.BurstPartCount:0}" + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_BURST_RADIUS, " + - $"{exportPrim.ParticleSys.BurstRadius:0.00000}" + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_BURST_RATE, " + - $"{exportPrim.ParticleSys.BurstRate:0.00000}" + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_BURST_SPEED_MIN, " + - $"{exportPrim.ParticleSys.BurstSpeedMin:0.00000}" + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_BURST_SPEED_MAX, " + - $"{exportPrim.ParticleSys.BurstSpeedMax:0.00000}" + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_INNERANGLE, " + - $"{exportPrim.ParticleSys.InnerAngle:0.00000}" + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_OUTERANGLE, " + - $"{exportPrim.ParticleSys.OuterAngle:0.00000}" + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_OMEGA, " + exportPrim.ParticleSys.AngularVelocity + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_TEXTURE, (key)\"" + exportPrim.ParticleSys.Texture + "\"," + Environment.NewLine); - lsl.Append(" PSYS_SRC_TARGET_KEY, (key)\"" + exportPrim.ParticleSys.Target + "\"" + Environment.NewLine); - - lsl.Append(" ]);" + Environment.NewLine); - lsl.Append(" }" + Environment.NewLine); - lsl.Append("}" + Environment.NewLine); - - #endregion Particle System to LSL - - return lsl.ToString(); - } - else - { - return "Prim " + exportPrim.LocalID + " does not have a particle system"; - } + return $"Prim {exportPrim.LocalID} does not have a particle system"; } + + StringBuilder lsl = new StringBuilder(); + + #region Particle System to LSL + + lsl.Append("default" + Environment.NewLine); + lsl.Append("{" + Environment.NewLine); + lsl.Append(" state_entry()" + Environment.NewLine); + lsl.Append(" {" + Environment.NewLine); + lsl.Append(" llParticleSystem([" + Environment.NewLine); + + lsl.Append(" PSYS_PART_FLAGS, 0"); + + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.InterpColor) != 0) + lsl.Append(" | PSYS_PART_INTERP_COLOR_MASK"); + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.InterpScale) != 0) + lsl.Append(" | PSYS_PART_INTERP_SCALE_MASK"); + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Bounce) != 0) + lsl.Append(" | PSYS_PART_BOUNCE_MASK"); + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Wind) != 0) + lsl.Append(" | PSYS_PART_WIND_MASK"); + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.FollowSrc) != 0) + lsl.Append(" | PSYS_PART_FOLLOW_SRC_MASK"); + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.FollowVelocity) != 0) + lsl.Append(" | PSYS_PART_FOLLOW_VELOCITY_MASK"); + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.TargetPos) != 0) + lsl.Append(" | PSYS_PART_TARGET_POS_MASK"); + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.TargetLinear) != 0) + lsl.Append(" | PSYS_PART_TARGET_LINEAR_MASK"); + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Emissive) != 0) + lsl.Append(" | PSYS_PART_EMISSIVE_MASK"); + + lsl.Append(","); lsl.Append(Environment.NewLine); + lsl.Append(" PSYS_SRC_PATTERN, 0"); + + if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Drop) != 0) + lsl.Append(" | PSYS_SRC_PATTERN_DROP"); + if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Explode) != 0) + lsl.Append(" | PSYS_SRC_PATTERN_EXPLODE"); + if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Angle) != 0) + lsl.Append(" | PSYS_SRC_PATTERN_ANGLE"); + if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.AngleCone) != 0) + lsl.Append(" | PSYS_SRC_PATTERN_ANGLE_CONE"); + if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.AngleConeEmpty) != 0) + lsl.Append(" | PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY"); + + lsl.Append("," + Environment.NewLine); + + lsl.Append(" PSYS_PART_START_ALPHA, " + + $"{exportPrim.ParticleSys.PartStartColor.A:0.00000}" + "," + Environment.NewLine); + lsl.Append(" PSYS_PART_END_ALPHA, " + + $"{exportPrim.ParticleSys.PartEndColor.A:0.00000}" + "," + Environment.NewLine); + lsl.Append(" PSYS_PART_START_COLOR, " + exportPrim.ParticleSys.PartStartColor.ToRGBString() + "," + Environment.NewLine); + lsl.Append(" PSYS_PART_END_COLOR, " + exportPrim.ParticleSys.PartEndColor.ToRGBString() + "," + Environment.NewLine); + lsl.Append(" PSYS_PART_START_SCALE, <" + + $"{exportPrim.ParticleSys.PartStartScaleX:0.00000}" + ", " + + $"{exportPrim.ParticleSys.PartStartScaleY:0.00000}" + ", 0>, " + Environment.NewLine); + lsl.Append(" PSYS_PART_END_SCALE, <" + + $"{exportPrim.ParticleSys.PartEndScaleX:0.00000}" + ", " + + $"{exportPrim.ParticleSys.PartEndScaleY:0.00000}" + ", 0>, " + Environment.NewLine); + lsl.Append(" PSYS_PART_MAX_AGE, " + $"{exportPrim.ParticleSys.PartMaxAge:0.00000}" + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_MAX_AGE, " + $"{exportPrim.ParticleSys.MaxAge:0.00000}" + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_ACCEL, " + exportPrim.ParticleSys.PartAcceleration + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_BURST_PART_COUNT, " + + $"{exportPrim.ParticleSys.BurstPartCount:0}" + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_BURST_RADIUS, " + + $"{exportPrim.ParticleSys.BurstRadius:0.00000}" + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_BURST_RATE, " + + $"{exportPrim.ParticleSys.BurstRate:0.00000}" + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_BURST_SPEED_MIN, " + + $"{exportPrim.ParticleSys.BurstSpeedMin:0.00000}" + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_BURST_SPEED_MAX, " + + $"{exportPrim.ParticleSys.BurstSpeedMax:0.00000}" + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_INNERANGLE, " + + $"{exportPrim.ParticleSys.InnerAngle:0.00000}" + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_OUTERANGLE, " + + $"{exportPrim.ParticleSys.OuterAngle:0.00000}" + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_OMEGA, " + exportPrim.ParticleSys.AngularVelocity + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_TEXTURE, (key)\"" + exportPrim.ParticleSys.Texture + "\"," + Environment.NewLine); + lsl.Append(" PSYS_SRC_TARGET_KEY, (key)\"" + exportPrim.ParticleSys.Target + "\"" + Environment.NewLine); + + lsl.Append(" ]);" + Environment.NewLine); + lsl.Append(" }" + Environment.NewLine); + lsl.Append("}" + Environment.NewLine); + + #endregion Particle System to LSL + + return lsl.ToString(); + } } - return "Couldn't find prim " + id; + return $"Could not find {id} object"; } } } diff --git a/Programs/examples/TestClient/Commands/Prims/FindObjectsCommand.cs b/Programs/examples/TestClient/Commands/Prims/FindObjectsCommand.cs index 0a65b29f..3be22d9d 100644 --- a/Programs/examples/TestClient/Commands/Prims/FindObjectsCommand.cs +++ b/Programs/examples/TestClient/Commands/Prims/FindObjectsCommand.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Numerics; using System.Threading; @@ -23,36 +25,36 @@ namespace OpenMetaverse.TestClient public override string Execute(string[] args, UUID fromAgentID) { // *** parse arguments *** - if ((args.Length < 1) || (args.Length > 2)) + if (args.Length < 1 || args.Length > 2) + { return "Usage: findobjects [radius] "; - float radius = float.Parse(args[0]); - string searchString = (args.Length > 1) ? args[1] : string.Empty; + } + + var radius = float.Parse(args[0]); + var searchString = (args.Length > 1) ? args[1] : string.Empty; // *** get current location *** - Vector3 location = Client.Self.SimPosition; + var location = Client.Self.SimPosition; // *** find all objects in radius *** - List prims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll( - delegate(Primitive prim) - { - Vector3 pos = prim.Position; - return ((prim.ParentID == 0) && (pos != Vector3.Zero) && (Vector3.Distance(pos, location) < radius)); - } - ); + var prims = (from kvp + in Client.Network.CurrentSim.ObjectsPrimitives + where kvp.Value != null select kvp.Value into prim let pos = prim.Position + where prim.ParentID == 0 && pos != Vector3.Zero && Vector3.Distance(pos, location) < radius select prim).ToList(); // *** request properties of these objects *** - bool complete = RequestObjectProperties(prims, 250); + var complete = RequestObjectProperties(prims, 250); - foreach (Primitive p in prims) + foreach (var p in prims) { - string name = p.Properties?.Name; + var name = p.Properties?.Name; if (string.IsNullOrEmpty(searchString) || ((name != null) && (name.Contains(searchString)))) Console.WriteLine("Object '{0}': {1}", name, p.ID.ToString()); } if (complete) return "Done searching"; Console.WriteLine("Warning: Unable to retrieve full properties for:"); - foreach (UUID uuid in PrimsWaiting.Keys) + foreach (var uuid in PrimsWaiting.Keys) Console.WriteLine(uuid); return "Done searching"; @@ -83,8 +85,7 @@ namespace OpenMetaverse.TestClient { lock (PrimsWaiting) { - Primitive prim; - if (PrimsWaiting.TryGetValue(e.Properties.ObjectID, out prim)) + if (PrimsWaiting.TryGetValue(e.Properties.ObjectID, out var prim)) { prim.Properties = e.Properties; } diff --git a/Programs/examples/TestClient/Commands/Prims/FindTextureCommand.cs b/Programs/examples/TestClient/Commands/Prims/FindTextureCommand.cs index 325babce..45ab1a2d 100644 --- a/Programs/examples/TestClient/Commands/Prims/FindTextureCommand.cs +++ b/Programs/examples/TestClient/Commands/Prims/FindTextureCommand.cs @@ -1,4 +1,5 @@ using System; +using System.Security.Permissions; namespace OpenMetaverse.TestClient { @@ -14,36 +15,32 @@ namespace OpenMetaverse.TestClient public override string Execute(string[] args, UUID fromAgentID) { - int faceIndex; - UUID textureID; - if (args.Length != 2) - return "Usage: findtexture [face-index] [texture-uuid]"; - - if (int.TryParse(args[0], out faceIndex) && - UUID.TryParse(args[1], out textureID)) { - Client.Network.CurrentSim.ObjectsPrimitives.ForEach( - delegate(Primitive prim) + return "Usage: findtexture [face-index] [texture-uuid]"; + } + + if (int.TryParse(args[0], out var faceIndex) && + UUID.TryParse(args[1], out var textureID)) + { + foreach (var kvp in Client.Network.CurrentSim.ObjectsPrimitives) + { + if (kvp.Value == null) { continue; } + + var prim = kvp.Value; + if (prim.Textures?.FaceTextures[faceIndex] == null) { continue; } + if (prim.Textures.FaceTextures[faceIndex].TextureID == textureID) { - if (prim.Textures?.FaceTextures[faceIndex] != null) - { - if (prim.Textures.FaceTextures[faceIndex].TextureID == textureID) - { - Logger.Log( - $"Primitive {prim.ID.ToString()} ({prim.LocalID}) has face index {faceIndex} set to {textureID.ToString()}", - Helpers.LogLevel.Info, Client); - } - } + Logger.Log( + $"Primitive {prim.ID.ToString()} ({prim.LocalID}) has face index {faceIndex} set to {textureID.ToString()}", + Helpers.LogLevel.Info, Client); } - ); + } return "Done searching"; } - else - { - return "Usage: findtexture [face-index] [texture-uuid]"; - } + + return "Usage: findtexture [face-index] [texture-uuid]"; } } } diff --git a/Programs/examples/TestClient/Commands/Prims/PrimInfoCommand.cs b/Programs/examples/TestClient/Commands/Prims/PrimInfoCommand.cs index 627b3505..156fffa3 100644 --- a/Programs/examples/TestClient/Commands/Prims/PrimInfoCommand.cs +++ b/Programs/examples/TestClient/Commands/Prims/PrimInfoCommand.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading; namespace OpenMetaverse.TestClient @@ -17,73 +18,73 @@ namespace OpenMetaverse.TestClient UUID primID; if (args.Length != 1) - return "Usage: priminfo [prim-uuid]"; - - if (UUID.TryParse(args[0], out primID)) { - Primitive target = Client.Network.CurrentSim.ObjectsPrimitives.Find( - prim => prim.ID == primID - ); + return "Usage: priminfo [prim-uuid]"; + } - if (target != null) + if (!UUID.TryParse(args[0], out primID)) + { + return $"{args[0]} is not a valid UUID"; + } + + var kvp = Client.Network.CurrentSim.ObjectsPrimitives.FirstOrDefault( + prim => prim.Value.ID == primID); + + if (kvp.Value == null) + { + return $"Could not find object {primID}"; + } + + var target = kvp.Value; + if (target.Text != string.Empty) + { + Logger.Log("Text: " + target.Text, Helpers.LogLevel.Info, Client); + } + if (target.Light != null) + { + Logger.Log("Light: " + target.Light, Helpers.LogLevel.Info, Client); + } + if (target.ParticleSys.CRC != 0) + { + Logger.Log("Particles: " + target.ParticleSys, Helpers.LogLevel.Info, Client); + } + + if (target.Textures != null) + { + Logger.Log($"Default texture: {target.Textures.DefaultTexture.TextureID.ToString()}", + Helpers.LogLevel.Info); + + for (int i = 0; i < target.Textures.FaceTextures.Length; i++) { - if (target.Text != string.Empty) + if (target.Textures.FaceTextures[i] != null) { - Logger.Log("Text: " + target.Text, Helpers.LogLevel.Info, Client); + Logger.Log($"Face {i}: {target.Textures.FaceTextures[i].TextureID.ToString()}", + Helpers.LogLevel.Info, Client); } - if(target.Light != null) - Logger.Log("Light: " + target.Light, Helpers.LogLevel.Info, Client); - - if (target.ParticleSys.CRC != 0) - Logger.Log("Particles: " + target.ParticleSys, Helpers.LogLevel.Info, Client); - - Logger.Log("TextureEntry:", Helpers.LogLevel.Info, Client); - if (target.Textures != null) - { - Logger.Log($"Default texure: {target.Textures.DefaultTexture.TextureID.ToString()}", - Helpers.LogLevel.Info); - - for (int i = 0; i < target.Textures.FaceTextures.Length; i++) - { - if (target.Textures.FaceTextures[i] != null) - { - Logger.Log($"Face {i}: {target.Textures.FaceTextures[i].TextureID.ToString()}", - Helpers.LogLevel.Info, Client); - } - } - } - else - { - Logger.Log("null", Helpers.LogLevel.Info, Client); - } - - AutoResetEvent propsEvent = new AutoResetEvent(false); - EventHandler propsCallback = - delegate(object sender, ObjectPropertiesEventArgs e) - { - Logger.Log( - $"Category: {e.Properties.Category}\nFolderID: {e.Properties.FolderID}\nFromTaskID: {e.Properties.FromTaskID}\nInventorySerial: {e.Properties.InventorySerial}\nItemID: {e.Properties.ItemID}\nCreationDate: {e.Properties.CreationDate}", Helpers.LogLevel.Info); - propsEvent.Set(); - }; - - Client.Objects.ObjectProperties += propsCallback; - - Client.Objects.SelectObject(Client.Network.CurrentSim, target.LocalID, true); - - propsEvent.WaitOne(TimeSpan.FromSeconds(10), false); - Client.Objects.ObjectProperties -= propsCallback; - - return "Done."; - } - else - { - return "Could not find prim " + primID; } } else { - return "Usage: priminfo [prim-uuid]"; + Logger.Log("null", Helpers.LogLevel.Info, Client); } + + AutoResetEvent propsEvent = new AutoResetEvent(false); + EventHandler propsCallback = + delegate(object sender, ObjectPropertiesEventArgs e) + { + Logger.Log( + $"Category: {e.Properties.Category}\nFolderID: {e.Properties.FolderID}\nFromTaskID: {e.Properties.FromTaskID}\nInventorySerial: {e.Properties.InventorySerial}\nItemID: {e.Properties.ItemID}\nCreationDate: {e.Properties.CreationDate}", Helpers.LogLevel.Info); + propsEvent.Set(); + }; + + Client.Objects.ObjectProperties += propsCallback; + + Client.Objects.SelectObject(Client.Network.CurrentSim, target.LocalID, true); + + propsEvent.WaitOne(TimeSpan.FromSeconds(10), false); + Client.Objects.ObjectProperties -= propsCallback; + + return "Done."; } } } diff --git a/Programs/examples/TestClient/Commands/Prims/PrimRegexCommand.cs b/Programs/examples/TestClient/Commands/Prims/PrimRegexCommand.cs index 5dc7ac6b..37dc0c9d 100644 --- a/Programs/examples/TestClient/Commands/Prims/PrimRegexCommand.cs +++ b/Programs/examples/TestClient/Commands/Prims/PrimRegexCommand.cs @@ -20,46 +20,46 @@ namespace OpenMetaverse.TestClient try { - // Build the predicat from the args list - string predicatPrim = args.Aggregate(string.Empty, (current, t) => current + (t + " ")); - predicatPrim = predicatPrim.TrimEnd(); + // Build the predicate from the args list + var predicatePrim = args.Aggregate(string.Empty, (current, t) => current + (t + " ")); + predicatePrim = predicatePrim.TrimEnd(); // Build Regex - Regex regexPrimName = new Regex(predicatPrim.ToLower()); + var regexPrimName = new Regex(predicatePrim.ToLower()); // Print result Logger.Log( - $"Searching prim for [{predicatPrim}] ({Client.Network.CurrentSim.ObjectsPrimitives.Count} prims loaded in simulator)\n", Helpers.LogLevel.Info, Client); + $"Searching prim for [{predicatePrim}] ({Client.Network.CurrentSim.ObjectsPrimitives.Count} prims loaded in simulator)\n", + Helpers.LogLevel.Info, Client); - Client.Network.CurrentSim.ObjectsPrimitives.ForEach( - delegate(Primitive prim) + foreach (var kvp in Client.Network.CurrentSim.ObjectsPrimitives) + { + if (kvp.Value == null) { continue; } + + var prim = kvp.Value; + var name = "(unknown)"; + var description = "(unknown)"; + + var match = (prim.Text != null && regexPrimName.IsMatch(prim.Text.ToLower())); + + if (prim.Properties != null && !match) { - bool match = false; - string name = "(unknown)"; - string description = "(unknown)"; - - - match = (prim.Text != null && regexPrimName.IsMatch(prim.Text.ToLower())); - - if (prim.Properties != null && !match) - { - match = regexPrimName.IsMatch(prim.Properties.Name.ToLower()); - if (!match) - match = regexPrimName.IsMatch(prim.Properties.Description.ToLower()); - } - - if (match) - { - if (prim.Properties != null) - { - name = prim.Properties.Name; - description = prim.Properties.Description; - } - Logger.Log( - $"\nNAME={name}\nID = {prim.ID}\nFLAGS = {prim.Flags.ToString()}\nTEXT = '{prim.Text}'\nDESC='{description}'", Helpers.LogLevel.Info, Client); - } + match = regexPrimName.IsMatch(prim.Properties.Name.ToLower()); + if (!match) + match = regexPrimName.IsMatch(prim.Properties.Description.ToLower()); } - ); + + if (!match) { continue; } + + if (prim.Properties != null) + { + name = prim.Properties.Name; + description = prim.Properties.Description; + } + Logger.Log( + $"\nNAME={name}\nID = {prim.ID}\nFLAGS = {prim.Flags.ToString()}\nTEXT = '{prim.Text}'\nDESC='{description}'", + Helpers.LogLevel.Info, Client); + } } catch (System.Exception e) { diff --git a/Programs/examples/TestClient/Commands/System/SetMasterKeyCommand.cs b/Programs/examples/TestClient/Commands/System/SetMasterKeyCommand.cs index 4d15f826..eccc858a 100644 --- a/Programs/examples/TestClient/Commands/System/SetMasterKeyCommand.cs +++ b/Programs/examples/TestClient/Commands/System/SetMasterKeyCommand.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; namespace OpenMetaverse.TestClient { @@ -19,18 +20,14 @@ namespace OpenMetaverse.TestClient lock (Client.Network.Simulators) { - foreach (var sim in Client.Network.Simulators) + foreach (var master in Client.Network.Simulators + .Select(sim => sim.ObjectsAvatars.FirstOrDefault( + kvp => kvp.Value.ID == Client.MasterKey)) + .Where(master => master.Value != null)) { - Avatar master = sim.ObjectsAvatars.Find( - avatar => avatar.ID == Client.MasterKey - ); - - if (master != null) - { - Client.Self.InstantMessage(master.ID, - "You are now my master. IM me with \"help\" for a command list."); - break; - } + Client.Self.InstantMessage(master.Value.ID, + "You are now my master. IM me with \"help\" for a command list."); + break; } }