* 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:
@@ -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(),
|
||||
|
||||
@@ -923,7 +923,7 @@ namespace libsecondlife
|
||||
/// <param name="notice"></param>
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -917,7 +917,7 @@ namespace libsecondlife
|
||||
/// <param name="item">InventoryObject object containing item details</param>
|
||||
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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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;
|
||||
|
||||
@@ -595,9 +595,9 @@ namespace libsecondlife
|
||||
// FIXME: Most all of these should change from public variables to read-only public properties
|
||||
|
||||
/// <summary>Your (client) avatar UUID</summary>
|
||||
public LLUUID ID = LLUUID.Zero;
|
||||
public LLUUID ID;
|
||||
/// <summary>Your (client) avatar ID, local to the current region/sim</summary>
|
||||
public uint LocalID = 0;
|
||||
public uint LocalID;
|
||||
/// <summary>Where the avatar started at login. Can be "last", "home"
|
||||
/// or a login URI</summary>
|
||||
public string StartLocation = String.Empty;
|
||||
@@ -606,46 +606,58 @@ namespace libsecondlife
|
||||
/// <summary>Positive and negative ratings</summary>
|
||||
/// <remarks>This information is read-only and any changes will not be
|
||||
/// reflected on the server</remarks>
|
||||
public Avatar.Statistics ProfileStatistics = new Avatar.Statistics();
|
||||
public Avatar.Statistics ProfileStatistics;
|
||||
/// <summary>Avatar properties including about text, profile URL, image IDs and
|
||||
/// publishing settings</summary>
|
||||
/// <remarks>If you change fields in this struct, the changes will not
|
||||
/// be reflected on the server until you call SetAvatarInformation</remarks>
|
||||
public Avatar.AvatarProperties ProfileProperties = new Avatar.AvatarProperties();
|
||||
public Avatar.AvatarProperties ProfileProperties;
|
||||
/// <summary>Avatar interests including spoken languages, skills, and "want to"
|
||||
/// choices</summary>
|
||||
/// <remarks>If you change fields in this struct, the changes will not
|
||||
/// be reflected on the server until you call SetAvatarInformation</remarks>
|
||||
public Avatar.Interests ProfileInterests = new Avatar.Interests();
|
||||
public Avatar.Interests ProfileInterests;
|
||||
/// <summary>Current position of avatar</summary>
|
||||
public LLVector3 Position = LLVector3.Zero;
|
||||
public LLVector3 Position;
|
||||
/// <summary>Current rotation of avatar</summary>
|
||||
public LLQuaternion Rotation = LLQuaternion.Identity;
|
||||
/// <summary></summary>
|
||||
public LLVector4 CollisionPlane = LLVector4.Zero;
|
||||
public LLVector4 CollisionPlane;
|
||||
/// <summary></summary>
|
||||
public LLVector3 Velocity = LLVector3.Zero;
|
||||
public LLVector3 Velocity;
|
||||
/// <summary></summary>
|
||||
public LLVector3 Acceleration = LLVector3.Zero;
|
||||
public LLVector3 Acceleration;
|
||||
/// <summary></summary>
|
||||
public LLVector3 AngularVelocity = LLVector3.Zero;
|
||||
public LLVector3 AngularVelocity;
|
||||
/// <summary>The point the avatar is currently looking at
|
||||
/// (may not stay updated)</summary>
|
||||
public LLVector3 LookAt = LLVector3.Zero;
|
||||
public LLVector3 LookAt;
|
||||
/// <summary>Position avatar client will goto when login to 'home' or during
|
||||
/// teleport request to 'home' region.</summary>
|
||||
public LLVector3 HomePosition = LLVector3.Zero;
|
||||
public LLVector3 HomePosition;
|
||||
/// <summary>LookAt point saved/restored with HomePosition</summary>
|
||||
public LLVector3 HomeLookAt = LLVector3.Zero;
|
||||
public LLVector3 HomeLookAt;
|
||||
/// <summary>Used for camera and control key state tracking</summary>
|
||||
public MainAvatarStatus Status;
|
||||
/// <summary>The UUID of your root inventory folder</summary>
|
||||
public LLUUID InventoryRootFolderUUID = LLUUID.Zero;
|
||||
public LLUUID InventoryRootFolderUUID;
|
||||
|
||||
/// <summary>Avatar First Name (i.e. Philip)</summary>
|
||||
public string FirstName { get { return firstName; } }
|
||||
/// <summary>Avatar Last Name (i.e. Linden)</summary>
|
||||
public string LastName { get { return lastName; } }
|
||||
/// <summary>Avatar Full Name (i.e. Philip Linden)</summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
/// <summary>Gets the health of the agent</summary>
|
||||
public float Health { get { return health; } }
|
||||
/// <summary>Gets the current balance of the agent</summary>
|
||||
@@ -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
|
||||
/// <param name="message">Text message being sent</param>
|
||||
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
|
||||
/// <param name="imSessionID">IM session ID (to differentiate between IM windows)</param>
|
||||
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
|
||||
/// <param name="message">Text Message being sent.</param>
|
||||
public void InstantMessageGroup(LLUUID groupUUID, string message)
|
||||
{
|
||||
InstantMessageGroup(Client.ToString(), groupUUID, message);
|
||||
InstantMessageGroup(Name, groupUUID, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1657,7 +1672,7 @@ namespace libsecondlife
|
||||
/// <param name="accept">Accept the teleport request or deny it</param>
|
||||
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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for parsing llDialog's
|
||||
/// Used for parsing llDialogs
|
||||
/// </summary>
|
||||
/// <param name="packet">Incoming ScriptDialog packet</param>
|
||||
/// <param name="simulator">Unused</param>
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,128 @@ namespace libsecondlife
|
||||
{
|
||||
public class ObjectTracker
|
||||
{
|
||||
public Dictionary<uint, Avatar> Avatars = new Dictionary<uint, Avatar>();
|
||||
public Dictionary<uint, Primitive> Prims = new Dictionary<uint, Primitive>();
|
||||
internal Dictionary<uint, Avatar> Avatars = new Dictionary<uint, Avatar>();
|
||||
internal Dictionary<uint, Primitive> Prims = new Dictionary<uint, Primitive>();
|
||||
|
||||
#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<Primitive> FindAll(Predicate<Primitive> match)
|
||||
{
|
||||
List<Primitive> found = new List<Primitive>();
|
||||
lock (Prims)
|
||||
{
|
||||
foreach (KeyValuePair<uint, Primitive> kvp in Prims)
|
||||
{
|
||||
if (match(kvp.Value))
|
||||
found.Add(kvp.Value);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
public List<Avatar> FindAll(Predicate<Avatar> match)
|
||||
{
|
||||
List<Avatar> found = new List<Avatar>();
|
||||
lock (Avatars)
|
||||
{
|
||||
foreach (KeyValuePair<uint, Avatar> kvp in Avatars)
|
||||
{
|
||||
if (match(kvp.Value))
|
||||
found.Add(kvp.Value);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
public Primitive Find(Predicate<Primitive> match)
|
||||
{
|
||||
lock (Prims)
|
||||
{
|
||||
foreach (Primitive prim in Prims.Values)
|
||||
{
|
||||
if (match(prim))
|
||||
return prim;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Avatar Find(Predicate<Avatar> match)
|
||||
{
|
||||
lock (Avatars)
|
||||
{
|
||||
foreach (Avatar avatar in Avatars.Values)
|
||||
{
|
||||
if (match(avatar))
|
||||
return avatar;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ForEach(Action<Primitive> action)
|
||||
{
|
||||
lock (Avatars)
|
||||
{
|
||||
foreach (Primitive prim in Prims.Values)
|
||||
{
|
||||
action(prim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ForEach(Action<Avatar> action)
|
||||
{
|
||||
lock (Prims)
|
||||
{
|
||||
foreach (Avatar avatar in Avatars.Values)
|
||||
{
|
||||
action(avatar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace libsecondlife
|
||||
/// <returns>Client avatars full name</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return Self.FirstName + " " + Self.LastName;
|
||||
return Self.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 + ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user