* 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
This commit is contained in:
John Hurliman
2007-08-29 08:55:53 +00:00
parent 92e460a066
commit 349830c983
21 changed files with 777 additions and 246 deletions

View File

@@ -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
{
/// <summary>Maximum number of transfer requests to send to the server</summary>
private const int MAX_TRANSFERS = 10;
// all items here, fed by the inventory walking thread
private Queue<QueuedDownloadInfo> PendingDownloads = new Queue<QueuedDownloadInfo>();
// items sent to the server here
private List<QueuedDownloadInfo> CurrentDownloads = new List<QueuedDownloadInfo>(MAX_TRANSFERS);
// background workers
private BackgroundWorker BackupWorker;
private BackgroundWorker QueueWorker;
// some stats
private int TextItemsFound;
private int TextItemsTransferred;
private int TextItemErrors;
#region Properties
/// <summary>
/// true if either of the background threads is running
/// </summary>
private bool BackgroundBackupRunning
{
get { return InventoryWalkerRunning || QueueRunnerRunning; }
}
/// <summary>
/// true if the thread walking inventory is running
/// </summary>
private bool InventoryWalkerRunning
{
get { return BackupWorker != null; }
}
/// <summary>
/// true if the thread feeding the queue to the server is running
/// </summary>
private bool QueueRunnerRunning
{
get { return QueueWorker != null; }
}
/// <summary>
/// returns a string summarizing activity
/// </summary>
/// <returns></returns>
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 <directory>] | [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 <directory>] | [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);
}
/// <summary>
/// BackupFolder - recurse through the inventory nodes sending scripts and notecards to the transfer queue
/// </summary>
/// <param name="folder">The current leaf in the inventory tree</param>
/// <param name="sPathSoFar">path so far, in the form @"c:\here" -- this needs to be "clean" for the current filesystem</param>
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());
}
}
}
/// <summary>
/// returns blank or "not" if false
/// </summary>
/// <param name="b"></param>
/// <returns></returns>
private static string BoolToNot(bool b)
{
return b ? String.Empty : "not";
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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)

View File

@@ -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)
{

View File

@@ -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 + ")";
}
}

View File

@@ -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<Primitive> prims = new List<Primitive>();
lock (Client.Network.CurrentSim.Objects.Prims)
{
foreach (Primitive prim in Client.Network.CurrentSim.Objects.Prims.Values)
List<Primitive> 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";
}
}

View File

@@ -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";
}
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}
);
}
}

View File

@@ -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<string> list = new List<string>();
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();
}
}

View File

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

View File

@@ -45,6 +45,7 @@
<Compile Include="Commands\GoHome.cs" />
<Compile Include="Commands\GotoLandmark.cs" />
<Compile Include="Commands\Inventory\AppearanceCommand.cs" />
<Compile Include="Commands\Inventory\BackupCommand.cs" />
<Compile Include="Commands\Inventory\BalanceCommand.cs" />
<Compile Include="Commands\Inventory\DeleteFolderCommand.cs" />
<Compile Include="Commands\Inventory\DumpOutfitCommand.cs" />