From 349830c983fcedf35cc2ea2622bcd1e599c3df4a Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 29 Aug 2007 08:55:53 +0000 Subject: [PATCH] * Object tracker dictionaries are now internal and only accessible through various methods and properties to prevent locking disasters. Basis of this code written by jradford in issue 342 * Added a Client.Self.Name property for the full name of the avatar which does smart caching * Adds BackupCommand to TestClient (submitted by CheechBode in issue 314), backs up notecards and scripts from your inventory to hard drive. Needs more refining but it works * Parsing problems in TestClient no longer crash the app git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@1378 52acb1d6-8a22-11de-b505-999d5b087335 --- libsecondlife/FriendsManager.cs | 2 +- libsecondlife/GroupManager.cs | 2 +- libsecondlife/InventoryManager.cs | 6 +- libsecondlife/MainAvatar.cs | 81 ++-- libsecondlife/ObjectTracker.cs | 125 +++++- libsecondlife/SecondLife.cs | 2 +- .../Commands/Inventory/BackupCommand.cs | 362 ++++++++++++++++++ .../Commands/Inventory/DumpOutfitCommand.cs | 69 ++-- .../Commands/Movement/FollowCommand.cs | 43 ++- .../Commands/Movement/SitCommand.cs | 11 +- .../Commands/Movement/SitOnCommand.cs | 21 +- .../Commands/Prims/ExportCommand.cs | 54 ++- .../Commands/Prims/ExportParticlesCommand.cs | 89 +++-- .../Commands/Prims/PrimCountCommand.cs | 13 +- .../Commands/System/SetMasterCommand.cs | 25 +- .../Commands/System/SetMasterKeyCommand.cs | 19 +- .../TestClient/Commands/TouchCommand.cs | 17 +- .../TestClient/Commands/WhoCommand.cs | 11 +- libsecondlife/examples/TestClient/Parsing.cs | 63 ++- .../examples/TestClient/TestClient.cs | 7 +- .../examples/TestClient/TestClient.csproj | 1 + 21 files changed, 777 insertions(+), 246 deletions(-) create mode 100644 libsecondlife/examples/TestClient/Commands/Inventory/BackupCommand.cs diff --git a/libsecondlife/FriendsManager.cs b/libsecondlife/FriendsManager.cs index 54b724e1..1e0d18d1 100644 --- a/libsecondlife/FriendsManager.cs +++ b/libsecondlife/FriendsManager.cs @@ -374,7 +374,7 @@ namespace libsecondlife { // HACK: folder id stored as "message" LLUUID callingCardFolder = Client.Inventory.FindFolderForType(AssetType.CallingCard); - Client.Self.InstantMessage(Client.ToString(), + Client.Self.InstantMessage(Client.Self.Name, agentID, callingCardFolder.ToString(), LLUUID.Random(), diff --git a/libsecondlife/GroupManager.cs b/libsecondlife/GroupManager.cs index 6d52bcbe..f094430a 100644 --- a/libsecondlife/GroupManager.cs +++ b/libsecondlife/GroupManager.cs @@ -923,7 +923,7 @@ namespace libsecondlife /// public void SendGroupNotice(LLUUID group, GroupNotice notice) { - Client.Self.InstantMessage(Client.ToString(), group, notice.Subject + "|" + notice.Message, + Client.Self.InstantMessage(Client.Self.Name, group, notice.Subject + "|" + notice.Message, LLUUID.Zero, InstantMessageDialog.GroupNotice, InstantMessageOnline.Online, LLVector3.Zero, LLUUID.Zero, notice.SerializeAttachment()); } diff --git a/libsecondlife/InventoryManager.cs b/libsecondlife/InventoryManager.cs index d8645e0a..286e472f 100644 --- a/libsecondlife/InventoryManager.cs +++ b/libsecondlife/InventoryManager.cs @@ -917,7 +917,7 @@ namespace libsecondlife /// InventoryObject object containing item details public LLUUID RezFromInventory(Simulator simulator, LLQuaternion rotation, LLVector3 position, InventoryObject item) { - return RezFromInventory(simulator, rotation, position, item, Client.Self.ActiveGroup, LLUUID.Zero); + return RezFromInventory(simulator, rotation, position, item, Client.Self.ActiveGroup, LLUUID.Random()); } /// @@ -931,7 +931,7 @@ namespace libsecondlife public LLUUID RezFromInventory(Simulator simulator, LLQuaternion rotation, LLVector3 position, InventoryObject item, LLUUID groupOwner) { - return RezFromInventory(simulator, rotation, position, item, groupOwner, LLUUID.Zero); + return RezFromInventory(simulator, rotation, position, item, groupOwner, LLUUID.Random()); } /// @@ -1266,7 +1266,7 @@ namespace libsecondlife imp.MessageBlock.Offline = 0; imp.MessageBlock.ID = im.IMSessionID; imp.MessageBlock.Timestamp = 0; - imp.MessageBlock.FromAgentName = Helpers.StringToField(Client.ToString()); + imp.MessageBlock.FromAgentName = Helpers.StringToField(Client.Self.Name); imp.MessageBlock.Message = new byte[0]; imp.MessageBlock.ParentEstateID = 0; imp.MessageBlock.RegionID = LLUUID.Zero; diff --git a/libsecondlife/MainAvatar.cs b/libsecondlife/MainAvatar.cs index 660d6fa9..fddc2b72 100644 --- a/libsecondlife/MainAvatar.cs +++ b/libsecondlife/MainAvatar.cs @@ -595,9 +595,9 @@ namespace libsecondlife // FIXME: Most all of these should change from public variables to read-only public properties /// Your (client) avatar UUID - public LLUUID ID = LLUUID.Zero; + public LLUUID ID; /// Your (client) avatar ID, local to the current region/sim - public uint LocalID = 0; + public uint LocalID; /// Where the avatar started at login. Can be "last", "home" /// or a login URI public string StartLocation = String.Empty; @@ -606,46 +606,58 @@ namespace libsecondlife /// Positive and negative ratings /// This information is read-only and any changes will not be /// reflected on the server - public Avatar.Statistics ProfileStatistics = new Avatar.Statistics(); + public Avatar.Statistics ProfileStatistics; /// Avatar properties including about text, profile URL, image IDs and /// publishing settings /// If you change fields in this struct, the changes will not /// be reflected on the server until you call SetAvatarInformation - public Avatar.AvatarProperties ProfileProperties = new Avatar.AvatarProperties(); + public Avatar.AvatarProperties ProfileProperties; /// Avatar interests including spoken languages, skills, and "want to" /// choices /// If you change fields in this struct, the changes will not /// be reflected on the server until you call SetAvatarInformation - public Avatar.Interests ProfileInterests = new Avatar.Interests(); + public Avatar.Interests ProfileInterests; /// Current position of avatar - public LLVector3 Position = LLVector3.Zero; + public LLVector3 Position; /// Current rotation of avatar public LLQuaternion Rotation = LLQuaternion.Identity; /// - public LLVector4 CollisionPlane = LLVector4.Zero; + public LLVector4 CollisionPlane; /// - public LLVector3 Velocity = LLVector3.Zero; + public LLVector3 Velocity; /// - public LLVector3 Acceleration = LLVector3.Zero; + public LLVector3 Acceleration; /// - public LLVector3 AngularVelocity = LLVector3.Zero; + public LLVector3 AngularVelocity; /// The point the avatar is currently looking at /// (may not stay updated) - public LLVector3 LookAt = LLVector3.Zero; + public LLVector3 LookAt; /// Position avatar client will goto when login to 'home' or during /// teleport request to 'home' region. - public LLVector3 HomePosition = LLVector3.Zero; + public LLVector3 HomePosition; /// LookAt point saved/restored with HomePosition - public LLVector3 HomeLookAt = LLVector3.Zero; + public LLVector3 HomeLookAt; /// Used for camera and control key state tracking public MainAvatarStatus Status; /// The UUID of your root inventory folder - public LLUUID InventoryRootFolderUUID = LLUUID.Zero; + public LLUUID InventoryRootFolderUUID; /// Avatar First Name (i.e. Philip) public string FirstName { get { return firstName; } } /// Avatar Last Name (i.e. Linden) public string LastName { get { return lastName; } } + /// Avatar Full Name (i.e. Philip Linden) + public string Name + { + get + { + // This is a fairly common request, so assume the name doesn't + // change mid-session and cache the result + if (fullName == null) + fullName = String.Format("{0} {1}", firstName, lastName); + return fullName; + } + } /// Gets the health of the agent public float Health { get { return health; } } /// Gets the current balance of the agent @@ -681,15 +693,16 @@ namespace libsecondlife internal string firstName = String.Empty; internal string lastName = String.Empty; internal string teleportMessage = String.Empty; - internal uint sittingOn = 0; + internal uint sittingOn; internal DateTime lastInterpolation; + private string fullName; private TeleportStatus TeleportStat = TeleportStatus.None; private ManualResetEvent TeleportEvent = new ManualResetEvent(false); - private uint HeightWidthGenCounter = 0; - private float health = 0.0f; - private int balance = 0; - private LLUUID activeGroup = LLUUID.Zero; + private uint HeightWidthGenCounter; + private float health; + private int balance; + private LLUUID activeGroup; #region AgentUpdate Constants @@ -740,6 +753,8 @@ namespace libsecondlife Status = new MainAvatarStatus(Client); NetworkManager.PacketCallback callback; + Client.Network.OnDisconnected += new NetworkManager.DisconnectedCallback(Network_OnDisconnected); + // Teleport callbacks callback = new NetworkManager.PacketCallback(TeleportHandler); Client.Network.RegisterCallback(PacketType.TeleportStart, callback); @@ -786,7 +801,7 @@ namespace libsecondlife /// Text message being sent public void InstantMessage(LLUUID target, string message) { - InstantMessage(Client.ToString(), target, message, LLUUID.Random(), + InstantMessage(Name, target, message, LLUUID.Random(), InstantMessageDialog.MessageFromAgent, InstantMessageOnline.Offline, this.Position, LLUUID.Zero, new byte[0]); } @@ -799,7 +814,7 @@ namespace libsecondlife /// IM session ID (to differentiate between IM windows) public void InstantMessage(LLUUID target, string message, LLUUID imSessionID) { - InstantMessage(Client.ToString(), target, message, imSessionID, + InstantMessage(Name, target, message, imSessionID, InstantMessageDialog.MessageFromAgent, InstantMessageOnline.Offline, this.Position, LLUUID.Zero, new byte[0]); } @@ -883,7 +898,7 @@ namespace libsecondlife /// Text Message being sent. public void InstantMessageGroup(LLUUID groupUUID, string message) { - InstantMessageGroup(Client.ToString(), groupUUID, message); + InstantMessageGroup(Name, groupUUID, message); } /// @@ -1657,7 +1672,7 @@ namespace libsecondlife /// Accept the teleport request or deny it public void TeleportLureRespond(LLUUID requesterID, bool accept) { - InstantMessage(Client.ToString(), requesterID, String.Empty, LLUUID.Random(), + InstantMessage(Name, requesterID, String.Empty, LLUUID.Random(), accept ? InstantMessageDialog.AcceptTeleport : InstantMessageDialog.DenyTeleport, InstantMessageOnline.Offline, this.Position, LLUUID.Zero, new byte[0]); @@ -1927,7 +1942,7 @@ namespace libsecondlife } /// - /// Used for parsing llDialog's + /// Used for parsing llDialogs /// /// Incoming ScriptDialog packet /// Unused @@ -2001,9 +2016,7 @@ namespace libsecondlife health = ((HealthMessagePacket)packet).HealthData.Health; } - - - public void AgentDataUpdateHandler(Packet packet, Simulator simulator) + private void AgentDataUpdateHandler(Packet packet, Simulator simulator) { AgentDataUpdatePacket p = (AgentDataUpdatePacket)packet; @@ -2079,7 +2092,7 @@ namespace libsecondlife Client.DebugLog(String.Format( "Received a TeleportFinish event from {0}, SimIP: {1}, Location: {2}, RegionHandle: {3}", - caps.Simulator.ToString(), packet.Info.SimIP, packet.Info.LocationID, packet.Info.RegionHandle)); + caps.Simulator, packet.Info.SimIP, packet.Info.LocationID, packet.Info.RegionHandle)); TeleportHandler(packet, Client.Network.CurrentSim); } @@ -2218,7 +2231,15 @@ namespace libsecondlife if (finished) TeleportEvent.Set(); } - } - #endregion Packet Handlers + private void Network_OnDisconnected(NetworkManager.DisconnectType reason, string message) + { + // Null out the cached fullName since it can change after logging + // in again (with a different account name or different login + // server but using the same SecondLife object + fullName = null; + } + + #endregion Packet Handlers + } } diff --git a/libsecondlife/ObjectTracker.cs b/libsecondlife/ObjectTracker.cs index 6870f285..bfd8230e 100644 --- a/libsecondlife/ObjectTracker.cs +++ b/libsecondlife/ObjectTracker.cs @@ -31,7 +31,128 @@ namespace libsecondlife { public class ObjectTracker { - public Dictionary Avatars = new Dictionary(); - public Dictionary Prims = new Dictionary(); + internal Dictionary Avatars = new Dictionary(); + internal Dictionary Prims = new Dictionary(); + + #region Properties + + public int AvatarCount + { + get { return Avatars.Count; } + } + + public int PrimCount + { + get { return Prims.Count; } + } + + #endregion Properties + + public bool TryGetValue(uint objectLocalID, out LLObject obj) + { + Avatar avatar; + Primitive prim; + + if (Avatars.TryGetValue(objectLocalID, out avatar)) + { + obj = avatar; + return true; + } + + if (Prims.TryGetValue(objectLocalID, out prim)) + { + obj = prim; + return true; + } + + obj = null; + return false; + } + + public bool TryGetAvatar(uint avatarLocalID, out Avatar avatar) + { + return Avatars.TryGetValue(avatarLocalID, out avatar); + } + + public bool TryGetPrimitive(uint primLocalID, out Primitive prim) + { + return Prims.TryGetValue(primLocalID, out prim); + } + + public List FindAll(Predicate match) + { + List found = new List(); + lock (Prims) + { + foreach (KeyValuePair kvp in Prims) + { + if (match(kvp.Value)) + found.Add(kvp.Value); + } + } + return found; + } + + public List FindAll(Predicate match) + { + List found = new List(); + lock (Avatars) + { + foreach (KeyValuePair kvp in Avatars) + { + if (match(kvp.Value)) + found.Add(kvp.Value); + } + } + return found; + } + + public Primitive Find(Predicate match) + { + lock (Prims) + { + foreach (Primitive prim in Prims.Values) + { + if (match(prim)) + return prim; + } + } + return null; + } + + public Avatar Find(Predicate match) + { + lock (Avatars) + { + foreach (Avatar avatar in Avatars.Values) + { + if (match(avatar)) + return avatar; + } + } + return null; + } + + public void ForEach(Action action) + { + lock (Avatars) + { + foreach (Primitive prim in Prims.Values) + { + action(prim); + } + } + } + + public void ForEach(Action action) + { + lock (Prims) + { + foreach (Avatar avatar in Avatars.Values) + { + action(avatar); + } + } + } } } diff --git a/libsecondlife/SecondLife.cs b/libsecondlife/SecondLife.cs index 0497fe8a..a3c8dd66 100644 --- a/libsecondlife/SecondLife.cs +++ b/libsecondlife/SecondLife.cs @@ -116,7 +116,7 @@ namespace libsecondlife /// Client avatars full name public override string ToString() { - return Self.FirstName + " " + Self.LastName; + return Self.Name; } /// diff --git a/libsecondlife/examples/TestClient/Commands/Inventory/BackupCommand.cs b/libsecondlife/examples/TestClient/Commands/Inventory/BackupCommand.cs new file mode 100644 index 00000000..2544a2ed --- /dev/null +++ b/libsecondlife/examples/TestClient/Commands/Inventory/BackupCommand.cs @@ -0,0 +1,362 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Xml; +using System.Xml.Serialization; +using libsecondlife; +using libsecondlife.Packets; +using libsecondlife.TestClient; + +namespace libsecondlife.TestClient +{ + public class QueuedDownloadInfo + { + public LLUUID TransferID; + public LLUUID AssetID; + public LLUUID ItemID; + public LLUUID TaskID; + public LLUUID OwnerID; + public AssetType Type; + public string FileName; + public DateTime WhenRequested; + public bool IsRequested; + + public QueuedDownloadInfo(string file, LLUUID asset, LLUUID item, LLUUID task, LLUUID owner, AssetType type) + { + FileName = file; + AssetID = asset; + ItemID = item; + TaskID = task; + OwnerID = owner; + Type = type; + TransferID = LLUUID.Zero; + WhenRequested = DateTime.Now; + IsRequested = false; + } + } + + public class BackupCommand : Command + { + /// Maximum number of transfer requests to send to the server + private const int MAX_TRANSFERS = 10; + + // all items here, fed by the inventory walking thread + private Queue PendingDownloads = new Queue(); + + // items sent to the server here + private List CurrentDownloads = new List(MAX_TRANSFERS); + + // background workers + private BackgroundWorker BackupWorker; + private BackgroundWorker QueueWorker; + + // some stats + private int TextItemsFound; + private int TextItemsTransferred; + private int TextItemErrors; + + #region Properties + + /// + /// true if either of the background threads is running + /// + private bool BackgroundBackupRunning + { + get { return InventoryWalkerRunning || QueueRunnerRunning; } + } + + /// + /// true if the thread walking inventory is running + /// + private bool InventoryWalkerRunning + { + get { return BackupWorker != null; } + } + + /// + /// true if the thread feeding the queue to the server is running + /// + private bool QueueRunnerRunning + { + get { return QueueWorker != null; } + } + + /// + /// returns a string summarizing activity + /// + /// + private string BackgroundBackupStatus + { + get + { + StringBuilder sbResult = new StringBuilder(); + sbResult.AppendFormat("{0} is {1} running.", Name, BoolToNot(BackgroundBackupRunning)); + if (TextItemErrors != 0 || TextItemsFound != 0 || TextItemsTransferred != 0) + { + sbResult.AppendFormat("\r\n{0} : Inventory walker ( {1} running ) has found {2} items.", + Name, BoolToNot(InventoryWalkerRunning), TextItemsFound); + sbResult.AppendFormat("\r\n{0} : Server Transfers ( {1} running ) has transferred {2} items with {3} errors.", + Name, BoolToNot(QueueRunnerRunning), TextItemsTransferred, TextItemErrors); + sbResult.AppendFormat("\r\n{0} : {1} items in Queue, {2} items requested from server.", + Name, PendingDownloads.Count, CurrentDownloads.Count); + } + return sbResult.ToString(); + } + } + + #endregion Properties + + public BackupCommand(TestClient testClient) + { + Name = "backuptext"; + Description = "Backup inventory to a folder on your hard drive. Usage: " + Name + " [to ] | [abort] | [status]"; + testClient.Assets.OnAssetReceived += new AssetManager.AssetReceivedCallback(Assets_OnAssetReceived); + } + + public override string Execute(string[] args, LLUUID fromAgentID) + { + StringBuilder sbResult = new StringBuilder(); + + if (args.Length == 1 && args[0] == "status") + { + return BackgroundBackupStatus; + } + else if (args.Length == 1 && args[0] == "abort") + { + if (!BackgroundBackupRunning) + return BackgroundBackupStatus; + + BackupWorker.CancelAsync(); + QueueWorker.CancelAsync(); + + Thread.Sleep(500); + + // check status + return BackgroundBackupStatus; + } + else if (args.Length != 2) + { + return "Usage: " + Name + " [to ] | [abort] | [status]"; + } + else if (BackgroundBackupRunning) + { + return BackgroundBackupStatus; + } + + QueueWorker = new BackgroundWorker(); + QueueWorker.WorkerSupportsCancellation = true; + QueueWorker.DoWork += new DoWorkEventHandler(bwQueueRunner_DoWork); + QueueWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwQueueRunner_RunWorkerCompleted); + + QueueWorker.RunWorkerAsync(); + + BackupWorker = new BackgroundWorker(); + BackupWorker.WorkerSupportsCancellation = true; + BackupWorker.DoWork +=new DoWorkEventHandler(bwBackup_DoWork); + BackupWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwBackup_RunWorkerCompleted); + + BackupWorker.RunWorkerAsync(args); + return "Started background operations."; + } + + void bwQueueRunner_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + QueueWorker = null; + Console.WriteLine(BackgroundBackupStatus); + } + + void bwQueueRunner_DoWork(object sender, DoWorkEventArgs e) + { + TextItemErrors = TextItemsTransferred = 0; + + while (QueueWorker.CancellationPending == false) + { + // have any timed out? + if (CurrentDownloads.Count > 0) + { + foreach (QueuedDownloadInfo qdi in CurrentDownloads) + { + if ((qdi.WhenRequested + TimeSpan.FromSeconds(60)) < DateTime.Now) + { + Client.DebugLog(Name + ": timeout on asset " + qdi.AssetID.ToStringHyphenated()); + // submit request again + qdi.TransferID = Client.Assets.RequestInventoryAsset( + qdi.AssetID, qdi.ItemID, qdi.TaskID, qdi.OwnerID, qdi.Type, true); + qdi.WhenRequested = DateTime.Now; + qdi.IsRequested = true; + } + } + } + + if (PendingDownloads.Count != 0) + { + // room in the server queue? + if (CurrentDownloads.Count < MAX_TRANSFERS) + { + // yes + QueuedDownloadInfo qdi = PendingDownloads.Dequeue(); + qdi.WhenRequested = DateTime.Now; + qdi.IsRequested = true; + qdi.TransferID = Client.Assets.RequestInventoryAsset( + qdi.AssetID, qdi.ItemID, qdi.TaskID, qdi.OwnerID, qdi.Type, true); + + lock (CurrentDownloads) CurrentDownloads.Add(qdi); + } + } + + if (CurrentDownloads.Count == 0 && PendingDownloads.Count == 0 && BackupWorker == null) + { + Client.DebugLog(Name + ": both transfer queues empty AND inventory walking thread is done"); + return; + } + + Thread.Sleep(100); + } + } + + void bwBackup_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + Console.WriteLine(Name + ": Inventory walking thread done."); + BackupWorker = null; + } + + private void bwBackup_DoWork(object sender, DoWorkEventArgs e) + { + string[] args; + + TextItemsFound = 0; + + args = (string[]) e.Argument; + + lock (CurrentDownloads) CurrentDownloads.Clear(); + + // this call blocks + Client.Inventory.RequestFolderContents(Client.Inventory.Store.RootFolder.UUID, Client.Network.AgentID, + true, true, false, InventorySortOrder.ByName); + + DirectoryInfo di = new DirectoryInfo(args[1]); + + // recurse on the root folder into the entire inventory + BackupFolder(Client.Inventory.Store.RootNode, di.FullName); + } + + /// + /// BackupFolder - recurse through the inventory nodes sending scripts and notecards to the transfer queue + /// + /// The current leaf in the inventory tree + /// path so far, in the form @"c:\here" -- this needs to be "clean" for the current filesystem + private void BackupFolder(InventoryNode folder, string sPathSoFar) + { + StringBuilder sbRequests = new StringBuilder(); + + // blocking call + Client.Inventory.RequestFolderContents(folder.Data.UUID, Client.Network.AgentID, true, true, false, + InventorySortOrder.ByName); + + // first scan this folder for text + foreach (InventoryNode iNode in folder.Nodes.Values) + { + if (BackupWorker.CancellationPending) + return; + if (iNode.Data is libsecondlife.InventoryItem) + { + InventoryItem ii = iNode.Data as InventoryItem; + if (ii.AssetType == AssetType.LSLText || ii.AssetType == AssetType.Notecard) + { + // check permissions on scripts + if (ii.AssetType == AssetType.LSLText) + { + if ((ii.Permissions.OwnerMask & PermissionMask.Modify) == PermissionMask.None) + { + // skip this one + continue; + } + } + + string sExtension = (ii.AssetType == AssetType.LSLText) ? ".lsl" : ".txt"; + // make the output file + string sPath = sPathSoFar + @"\" + MakeValid(ii.Name.Trim()) + sExtension; + + // create the new qdi + QueuedDownloadInfo qdi = new QueuedDownloadInfo(sPath, ii.AssetUUID, iNode.Data.UUID, LLUUID.Zero, + Client.Network.AgentID, ii.AssetType); + + // add it to the queue + lock (PendingDownloads) + { + TextItemsFound++; + PendingDownloads.Enqueue(qdi); + } + } + } + } + + // now run any subfolders + foreach (InventoryNode i in folder.Nodes.Values) + { + if (BackupWorker.CancellationPending) + return; + else if (i.Data is libsecondlife.InventoryFolder) + BackupFolder(i, sPathSoFar + @"\" + MakeValid(i.Data.Name.Trim())); + } + } + + private string MakeValid(string path) + { + // FIXME: We need to strip illegal characters out + return path.Trim().Replace('"', '\''); + } + + private void Assets_OnAssetReceived(AssetDownload asset, Asset blah) + { + lock (CurrentDownloads) + { + // see if we have this in our transfer list + QueuedDownloadInfo r = CurrentDownloads.Find(delegate(QueuedDownloadInfo q) + { + return q.TransferID == asset.ID; + }); + + if (r != null && r.TransferID == asset.ID) + { + if (asset.Success) + { + // create the directory to put this in + Directory.CreateDirectory(Path.GetDirectoryName(r.FileName)); + + // write out the file + File.WriteAllBytes(r.FileName, asset.AssetData); + Client.DebugLog(Name + " Wrote: " + r.FileName); + TextItemsTransferred++; + } + else + { + TextItemErrors++; + Console.WriteLine("{0}: Download of asset {1} ({2}) failed with status {3}", Name, r.FileName, + r.AssetID.ToStringHyphenated(), asset.Status.ToString()); + } + + // remove the entry + CurrentDownloads.Remove(r); + } + else + { + Client.DebugLog(Name + ": Unexpected asset " + asset.AssetID.ToStringHyphenated()); + } + } + } + + /// + /// returns blank or "not" if false + /// + /// + /// + private static string BoolToNot(bool b) + { + return b ? String.Empty : "not"; + } + } +} diff --git a/libsecondlife/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs b/libsecondlife/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs index fe205973..fdf4ebe4 100644 --- a/libsecondlife/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs @@ -32,47 +32,50 @@ namespace libsecondlife.TestClient { for (int i = 0; i < Client.Network.Simulators.Count; i++) { - lock (Client.Network.Simulators[i].Objects.Avatars) - { - foreach (Avatar avatar in Client.Network.Simulators[i].Objects.Avatars.Values) + Avatar targetAv; + + targetAv = Client.Network.Simulators[i].Objects.Find( + delegate(Avatar avatar) { - if (avatar.ID == target) + return avatar.ID == target; + } + ); + + if (targetAv != null) + { + StringBuilder output = new StringBuilder("Downloading "); + + lock (OutfitAssets) OutfitAssets.Clear(); + Client.Assets.OnImageReceived += ImageReceivedHandler; + + for (int j = 0; j < targetAv.Textures.FaceTextures.Length; j++) + { + LLObject.TextureEntryFace face = targetAv.Textures.FaceTextures[j]; + + if (face != null) { - StringBuilder output = new StringBuilder("Downloading "); + ImageType type = ImageType.Normal; - lock (OutfitAssets) OutfitAssets.Clear(); - Client.Assets.OnImageReceived += ImageReceivedHandler; - - for (int j = 0; j < avatar.Textures.FaceTextures.Length; j++) + switch ((AppearanceManager.TextureIndex)j) { - LLObject.TextureEntryFace face = avatar.Textures.FaceTextures[j]; - - if (face != null) - { - ImageType type = ImageType.Normal; - - switch ((AppearanceManager.TextureIndex)j) - { - case AppearanceManager.TextureIndex.HeadBaked: - case AppearanceManager.TextureIndex.EyesBaked: - case AppearanceManager.TextureIndex.UpperBaked: - case AppearanceManager.TextureIndex.LowerBaked: - case AppearanceManager.TextureIndex.SkirtBaked: - type = ImageType.Baked; - break; - } - - OutfitAssets.Add(face.TextureID); - Client.Assets.RequestImage(face.TextureID, type, 100000.0f, 0); - - output.Append(((AppearanceManager.TextureIndex)j).ToString()); - output.Append(" "); - } + case AppearanceManager.TextureIndex.HeadBaked: + case AppearanceManager.TextureIndex.EyesBaked: + case AppearanceManager.TextureIndex.UpperBaked: + case AppearanceManager.TextureIndex.LowerBaked: + case AppearanceManager.TextureIndex.SkirtBaked: + type = ImageType.Baked; + break; } - return output.ToString(); + OutfitAssets.Add(face.TextureID); + Client.Assets.RequestImage(face.TextureID, type, 100000.0f, 0); + + output.Append(((AppearanceManager.TextureIndex)j).ToString()); + output.Append(" "); } } + + return output.ToString(); } } } diff --git a/libsecondlife/examples/TestClient/Commands/Movement/FollowCommand.cs b/libsecondlife/examples/TestClient/Commands/Movement/FollowCommand.cs index b6ce0e93..3067a8ef 100644 --- a/libsecondlife/examples/TestClient/Commands/Movement/FollowCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/Movement/FollowCommand.cs @@ -48,17 +48,18 @@ namespace libsecondlife.TestClient { for (int i = 0; i < Client.Network.Simulators.Count; i++) { - lock (Client.Network.Simulators[i].Objects.Avatars) - { - foreach (Avatar avatar in Client.Network.Simulators[i].Objects.Avatars.Values) + Avatar target = Client.Network.Simulators[i].Objects.Find( + delegate(Avatar avatar) { - if (avatar.Name == name) - { - targetLocalID = avatar.LocalID; - Active = true; - return true; - } + return avatar.Name == name; } + ); + + if (target != null) + { + targetLocalID = target.LocalID; + Active = true; + return true; } } } @@ -73,17 +74,18 @@ namespace libsecondlife.TestClient { for (int i = 0; i < Client.Network.Simulators.Count; i++) { - lock (Client.Network.Simulators[i].Objects.Avatars) - { - foreach (Avatar avatar in Client.Network.Simulators[i].Objects.Avatars.Values) + Avatar target = Client.Network.Simulators[i].Objects.Find( + delegate(Avatar avatar) { - if (avatar.ID == id) - { - targetLocalID = avatar.LocalID; - Active = true; - return true; - } + return avatar.ID == id; } + ); + + if (target != null) + { + targetLocalID = target.LocalID; + Active = true; + return true; } } } @@ -99,9 +101,10 @@ namespace libsecondlife.TestClient { for (int i = 0; i < Client.Network.Simulators.Count; i++) { - if (Client.Network.Simulators[i].Objects.Avatars.ContainsKey(targetLocalID)) + Avatar targetAv; + + if (Client.Network.Simulators[i].Objects.TryGetAvatar(targetLocalID, out targetAv)) { - Avatar targetAv = Client.Network.Simulators[i].Objects.Avatars[targetLocalID]; float distance = 0.0f; if (Client.Network.Simulators[i] == Client.Network.CurrentSim) diff --git a/libsecondlife/examples/TestClient/Commands/Movement/SitCommand.cs b/libsecondlife/examples/TestClient/Commands/Movement/SitCommand.cs index bb915c03..e16ee834 100644 --- a/libsecondlife/examples/TestClient/Commands/Movement/SitCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/Movement/SitCommand.cs @@ -19,19 +19,18 @@ namespace libsecondlife.TestClient Primitive closest = null; double closestDistance = Double.MaxValue; - lock (Client.Network.CurrentSim.Objects.Prims) - { - foreach (Primitive p in Client.Network.CurrentSim.Objects.Prims.Values) + Client.Network.CurrentSim.Objects.ForEach( + delegate(Primitive prim) { - float distance = Helpers.VecDist(Client.Self.Position, p.Position); + float distance = Helpers.VecDist(Client.Self.Position, prim.Position); if (closest == null || distance < closestDistance) { - closest = p; + closest = prim; closestDistance = distance; } } - } + ); if (closest != null) { diff --git a/libsecondlife/examples/TestClient/Commands/Movement/SitOnCommand.cs b/libsecondlife/examples/TestClient/Commands/Movement/SitOnCommand.cs index 75baefe2..d2279e31 100644 --- a/libsecondlife/examples/TestClient/Commands/Movement/SitOnCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/Movement/SitOnCommand.cs @@ -23,18 +23,19 @@ namespace libsecondlife.TestClient if (LLUUID.TryParse(args[0], out target)) { - lock (Client.Network.CurrentSim.Objects.Prims) - { - foreach (Primitive prim in Client.Network.CurrentSim.Objects.Prims.Values) + Primitive targetPrim = Client.Network.CurrentSim.Objects.Find( + delegate(Primitive prim) { - if (prim.ID == target) - { - Client.Self.RequestSit(prim.ID, LLVector3.Zero); - Client.Self.Sit(); - return "Requested to sit on prim " + prim.ID.ToStringHyphenated() + - " (" + prim.LocalID + ")"; - } + return prim.ID == target; } + ); + + if (targetPrim != null) + { + Client.Self.RequestSit(targetPrim.ID, LLVector3.Zero); + Client.Self.Sit(); + return "Requested to sit on prim " + targetPrim.ID.ToStringHyphenated() + + " (" + targetPrim.LocalID + ")"; } } diff --git a/libsecondlife/examples/TestClient/Commands/Prims/ExportCommand.cs b/libsecondlife/examples/TestClient/Commands/Prims/ExportCommand.cs index 71a40006..d9900ee9 100644 --- a/libsecondlife/examples/TestClient/Commands/Prims/ExportCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/Prims/ExportCommand.cs @@ -35,8 +35,8 @@ namespace libsecondlife.TestClient return "Usage: export uuid outputfile.xml"; LLUUID id; - uint localid = 0; - int count = 0; + uint localid; + int count; string file; if (args.Length == 2) @@ -51,27 +51,25 @@ namespace libsecondlife.TestClient id = SelectedObject; } - lock (Client.Network.CurrentSim.Objects.Prims) - { - foreach (Primitive prim in Client.Network.CurrentSim.Objects.Prims.Values) - { - if (prim.ID == id) - { - if (prim.ParentID != 0) - localid = prim.ParentID; - else - localid = prim.LocalID; + Primitive exportPrim; - break; - } + exportPrim = Client.Network.CurrentSim.Objects.Find( + delegate(Primitive prim) + { + return prim.ID == id; } - } - - if (localid != 0) + ); + + if (exportPrim != null) { + if (exportPrim.ParentID != 0) + localid = exportPrim.ParentID; + else + localid = exportPrim.LocalID; + // Check for export permission first Client.Objects.RequestObjectPropertiesFamily(Client.Network.CurrentSim, id); - GotPermissionsEvent.WaitOne(8000, false); + GotPermissionsEvent.WaitOne(1000 * 10, false); if (!GotPermissions) { @@ -97,19 +95,13 @@ namespace libsecondlife.TestClient try { - List prims = new List(); - - lock (Client.Network.CurrentSim.Objects.Prims) - { - foreach (Primitive prim in Client.Network.CurrentSim.Objects.Prims.Values) + List prims = Client.Network.CurrentSim.Objects.FindAll( + delegate(Primitive prim) { - if (prim.LocalID == localid || prim.ParentID == localid) - { - prims.Add(prim); - count++; - } - } - } + return (prim.LocalID == localid || prim.ParentID == localid); + } + ); + count = prims.Count; bool complete = RequestObjectProperties(prims, 250); @@ -141,7 +133,7 @@ namespace libsecondlife.TestClient else { return "Couldn't find UUID " + id.ToString() + " in the " + - Client.Network.CurrentSim.Objects.Prims.Count + + Client.Network.CurrentSim.Objects.PrimCount + "objects currently indexed in the current simulator"; } } diff --git a/libsecondlife/examples/TestClient/Commands/Prims/ExportParticlesCommand.cs b/libsecondlife/examples/TestClient/Commands/Prims/ExportParticlesCommand.cs index 5081337a..6dab8068 100644 --- a/libsecondlife/examples/TestClient/Commands/Prims/ExportParticlesCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/Prims/ExportParticlesCommand.cs @@ -23,16 +23,25 @@ namespace libsecondlife.TestClient if (!LLUUID.TryParse(args[0], out id)) return "Usage: exportparticles [prim-uuid]"; - lock (Client.Network.CurrentSim.Objects.Prims) + lock (Client.Network.Simulators) { - foreach (Primitive prim in Client.Network.CurrentSim.Objects.Prims.Values) + for (int i = 0; i < Client.Network.Simulators.Count; i++) { - if (prim.ID == id) + Primitive exportPrim = Client.Network.Simulators[i].Objects.Find( + delegate(Primitive prim) + { + return prim.ID == id; + } + ); + + if (exportPrim != null) { - if (prim.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); @@ -41,70 +50,72 @@ namespace libsecondlife.TestClient lsl.Append(" PSYS_PART_FLAGS, 0"); - if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.InterpColor) != 0) + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.InterpColor) != 0) lsl.Append(" | PSYS_PART_INTERP_COLOR_MASK"); - if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.InterpScale) != 0) + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.InterpScale) != 0) lsl.Append(" | PSYS_PART_INTERP_SCALE_MASK"); - if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Bounce) != 0) + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Bounce) != 0) lsl.Append(" | PSYS_PART_BOUNCE_MASK"); - if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Wind) != 0) + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Wind) != 0) lsl.Append(" | PSYS_PART_WIND_MASK"); - if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.FollowSrc) != 0) + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.FollowSrc) != 0) lsl.Append(" | PSYS_PART_FOLLOW_SRC_MASK"); - if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.FollowVelocity) != 0) + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.FollowVelocity) != 0) lsl.Append(" | PSYS_PART_FOLLOW_VELOCITY_MASK"); - if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.TargetPos) != 0) + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.TargetPos) != 0) lsl.Append(" | PSYS_PART_TARGET_POS_MASK"); - if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.TargetLinear) != 0) + if ((exportPrim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.TargetLinear) != 0) lsl.Append(" | PSYS_PART_TARGET_LINEAR_MASK"); - if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Emissive) != 0) + 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 ((prim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Drop) != 0) + if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Drop) != 0) lsl.Append(" | PSYS_SRC_PATTERN_DROP"); - if ((prim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Explode) != 0) + if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Explode) != 0) lsl.Append(" | PSYS_SRC_PATTERN_EXPLODE"); - if ((prim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Angle) != 0) + if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Angle) != 0) lsl.Append(" | PSYS_SRC_PATTERN_ANGLE"); - if ((prim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.AngleCone) != 0) + if ((exportPrim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.AngleCone) != 0) lsl.Append(" | PSYS_SRC_PATTERN_ANGLE_CONE"); - if ((prim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.AngleConeEmpty) != 0) + 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, " + String.Format("{0:0.00000}", prim.ParticleSys.PartStartColor.A) + "," + Environment.NewLine); - lsl.Append(" PSYS_PART_END_ALPHA, " + String.Format("{0:0.00000}", prim.ParticleSys.PartEndColor.A) + "," + Environment.NewLine); - lsl.Append(" PSYS_PART_START_COLOR, " + prim.ParticleSys.PartStartColor.ToStringRGB() + "," + Environment.NewLine); - lsl.Append(" PSYS_PART_END_COLOR, " + prim.ParticleSys.PartEndColor.ToStringRGB() + "," + Environment.NewLine); - lsl.Append(" PSYS_PART_START_SCALE, <" + String.Format("{0:0.00000}", prim.ParticleSys.PartStartScaleX) + ", " + String.Format("{0:0.00000}", prim.ParticleSys.PartStartScaleY) + ", 0>, " + Environment.NewLine); - lsl.Append(" PSYS_PART_END_SCALE, <" + String.Format("{0:0.00000}", prim.ParticleSys.PartEndScaleX) + ", " + String.Format("{0:0.00000}", prim.ParticleSys.PartEndScaleY) + ", 0>, " + Environment.NewLine); - lsl.Append(" PSYS_PART_MAX_AGE, " + String.Format("{0:0.00000}", prim.ParticleSys.PartMaxAge) + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_MAX_AGE, " + String.Format("{0:0.00000}", prim.ParticleSys.MaxAge) + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_ACCEL, " + prim.ParticleSys.PartAcceleration.ToString() + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_BURST_PART_COUNT, " + String.Format("{0:0}", prim.ParticleSys.BurstPartCount) + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_BURST_RADIUS, " + String.Format("{0:0.00000}", prim.ParticleSys.BurstRadius) + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_BURST_RATE, " + String.Format("{0:0.00000}", prim.ParticleSys.BurstRate) + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_BURST_SPEED_MIN, " + String.Format("{0:0.00000}", prim.ParticleSys.BurstSpeedMin) + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_BURST_SPEED_MAX, " + String.Format("{0:0.00000}", prim.ParticleSys.BurstSpeedMax) + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_INNERANGLE, " + String.Format("{0:0.00000}", prim.ParticleSys.InnerAngle) + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_OUTERANGLE, " + String.Format("{0:0.00000}", prim.ParticleSys.OuterAngle) + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_OMEGA, " + prim.ParticleSys.AngularVelocity.ToString() + "," + Environment.NewLine); - lsl.Append(" PSYS_SRC_TEXTURE, (key)\"" + prim.ParticleSys.Texture.ToStringHyphenated() + "\"," + Environment.NewLine); - lsl.Append(" PSYS_SRC_TARGET_KEY, (key)\"" + prim.ParticleSys.Target.ToStringHyphenated() + "\"" + Environment.NewLine); - + lsl.Append(" PSYS_PART_START_ALPHA, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartStartColor.A) + "," + Environment.NewLine); + lsl.Append(" PSYS_PART_END_ALPHA, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartEndColor.A) + "," + Environment.NewLine); + lsl.Append(" PSYS_PART_START_COLOR, " + exportPrim.ParticleSys.PartStartColor.ToStringRGB() + "," + Environment.NewLine); + lsl.Append(" PSYS_PART_END_COLOR, " + exportPrim.ParticleSys.PartEndColor.ToStringRGB() + "," + Environment.NewLine); + lsl.Append(" PSYS_PART_START_SCALE, <" + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartStartScaleX) + ", " + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartStartScaleY) + ", 0>, " + Environment.NewLine); + lsl.Append(" PSYS_PART_END_SCALE, <" + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartEndScaleX) + ", " + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartEndScaleY) + ", 0>, " + Environment.NewLine); + lsl.Append(" PSYS_PART_MAX_AGE, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.PartMaxAge) + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_MAX_AGE, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.MaxAge) + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_ACCEL, " + exportPrim.ParticleSys.PartAcceleration.ToString() + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_BURST_PART_COUNT, " + String.Format("{0:0}", exportPrim.ParticleSys.BurstPartCount) + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_BURST_RADIUS, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.BurstRadius) + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_BURST_RATE, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.BurstRate) + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_BURST_SPEED_MIN, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.BurstSpeedMin) + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_BURST_SPEED_MAX, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.BurstSpeedMax) + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_INNERANGLE, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.InnerAngle) + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_OUTERANGLE, " + String.Format("{0:0.00000}", exportPrim.ParticleSys.OuterAngle) + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_OMEGA, " + exportPrim.ParticleSys.AngularVelocity.ToString() + "," + Environment.NewLine); + lsl.Append(" PSYS_SRC_TEXTURE, (key)\"" + exportPrim.ParticleSys.Texture.ToStringHyphenated() + "\"," + Environment.NewLine); + lsl.Append(" PSYS_SRC_TARGET_KEY, (key)\"" + exportPrim.ParticleSys.Target.ToStringHyphenated() + "\"" + 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 " + prim.LocalID + " does not have a particle system"; + return "Prim " + exportPrim.LocalID + " does not have a particle system"; } } } diff --git a/libsecondlife/examples/TestClient/Commands/Prims/PrimCountCommand.cs b/libsecondlife/examples/TestClient/Commands/Prims/PrimCountCommand.cs index 0ac24faf..2367a44d 100644 --- a/libsecondlife/examples/TestClient/Commands/Prims/PrimCountCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/Prims/PrimCountCommand.cs @@ -22,13 +22,14 @@ namespace libsecondlife.TestClient { for (int i = 0; i < Client.Network.Simulators.Count; i++) { - Console.WriteLine("{0} (Avatars: {1} Primitives: {2})", - Client.Network.Simulators[i].Name, - Client.Network.Simulators[i].Objects.Avatars.Count, - Client.Network.Simulators[i].Objects.Prims.Count); + int avcount = Client.Network.Simulators[i].Objects.AvatarCount; + int primcount = Client.Network.Simulators[i].Objects.PrimCount; - count += Client.Network.Simulators[i].Objects.Avatars.Count; - count += Client.Network.Simulators[i].Objects.Prims.Count; + Console.WriteLine("{0} (Avatars: {1} Primitives: {2})", + Client.Network.Simulators[i].Name, avcount, primcount); + + count += avcount; + count += primcount; } } diff --git a/libsecondlife/examples/TestClient/Commands/System/SetMasterCommand.cs b/libsecondlife/examples/TestClient/Commands/System/SetMasterCommand.cs index 49039dd8..393c0094 100644 --- a/libsecondlife/examples/TestClient/Commands/System/SetMasterCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/System/SetMasterCommand.cs @@ -48,26 +48,11 @@ namespace libsecondlife.TestClient return "Unable to obtain UUID for \"" + masterName + "\". Master unchanged."; } - // If we see this avatar standing around, IM them. Otherwise don't bother - // because they may be offline - lock (Client.Network.Simulators) - { - for (int i = 0; i < Client.Network.Simulators.Count; i++) - { - lock (Client.Network.Simulators[i].Objects.Avatars) - { - foreach (Avatar avatar in Client.Network.Simulators[i].Objects.Avatars.Values) - { - if (avatar.ID == Client.MasterKey) - { - Client.Self.InstantMessage(avatar.ID, - "You are now my master. IM me with \"help\" for a command list."); - break; - } - } - } - } - } + // Send an Online-only IM to the new master + Client.Self.InstantMessage(Client.Self.Name, Client.MasterKey, + "You are now my master. IM me with \"help\" for a command list.", LLUUID.Random(), + InstantMessageDialog.MessageFromAgent, InstantMessageOnline.Online, Client.Self.Position, + Client.Network.CurrentSim.ID, new byte[0]); return String.Format("Master set to {0} ({1})", masterName, Client.MasterKey.ToStringHyphenated()); } diff --git a/libsecondlife/examples/TestClient/Commands/System/SetMasterKeyCommand.cs b/libsecondlife/examples/TestClient/Commands/System/SetMasterKeyCommand.cs index 837eaccd..574f7b98 100644 --- a/libsecondlife/examples/TestClient/Commands/System/SetMasterKeyCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/System/SetMasterKeyCommand.cs @@ -24,17 +24,18 @@ namespace libsecondlife.TestClient { for (int i = 0; i < Client.Network.Simulators.Count; i++) { - lock (Client.Network.Simulators[i].Objects.Avatars) - { - foreach (Avatar avatar in Client.Network.Simulators[i].Objects.Avatars.Values) + Avatar master = Client.Network.Simulators[i].Objects.Find( + delegate(Avatar avatar) { - if (avatar.ID == Client.MasterKey) - { - Client.Self.InstantMessage(avatar.ID, - "You are now my master. IM me with \"help\" for a command list."); - break; - } + return 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; } } } diff --git a/libsecondlife/examples/TestClient/Commands/TouchCommand.cs b/libsecondlife/examples/TestClient/Commands/TouchCommand.cs index c5cc59bc..cdd9e095 100644 --- a/libsecondlife/examples/TestClient/Commands/TouchCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/TouchCommand.cs @@ -23,16 +23,17 @@ namespace libsecondlife.TestClient if (LLUUID.TryParse(args[0], out target)) { - lock (Client.Network.CurrentSim.Objects.Prims) - { - foreach (Primitive prim in Client.Network.CurrentSim.Objects.Prims.Values) + Primitive targetPrim = Client.Network.CurrentSim.Objects.Find( + delegate(Primitive prim) { - if (prim.ID == target) - { - Client.Self.Touch(prim.LocalID); - return "Touched prim " + prim.LocalID; - } + return prim.ID == target; } + ); + + if (targetPrim != null) + { + Client.Self.Touch(targetPrim.LocalID); + return "Touched prim " + targetPrim.LocalID; } } diff --git a/libsecondlife/examples/TestClient/Commands/WhoCommand.cs b/libsecondlife/examples/TestClient/Commands/WhoCommand.cs index 87950ed7..e19507f5 100644 --- a/libsecondlife/examples/TestClient/Commands/WhoCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/WhoCommand.cs @@ -22,16 +22,15 @@ namespace libsecondlife.TestClient { for (int i = 0; i < Client.Network.Simulators.Count; i++) { - lock (Client.Network.Simulators[i].Objects.Avatars) - { - foreach (Avatar av in Client.Network.Simulators[i].Objects.Avatars.Values) + Client.Network.Simulators[i].Objects.ForEach( + delegate(Avatar av) { result.AppendLine(); - result.AppendFormat("{0} (Group: {1}, Location: {2}/{3}, UUID: {4})", av.Name, - av.GroupName, (av.CurrentSim != null ? av.CurrentSim.Name : String.Empty), + result.AppendFormat("{0} (Group: {1}, Location: {2}/{3}, UUID: {4})", av.Name, + av.GroupName, (av.CurrentSim != null ? av.CurrentSim.Name : String.Empty), av.Position, av.ID.ToStringHyphenated()); } - } + ); } } diff --git a/libsecondlife/examples/TestClient/Parsing.cs b/libsecondlife/examples/TestClient/Parsing.cs index 1e081c1c..d54d8190 100644 --- a/libsecondlife/examples/TestClient/Parsing.cs +++ b/libsecondlife/examples/TestClient/Parsing.cs @@ -2,59 +2,86 @@ using System; using System.Collections.Generic; using System.Text; -namespace libsecondlife.TestClient { - class Parsing { - public static string[] ParseArguments(string str) { +namespace libsecondlife.TestClient +{ + class Parsing + { + public static string[] ParseArguments(string str) + { List list = new List(); - string current = ""; + string current = String.Empty; string trimmed = null; bool withinQuote = false; bool escaped = false; - foreach (char c in str) { - if (c == '"') { - if (escaped) { + + foreach (char c in str) + { + if (c == '"') + { + if (escaped) + { current += '"'; escaped = false; - } else { + } + else + { current += '"'; withinQuote = !withinQuote; } - } else if (c == ' ' || c == '\t') { - if (escaped || withinQuote) { + } + else if (c == ' ' || c == '\t') + { + if (escaped || withinQuote) + { current += c; escaped = false; - } else { + } + else + { trimmed = current.Trim(); - if (trimmed.StartsWith("\"") && trimmed.EndsWith("\"")) { + if (trimmed.StartsWith("\"") && trimmed.EndsWith("\"")) + { trimmed = trimmed.Remove(0, 1); trimmed = trimmed.Remove(trimmed.Length - 1); trimmed = trimmed.Trim(); } if (trimmed.Length > 0) list.Add(trimmed); - current = ""; + current = String.Empty; } - } else if (c == '\\') { - if (escaped) { + } + else if (c == '\\') + { + if (escaped) + { current += '\\'; escaped = false; - } else { + } + else + { escaped = true; } - } else { + } + else + { if (escaped) throw new FormatException(c.ToString() + " is not an escapable character."); current += c; } } + trimmed = current.Trim(); - if (trimmed.StartsWith("\"") && trimmed.EndsWith("\"")) { + + if (trimmed.StartsWith("\"") && trimmed.EndsWith("\"")) + { trimmed = trimmed.Remove(0, 1); trimmed = trimmed.Remove(trimmed.Length - 1); trimmed = trimmed.Trim(); } + if (trimmed.Length > 0) list.Add(trimmed); + return list.ToArray(); } } diff --git a/libsecondlife/examples/TestClient/TestClient.cs b/libsecondlife/examples/TestClient/TestClient.cs index 9a9e48ac..123ec0fa 100644 --- a/libsecondlife/examples/TestClient/TestClient.cs +++ b/libsecondlife/examples/TestClient/TestClient.cs @@ -105,11 +105,14 @@ namespace libsecondlife.TestClient public void DoCommand(string cmd, LLUUID fromAgentID, LLUUID imSessionID) { - string[] tokens = Parsing.ParseArguments(cmd); + string[] tokens; + + try { tokens = Parsing.ParseArguments(cmd); } + catch (FormatException ex) { Console.WriteLine(ex.Message); return; } if (tokens.Length == 0) return; - + string firstToken = tokens[0].ToLower(); // "all balance" will send the balance command to all currently logged in bots diff --git a/libsecondlife/examples/TestClient/TestClient.csproj b/libsecondlife/examples/TestClient/TestClient.csproj index 08438e26..c2b31d01 100644 --- a/libsecondlife/examples/TestClient/TestClient.csproj +++ b/libsecondlife/examples/TestClient/TestClient.csproj @@ -45,6 +45,7 @@ +