Brought in TestClient code for teh fork
parent
74321101e0
commit
5b6afeafbc
|
@ -0,0 +1,111 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace CommandLine.Utility
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Arguments class
|
||||||
|
/// </summary>
|
||||||
|
public class Arguments
|
||||||
|
{
|
||||||
|
// Variables
|
||||||
|
private StringDictionary Parameters;
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public Arguments(string[] Args)
|
||||||
|
{
|
||||||
|
Parameters = new StringDictionary();
|
||||||
|
Regex Splitter = new Regex(@"^-{1,2}|=|:",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
|
||||||
|
Regex Remover = new Regex(@"^['""]?(.*?)['""]?$",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
|
||||||
|
string Parameter = null;
|
||||||
|
string[] Parts;
|
||||||
|
|
||||||
|
// Valid parameters forms:
|
||||||
|
// {-,/,--}param{ ,=,:}((",')value(",'))
|
||||||
|
// Examples:
|
||||||
|
// -param1 value1 --param2 /param3:"Test-:-work"
|
||||||
|
// /param4=happy -param5 '--=nice=--'
|
||||||
|
foreach (string Txt in Args)
|
||||||
|
{
|
||||||
|
// Look for new parameters (-,/ or --) and a
|
||||||
|
// possible enclosed value (=,:)
|
||||||
|
Parts = Splitter.Split(Txt, 3);
|
||||||
|
|
||||||
|
switch (Parts.Length)
|
||||||
|
{
|
||||||
|
// Found a value (for the last parameter
|
||||||
|
// found (space separator))
|
||||||
|
case 1:
|
||||||
|
if (Parameter != null)
|
||||||
|
{
|
||||||
|
if (!Parameters.ContainsKey(Parameter))
|
||||||
|
{
|
||||||
|
Parts[0] =
|
||||||
|
Remover.Replace(Parts[0], "$1");
|
||||||
|
|
||||||
|
Parameters.Add(Parameter, Parts[0]);
|
||||||
|
}
|
||||||
|
Parameter = null;
|
||||||
|
}
|
||||||
|
// else Error: no parameter waiting for a value (skipped)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Found just a parameter
|
||||||
|
case 2:
|
||||||
|
// The last parameter is still waiting.
|
||||||
|
// With no value, set it to true.
|
||||||
|
if (Parameter != null)
|
||||||
|
{
|
||||||
|
if (!Parameters.ContainsKey(Parameter))
|
||||||
|
Parameters.Add(Parameter, "true");
|
||||||
|
}
|
||||||
|
Parameter = Parts[1];
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Parameter with enclosed value
|
||||||
|
case 3:
|
||||||
|
// The last parameter is still waiting.
|
||||||
|
// With no value, set it to true.
|
||||||
|
if (Parameter != null)
|
||||||
|
{
|
||||||
|
if (!Parameters.ContainsKey(Parameter))
|
||||||
|
Parameters.Add(Parameter, "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
Parameter = Parts[1];
|
||||||
|
|
||||||
|
// Remove possible enclosing characters (",')
|
||||||
|
if (!Parameters.ContainsKey(Parameter))
|
||||||
|
{
|
||||||
|
Parts[2] = Remover.Replace(Parts[2], "$1");
|
||||||
|
Parameters.Add(Parameter, Parts[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Parameter = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// In case a parameter is still waiting
|
||||||
|
if (Parameter != null)
|
||||||
|
{
|
||||||
|
if (!Parameters.ContainsKey(Parameter))
|
||||||
|
Parameters.Add(Parameter, "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve a parameter value if it exists
|
||||||
|
// (overriding C# indexer property)
|
||||||
|
public string this[string Param]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (Parameters[Param]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,307 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Threading;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
using libsecondlife.AssetSystem;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class LoginDetails
|
||||||
|
{
|
||||||
|
public string FirstName;
|
||||||
|
public string LastName;
|
||||||
|
public string Password;
|
||||||
|
public string StartLocation;
|
||||||
|
public string MasterName;
|
||||||
|
public LLUUID MasterKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StartPosition
|
||||||
|
{
|
||||||
|
public string sim;
|
||||||
|
public int x;
|
||||||
|
public int y;
|
||||||
|
public int z;
|
||||||
|
|
||||||
|
public StartPosition()
|
||||||
|
{
|
||||||
|
this.sim = null;
|
||||||
|
this.x = 0;
|
||||||
|
this.y = 0;
|
||||||
|
this.z = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ClientManager
|
||||||
|
{
|
||||||
|
public Dictionary<LLUUID, SecondLife> Clients = new Dictionary<LLUUID, SecondLife>();
|
||||||
|
public Dictionary<Simulator, Dictionary<uint, Primitive>> SimPrims = new Dictionary<Simulator, Dictionary<uint, Primitive>>();
|
||||||
|
|
||||||
|
public bool Running = true;
|
||||||
|
|
||||||
|
string contactPerson = String.Empty;
|
||||||
|
private LLUUID resolvedMasterKey = LLUUID.Zero;
|
||||||
|
private ManualResetEvent keyResolution = new ManualResetEvent(false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="accounts"></param>
|
||||||
|
public ClientManager(List<LoginDetails> accounts, string c)
|
||||||
|
{
|
||||||
|
this.contactPerson = c;
|
||||||
|
foreach (LoginDetails account in accounts)
|
||||||
|
Login(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientManager(List<LoginDetails> accounts, string c, string s)
|
||||||
|
{
|
||||||
|
this.contactPerson = c;
|
||||||
|
char sep = '/';
|
||||||
|
string[] startbits = s.Split(sep);
|
||||||
|
|
||||||
|
foreach (LoginDetails account in accounts)
|
||||||
|
{
|
||||||
|
account.StartLocation = NetworkManager.StartLocation(startbits[0], Int32.Parse(startbits[1]),
|
||||||
|
Int32.Parse(startbits[2]), Int32.Parse(startbits[3]));
|
||||||
|
Login(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="account"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public TestClient Login(LoginDetails account)
|
||||||
|
{
|
||||||
|
// Check if this client is already logged in
|
||||||
|
foreach (TestClient c in Clients.Values)
|
||||||
|
{
|
||||||
|
if (c.Self.FirstName == account.FirstName && c.Self.LastName == account.LastName)
|
||||||
|
{
|
||||||
|
Logout(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestClient client = new TestClient(this);
|
||||||
|
|
||||||
|
// Optimize the throttle
|
||||||
|
client.Throttle.Wind = 0;
|
||||||
|
client.Throttle.Cloud = 0;
|
||||||
|
client.Throttle.Land = 1000000;
|
||||||
|
client.Throttle.Task = 1000000;
|
||||||
|
|
||||||
|
client.SimPrims = SimPrims;
|
||||||
|
client.MasterName = account.MasterName;
|
||||||
|
client.MasterKey = account.MasterKey;
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(account.StartLocation))
|
||||||
|
{
|
||||||
|
if (!client.Network.Login(account.FirstName, account.LastName, account.Password, "TestClient",
|
||||||
|
account.StartLocation, contactPerson))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed to login " + account.FirstName + " " + account.LastName + ": " +
|
||||||
|
client.Network.LoginMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!client.Network.Login(account.FirstName, account.LastName, account.Password, "TestClient",
|
||||||
|
contactPerson))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed to login " + account.FirstName + " " + account.LastName + ": " +
|
||||||
|
client.Network.LoginStatusMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client.Network.Connected)
|
||||||
|
{
|
||||||
|
if (account.MasterKey == LLUUID.Zero && !String.IsNullOrEmpty(account.MasterName))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Resolving {0}'s UUID", account.MasterName);
|
||||||
|
// Find master's key from name
|
||||||
|
DirectoryManager.DirPeopleReplyCallback callback = new DirectoryManager.DirPeopleReplyCallback(KeyResolvHandler);
|
||||||
|
client.Directory.OnDirPeopleReply += callback;
|
||||||
|
client.Directory.StartPeopleSearch(DirectoryManager.DirFindFlags.People, account.MasterName);
|
||||||
|
if (keyResolution.WaitOne(TimeSpan.FromMinutes(1), false))
|
||||||
|
{
|
||||||
|
account.MasterKey = resolvedMasterKey;
|
||||||
|
Console.WriteLine("\"{0}\" resolved to {1}", account.MasterName, account.MasterKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Unable to obtain UUID for \"{0}\". No master will be used. Try specifying a key with --masterkey.", account.MasterName);
|
||||||
|
}
|
||||||
|
client.Directory.OnDirPeopleReply -= callback;
|
||||||
|
keyResolution.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
client.MasterKey = account.MasterKey;
|
||||||
|
|
||||||
|
Clients[client.Network.AgentID] = client;
|
||||||
|
|
||||||
|
Console.WriteLine("Logged in " + client.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void KeyResolvHandler(LLUUID queryid, List<DirectoryManager.AgentSearchData> matches)
|
||||||
|
{
|
||||||
|
LLUUID master = matches[0].AgentID;
|
||||||
|
if (matches.Count > 1)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Possible masters:");
|
||||||
|
for (int i = 0; i < matches.Count; ++i)
|
||||||
|
{
|
||||||
|
Console.WriteLine("{0}: {1}", i, matches[i].FirstName + " " + matches[i].LastName);
|
||||||
|
}
|
||||||
|
Console.Write("Ambiguous master, choose one:");
|
||||||
|
string read = Console.ReadLine();
|
||||||
|
while (read != null)
|
||||||
|
{
|
||||||
|
int choice = 0;
|
||||||
|
if (int.TryParse(read, out choice))
|
||||||
|
{
|
||||||
|
master = matches[choice].AgentID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Responce misunderstood.");
|
||||||
|
Console.Write("Type the corresponding number:");
|
||||||
|
}
|
||||||
|
read = Console.ReadLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolvedMasterKey = master;
|
||||||
|
keyResolution.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public TestClient Login(string[] args)
|
||||||
|
{
|
||||||
|
LoginDetails account = new LoginDetails();
|
||||||
|
account.FirstName = args[0];
|
||||||
|
account.LastName = args[1];
|
||||||
|
account.Password = args[2];
|
||||||
|
|
||||||
|
if (args.Length == 4)
|
||||||
|
{
|
||||||
|
account.StartLocation = NetworkManager.StartLocation(args[3], 128, 128, 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Login(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Type quit to exit. Type help for a command list.");
|
||||||
|
|
||||||
|
while (Running)
|
||||||
|
{
|
||||||
|
PrintPrompt();
|
||||||
|
string input = Console.ReadLine();
|
||||||
|
DoCommandAll(input, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (SecondLife client in Clients.Values)
|
||||||
|
{
|
||||||
|
if (client.Network.Connected)
|
||||||
|
client.Network.Logout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintPrompt()
|
||||||
|
{
|
||||||
|
int online = 0;
|
||||||
|
|
||||||
|
foreach (SecondLife client in Clients.Values)
|
||||||
|
{
|
||||||
|
if (client.Network.Connected) online++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.Write(online + " avatars online> ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd"></param>
|
||||||
|
/// <param name="fromAgentID"></param>
|
||||||
|
/// <param name="imSessionID"></param>
|
||||||
|
public void DoCommandAll(string cmd, LLUUID fromAgentID, LLUUID imSessionID)
|
||||||
|
{
|
||||||
|
string[] tokens = cmd.Trim().Split(new char[] { ' ', '\t' });
|
||||||
|
string firstToken = tokens[0].ToLower();
|
||||||
|
|
||||||
|
if (tokens.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (firstToken == "login")
|
||||||
|
{
|
||||||
|
// Special login case: Only call it once, and allow it with
|
||||||
|
// no logged in avatars
|
||||||
|
string[] args = new string[tokens.Length - 1];
|
||||||
|
Array.Copy(tokens, 1, args, 0, args.Length);
|
||||||
|
Login(args);
|
||||||
|
}
|
||||||
|
else if (firstToken == "quit")
|
||||||
|
{
|
||||||
|
Quit();
|
||||||
|
Console.WriteLine("All clients logged out and program finished running.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// make a copy of the clients list so that it can be iterated without fear of being changed during iteration
|
||||||
|
Dictionary<LLUUID, SecondLife> clientsCopy = new Dictionary<LLUUID, SecondLife>(Clients);
|
||||||
|
|
||||||
|
foreach (TestClient client in clientsCopy.Values)
|
||||||
|
client.DoCommand(cmd, fromAgentID, imSessionID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client"></param>
|
||||||
|
public void Logout(TestClient client)
|
||||||
|
{
|
||||||
|
Clients.Remove(client.Network.AgentID);
|
||||||
|
client.Network.Logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public void LogoutAll()
|
||||||
|
{
|
||||||
|
// make a copy of the clients list so that it can be iterated without fear of being changed during iteration
|
||||||
|
Dictionary<LLUUID, SecondLife> clientsCopy = new Dictionary<LLUUID, SecondLife>(Clients);
|
||||||
|
|
||||||
|
foreach (TestClient client in clientsCopy.Values)
|
||||||
|
Logout(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public void Quit()
|
||||||
|
{
|
||||||
|
LogoutAll();
|
||||||
|
Running = false;
|
||||||
|
// TODO: It would be really nice if we could figure out a way to abort the ReadLine here in so that Run() will exit.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public abstract class Command
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public string Description;
|
||||||
|
public TestClient Client;
|
||||||
|
|
||||||
|
public abstract string Execute(string[] args, LLUUID fromAgentID);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When set to true, think will be called.
|
||||||
|
/// </summary>
|
||||||
|
public bool Active;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called twice per second, when Command.Active is set to true.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void Think()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class AppearanceCommand : Command
|
||||||
|
{
|
||||||
|
Utilities.Assets.AssetManager Assets;
|
||||||
|
Utilities.Appearance.AppearanceManager Appearance;
|
||||||
|
|
||||||
|
public AppearanceCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "appearance";
|
||||||
|
Description = "Set your current appearance to your last saved appearance";
|
||||||
|
|
||||||
|
Assets = new libsecondlife.Utilities.Assets.AssetManager(testClient);
|
||||||
|
Appearance = new libsecondlife.Utilities.Appearance.AppearanceManager(testClient, Assets);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
Appearance.SetPreviousAppearance();
|
||||||
|
return "Done.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class CloneProfileCommand : Command
|
||||||
|
{
|
||||||
|
Avatar.AvatarProperties Properties;
|
||||||
|
Avatar.Interests Interests;
|
||||||
|
List<LLUUID> Groups = new List<LLUUID>();
|
||||||
|
bool ReceivedProperties = false;
|
||||||
|
bool ReceivedInterests = false;
|
||||||
|
bool ReceivedGroups = false;
|
||||||
|
ManualResetEvent ReceivedProfileEvent = new ManualResetEvent(false);
|
||||||
|
|
||||||
|
public CloneProfileCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
testClient.Avatars.OnAvatarInterests += new AvatarManager.AvatarInterestsCallback(Avatars_OnAvatarInterests);
|
||||||
|
testClient.Avatars.OnAvatarProperties += new AvatarManager.AvatarPropertiesCallback(Avatars_OnAvatarProperties);
|
||||||
|
testClient.Avatars.OnAvatarGroups += new AvatarManager.AvatarGroupsCallback(Avatars_OnAvatarGroups);
|
||||||
|
testClient.Self.OnJoinGroup += new MainAvatar.JoinGroupCallback(Self_OnJoinGroup);
|
||||||
|
|
||||||
|
Name = "cloneprofile";
|
||||||
|
Description = "Clones another avatars profile as closely as possible. WARNING: This command will " +
|
||||||
|
"destroy your existing profile! Usage: cloneprofile [targetuuid]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
return Description;
|
||||||
|
|
||||||
|
LLUUID targetID;
|
||||||
|
ReceivedProperties = false;
|
||||||
|
ReceivedInterests = false;
|
||||||
|
ReceivedGroups = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
targetID = new LLUUID(args[0]);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return Description;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request all of the packets that make up an avatar profile
|
||||||
|
Client.Avatars.RequestAvatarProperties(targetID);
|
||||||
|
|
||||||
|
// Wait for all the packets to arrive
|
||||||
|
ReceivedProfileEvent.Reset();
|
||||||
|
ReceivedProfileEvent.WaitOne(5000, false);
|
||||||
|
|
||||||
|
// Check if everything showed up
|
||||||
|
if (!ReceivedInterests || !ReceivedProperties || !ReceivedGroups)
|
||||||
|
return "Failed to retrieve a complete profile for that UUID";
|
||||||
|
|
||||||
|
// Synchronize our profile
|
||||||
|
Client.Self.ProfileInterests = Interests;
|
||||||
|
Client.Self.ProfileProperties = Properties;
|
||||||
|
Client.Self.SetAvatarInformation();
|
||||||
|
|
||||||
|
// TODO: Leave all the groups we're currently a member of? This could
|
||||||
|
// break TestClient connectivity that might be relying on group authentication
|
||||||
|
|
||||||
|
// Attempt to join all the groups
|
||||||
|
foreach (LLUUID groupID in Groups)
|
||||||
|
{
|
||||||
|
Client.Self.RequestJoinGroup(groupID);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Synchronized our profile to the profile of " + targetID.ToStringHyphenated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatars_OnAvatarProperties(LLUUID avatarID, Avatar.AvatarProperties properties)
|
||||||
|
{
|
||||||
|
lock (ReceivedProfileEvent)
|
||||||
|
{
|
||||||
|
Properties = properties;
|
||||||
|
ReceivedProperties = true;
|
||||||
|
|
||||||
|
if (ReceivedInterests && ReceivedProperties && ReceivedGroups)
|
||||||
|
ReceivedProfileEvent.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatars_OnAvatarInterests(LLUUID avatarID, Avatar.Interests interests)
|
||||||
|
{
|
||||||
|
lock (ReceivedProfileEvent)
|
||||||
|
{
|
||||||
|
Interests = interests;
|
||||||
|
ReceivedInterests = true;
|
||||||
|
|
||||||
|
if (ReceivedInterests && ReceivedProperties && ReceivedGroups)
|
||||||
|
ReceivedProfileEvent.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatars_OnAvatarGroups(LLUUID avatarID, AvatarGroupsReplyPacket.GroupDataBlock[] groups)
|
||||||
|
{
|
||||||
|
lock (ReceivedProfileEvent)
|
||||||
|
{
|
||||||
|
foreach (AvatarGroupsReplyPacket.GroupDataBlock block in groups)
|
||||||
|
{
|
||||||
|
Groups.Add(block.GroupID);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReceivedGroups = true;
|
||||||
|
|
||||||
|
if (ReceivedInterests && ReceivedProperties && ReceivedGroups)
|
||||||
|
ReceivedProfileEvent.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Self_OnJoinGroup(LLUUID groupID, bool success)
|
||||||
|
{
|
||||||
|
Console.WriteLine(Client.ToString() + (success ? " joined " : " failed to join ") +
|
||||||
|
groupID.ToStringHyphenated());
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
Console.WriteLine(Client.ToString() + " setting " + groupID.ToStringHyphenated() +
|
||||||
|
" as the active group");
|
||||||
|
Client.Self.ActivateGroup(groupID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class EchoMasterCommand: Command
|
||||||
|
{
|
||||||
|
public EchoMasterCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "echoMaster";
|
||||||
|
Description = "Repeat everything that master says.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (!Active)
|
||||||
|
{
|
||||||
|
Active = true;
|
||||||
|
Client.Self.OnChat += new MainAvatar.ChatCallback(Self_OnChat);
|
||||||
|
return "Echoing is now on.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Active = false;
|
||||||
|
Client.Self.OnChat -= new MainAvatar.ChatCallback(Self_OnChat);
|
||||||
|
return "Echoing is now off.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Self_OnChat(string message, MainAvatar.ChatAudibleLevel audible, MainAvatar.ChatType type,
|
||||||
|
MainAvatar.ChatSourceType sourcetype, string fromName, LLUUID id, LLUUID ownerid, LLVector3 position)
|
||||||
|
{
|
||||||
|
if (message.Length > 0 && Client.MasterKey == id)
|
||||||
|
{
|
||||||
|
Client.Self.Chat(message, 0, MainAvatar.ChatType.Normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class ImCommand : Command
|
||||||
|
{
|
||||||
|
string ToAvatarName = String.Empty;
|
||||||
|
ManualResetEvent NameSearchEvent = new ManualResetEvent(false);
|
||||||
|
Dictionary<string, LLUUID> Name2Key = new Dictionary<string, LLUUID>();
|
||||||
|
|
||||||
|
public ImCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
testClient.Avatars.OnAvatarNameSearch += new AvatarManager.AvatarNameSearchCallback(Avatars_OnAvatarNameSearch);
|
||||||
|
|
||||||
|
Name = "im";
|
||||||
|
Description = "Instant message someone. Usage: im [firstname] [lastname] [message]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length < 3)
|
||||||
|
return "Usage: im [firstname] [lastname] [message]";
|
||||||
|
|
||||||
|
ToAvatarName = args[0] + " " + args[1];
|
||||||
|
|
||||||
|
// Build the message
|
||||||
|
string message = String.Empty;
|
||||||
|
for (int ct = 2; ct < args.Length; ct++)
|
||||||
|
message += args[ct] + " ";
|
||||||
|
message = message.TrimEnd();
|
||||||
|
if (message.Length > 1023) message = message.Remove(1023);
|
||||||
|
|
||||||
|
if (!Name2Key.ContainsKey(ToAvatarName.ToLower()))
|
||||||
|
{
|
||||||
|
// Send the Query
|
||||||
|
Client.Avatars.RequestAvatarNameSearch(ToAvatarName, LLUUID.Random());
|
||||||
|
|
||||||
|
NameSearchEvent.WaitOne(6000, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Name2Key.ContainsKey(ToAvatarName.ToLower()))
|
||||||
|
{
|
||||||
|
LLUUID id = Name2Key[ToAvatarName.ToLower()];
|
||||||
|
|
||||||
|
Client.Self.InstantMessage(id, message, id);
|
||||||
|
return "Instant Messaged " + id.ToStringHyphenated() + " with message: " + message;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Name lookup for " + ToAvatarName + " failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatars_OnAvatarNameSearch(LLUUID queryID, Dictionary<LLUUID, string> avatars)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<LLUUID, string> kvp in avatars)
|
||||||
|
{
|
||||||
|
if (kvp.Value.ToLower() == ToAvatarName.ToLower())
|
||||||
|
{
|
||||||
|
Name2Key[ToAvatarName.ToLower()] = kvp.Key;
|
||||||
|
NameSearchEvent.Set();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class SayCommand: Command
|
||||||
|
{
|
||||||
|
public SayCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "say";
|
||||||
|
Description = "Say something. (usage: say (optional channel) whatever)";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
int channel = 0;
|
||||||
|
int startIndex = 0;
|
||||||
|
|
||||||
|
if (args.Length < 1)
|
||||||
|
{
|
||||||
|
return "usage: say (optional channel) whatever";
|
||||||
|
}
|
||||||
|
else if (args.Length > 1)
|
||||||
|
{
|
||||||
|
if (Int32.TryParse(args[0], out channel))
|
||||||
|
startIndex = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder message = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = startIndex; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
message.Append(args[i]);
|
||||||
|
if (i != args.Length - 1) message.Append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
Client.Self.Chat(message.ToString(), channel, MainAvatar.ChatType.Normal);
|
||||||
|
|
||||||
|
return "Said " + message.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class ShoutCommand : Command
|
||||||
|
{
|
||||||
|
public ShoutCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "shout";
|
||||||
|
Description = "Shout something.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
int channel = 0;
|
||||||
|
int startIndex = 0;
|
||||||
|
string message = String.Empty;
|
||||||
|
if (args.Length < 1)
|
||||||
|
{
|
||||||
|
return "usage: shout (optional channel) whatever";
|
||||||
|
}
|
||||||
|
else if (args.Length > 1)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
channel = Convert.ToInt32(args[0]);
|
||||||
|
startIndex = 1;
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
channel = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = startIndex; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
message += args[i] + " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
Client.Self.Chat(message, channel, MainAvatar.ChatType.Shout);
|
||||||
|
|
||||||
|
return "Shouted " + message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Speech.Synthesis;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
using libsecondlife.AssetSystem;
|
||||||
|
|
||||||
|
|
||||||
|
// Since this requires .Net 3.0 I've left it out of the project by default.
|
||||||
|
// To use this: include it in the project and add a reference to the System.Speech.dll
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class TtsCommand : Command
|
||||||
|
{
|
||||||
|
SpeechSynthesizer _speechSynthesizer;
|
||||||
|
|
||||||
|
public TtsCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "tts";
|
||||||
|
Description = "Text To Speech. When activated, client will echo all recieved chat messages out thru the computer's speakers.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (!Active)
|
||||||
|
{
|
||||||
|
if (_speechSynthesizer == null)
|
||||||
|
_speechSynthesizer = new SpeechSynthesizer();
|
||||||
|
Active = true;
|
||||||
|
Client.Self.OnChat += new MainAvatar.ChatCallback(Self_OnChat);
|
||||||
|
return "TTS is now on.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Active = false;
|
||||||
|
Client.Self.OnChat -= new MainAvatar.ChatCallback(Self_OnChat);
|
||||||
|
return "TTS is now off.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Self_OnChat(string message, byte audible, byte type, byte sourcetype, string fromName, LLUUID id, LLUUID ownerid, LLVector3 position)
|
||||||
|
{
|
||||||
|
if (message.Length > 0)
|
||||||
|
{
|
||||||
|
_speechSynthesizer.SpeakAsync(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class WhisperCommand : Command
|
||||||
|
{
|
||||||
|
public WhisperCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "whisper";
|
||||||
|
Description = "Whisper something.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
int channel = 0;
|
||||||
|
int startIndex = 0;
|
||||||
|
string message = String.Empty;
|
||||||
|
if (args.Length < 1)
|
||||||
|
{
|
||||||
|
return "usage: whisper (optional channel) whatever";
|
||||||
|
}
|
||||||
|
else if (args.Length > 1)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
channel = Convert.ToInt32(args[0]);
|
||||||
|
startIndex = 1;
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
channel = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = startIndex; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
message += args[i] + " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
Client.Self.Chat(message, channel, MainAvatar.ChatType.Whisper);
|
||||||
|
|
||||||
|
return "Whispered " + message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class DebugCommand : Command
|
||||||
|
{
|
||||||
|
public DebugCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "debug";
|
||||||
|
Description = "Turn debug messages on or off. Usage: debug [on/off]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
return "Usage: debug [on/off]";
|
||||||
|
|
||||||
|
if (args[0].ToLower() == "on")
|
||||||
|
{
|
||||||
|
Client.Settings.DEBUG = true;
|
||||||
|
return "Debug logging is on";
|
||||||
|
}
|
||||||
|
else if (args[0].ToLower() == "off")
|
||||||
|
{
|
||||||
|
Client.Settings.DEBUG = false;
|
||||||
|
return "Debug logging is off";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Usage: debug [on/off]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class DilationCommand : Command
|
||||||
|
{
|
||||||
|
public DilationCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "dilation";
|
||||||
|
Description = "Shows time dilation for current sim.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
return "Dilation is " + Client.Network.CurrentSim.Dilation.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Utilities.Assets;
|
||||||
|
using libsecondlife.Utilities.Appearance;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class DumpOutfitCommand : Command
|
||||||
|
{
|
||||||
|
libsecondlife.Utilities.Assets.AssetManager Assets;
|
||||||
|
List<LLUUID> OutfitAssets = new List<LLUUID>();
|
||||||
|
|
||||||
|
public DumpOutfitCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "dumpoutfit";
|
||||||
|
Description = "Dumps all of the textures from an avatars outfit to the hard drive. Usage: dumpoutfit [avatar-uuid]";
|
||||||
|
|
||||||
|
Assets = new AssetManager(testClient);
|
||||||
|
Assets.OnImageReceived += new AssetManager.ImageReceivedCallback(Assets_OnImageReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
return "Usage: dumpoutfit [avatar-uuid]";
|
||||||
|
|
||||||
|
LLUUID target;
|
||||||
|
|
||||||
|
if (!LLUUID.TryParse(args[0], out target))
|
||||||
|
return "Usage: dumpoutfit [avatar-uuid]";
|
||||||
|
|
||||||
|
lock (Client.AvatarList)
|
||||||
|
{
|
||||||
|
foreach (Avatar avatar in Client.AvatarList.Values)
|
||||||
|
{
|
||||||
|
if (avatar.ID == target)
|
||||||
|
{
|
||||||
|
StringBuilder output = new StringBuilder("Downloading ");
|
||||||
|
|
||||||
|
lock (OutfitAssets) OutfitAssets.Clear();
|
||||||
|
|
||||||
|
foreach (KeyValuePair<uint, LLObject.TextureEntryFace> face in avatar.Textures.FaceTextures)
|
||||||
|
{
|
||||||
|
ImageType type = ImageType.Normal;
|
||||||
|
|
||||||
|
switch ((AppearanceManager.TextureIndex)face.Key)
|
||||||
|
{
|
||||||
|
case AppearanceManager.TextureIndex.HeadBaked:
|
||||||
|
case AppearanceManager.TextureIndex.EyesBaked:
|
||||||
|
case AppearanceManager.TextureIndex.UpperBaked:
|
||||||
|
case AppearanceManager.TextureIndex.LowerBaked:
|
||||||
|
case AppearanceManager.TextureIndex.SkirtBaked:
|
||||||
|
type = ImageType.Baked;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assets.RequestImage(face.Value.TextureID, type, 100000.0f, 0);
|
||||||
|
|
||||||
|
output.Append(((AppearanceManager.TextureIndex)face.Key).ToString());
|
||||||
|
output.Append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Couldn't find avatar " + target.ToStringHyphenated();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Assets_OnImageReceived(ImageDownload image)
|
||||||
|
{
|
||||||
|
if (image.Success)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllBytes(image.ID.ToStringHyphenated() + ".jp2", image.AssetData);
|
||||||
|
Console.WriteLine("Wrote JPEG2000 image " + image.ID.ToStringHyphenated() + ".jp2");
|
||||||
|
|
||||||
|
byte[] tgaFile = OpenJPEGNet.OpenJPEG.DecodeToTGA(image.AssetData);
|
||||||
|
File.WriteAllBytes(image.ID.ToStringHyphenated() + ".tga", tgaFile);
|
||||||
|
Console.WriteLine("Wrote TGA image " + image.ID.ToStringHyphenated() + ".tga");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed to download image " + image.ID.ToStringHyphenated());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class ExportCommand : Command
|
||||||
|
{
|
||||||
|
AutoResetEvent GotPermissionsEvent = new AutoResetEvent(false);
|
||||||
|
LLObject.ObjectPropertiesFamily Properties;
|
||||||
|
bool GotPermissions = false;
|
||||||
|
LLUUID SelectedObject = LLUUID.Zero;
|
||||||
|
|
||||||
|
Dictionary<LLUUID, Primitive> PrimsWaiting = new Dictionary<LLUUID, Primitive>();
|
||||||
|
AutoResetEvent AllPropertiesReceived = new AutoResetEvent(false);
|
||||||
|
|
||||||
|
public ExportCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
testClient.Objects.OnObjectPropertiesFamily += new ObjectManager.ObjectPropertiesFamilyCallback(Objects_OnObjectPropertiesFamily);
|
||||||
|
testClient.Objects.OnObjectProperties += new ObjectManager.ObjectPropertiesCallback(Objects_OnObjectProperties);
|
||||||
|
testClient.Avatars.OnPointAt += new AvatarManager.PointAtCallback(Avatars_OnPointAt);
|
||||||
|
|
||||||
|
Name = "export";
|
||||||
|
Description = "Exports an object to an xml file. Usage: export uuid outputfile.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length != 2 && !(args.Length == 1 && SelectedObject != LLUUID.Zero))
|
||||||
|
return "Usage: export uuid outputfile.xml";
|
||||||
|
|
||||||
|
LLUUID id;
|
||||||
|
uint localid = 0;
|
||||||
|
int count = 0;
|
||||||
|
string file;
|
||||||
|
|
||||||
|
if (args.Length == 2)
|
||||||
|
{
|
||||||
|
file = args[1];
|
||||||
|
if (!LLUUID.TryParse(args[0], out id))
|
||||||
|
return "Usage: export uuid outputfile.xml";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file = args[0];
|
||||||
|
id = SelectedObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (Client.SimPrims)
|
||||||
|
{
|
||||||
|
if (Client.SimPrims.ContainsKey(Client.Network.CurrentSim))
|
||||||
|
{
|
||||||
|
foreach (Primitive prim in Client.SimPrims[Client.Network.CurrentSim].Values)
|
||||||
|
{
|
||||||
|
if (prim.ID == id)
|
||||||
|
{
|
||||||
|
if (prim.ParentID != 0)
|
||||||
|
localid = prim.ParentID;
|
||||||
|
else
|
||||||
|
localid = prim.LocalID;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localid != 0)
|
||||||
|
{
|
||||||
|
// Check for export permission first
|
||||||
|
Client.Objects.RequestObjectPropertiesFamily(Client.Network.CurrentSim, id);
|
||||||
|
GotPermissionsEvent.WaitOne(8000, false);
|
||||||
|
|
||||||
|
if (!GotPermissions)
|
||||||
|
{
|
||||||
|
return "Couldn't fetch permissions for the requested object, try again";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GotPermissions = false;
|
||||||
|
if (Properties.OwnerID != Client.Network.AgentID &&
|
||||||
|
Properties.OwnerID != Client.MasterKey &&
|
||||||
|
Client.Network.AgentID != Client.Self.ID)
|
||||||
|
{
|
||||||
|
return "That object is owned by " + Properties.OwnerID + ", we don't have permission " +
|
||||||
|
"to export it";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
XmlWriterSettings settings = new XmlWriterSettings();
|
||||||
|
settings.Indent = true;
|
||||||
|
XmlWriter writer = XmlWriter.Create(file, settings);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<Primitive> prims = new List<Primitive>();
|
||||||
|
|
||||||
|
lock (Client.SimPrims)
|
||||||
|
{
|
||||||
|
if (Client.SimPrims.ContainsKey(Client.Network.CurrentSim))
|
||||||
|
{
|
||||||
|
foreach (Primitive prim in Client.SimPrims[Client.Network.CurrentSim].Values)
|
||||||
|
{
|
||||||
|
if (prim.LocalID == localid || prim.ParentID == localid)
|
||||||
|
{
|
||||||
|
prims.Add(prim);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool complete = RequestObjectProperties(prims, 250);
|
||||||
|
|
||||||
|
//Serialize it!
|
||||||
|
Helpers.PrimListToXml(prims, writer);
|
||||||
|
|
||||||
|
if (!complete) {
|
||||||
|
Console.WriteLine("Warning: Unable to retrieve full properties for:");
|
||||||
|
foreach (LLUUID uuid in PrimsWaiting.Keys)
|
||||||
|
Console.WriteLine(uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
writer.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
string ret = "Failed to write to " + file + ":" + e.ToString();
|
||||||
|
if (ret.Length > 1000)
|
||||||
|
{
|
||||||
|
ret = ret.Remove(1000);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return "Exported " + count + " prims to " + file;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Couldn't find UUID " + id.ToString() + " in the " +
|
||||||
|
Client.SimPrims[Client.Network.CurrentSim].Count +
|
||||||
|
"objects currently indexed in the current simulator";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool RequestObjectProperties(List<Primitive> objects, int msPerRequest)
|
||||||
|
{
|
||||||
|
// Create an array of the local IDs of all the prims we are requesting properties for
|
||||||
|
uint[] localids = new uint[objects.Count];
|
||||||
|
|
||||||
|
lock (PrimsWaiting)
|
||||||
|
{
|
||||||
|
PrimsWaiting.Clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < objects.Count; ++i)
|
||||||
|
{
|
||||||
|
localids[i] = objects[i].LocalID;
|
||||||
|
PrimsWaiting.Add(objects[i].ID, objects[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Client.Objects.SelectObjects(Client.Network.CurrentSim, localids);
|
||||||
|
|
||||||
|
return AllPropertiesReceived.WaitOne(2000 + msPerRequest * objects.Count, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatars_OnPointAt(LLUUID sourceID, LLUUID targetID, LLVector3d targetPos,
|
||||||
|
MainAvatar.PointAtType pointType, float duration, LLUUID id)
|
||||||
|
{
|
||||||
|
if (sourceID == Client.MasterKey)
|
||||||
|
{
|
||||||
|
//Client.DebugLog("Master is now selecting " + targetID.ToStringHyphenated());
|
||||||
|
SelectedObject = targetID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Objects_OnObjectPropertiesFamily(Simulator simulator, LLObject.ObjectPropertiesFamily properties)
|
||||||
|
{
|
||||||
|
Properties = properties;
|
||||||
|
GotPermissions = true;
|
||||||
|
GotPermissionsEvent.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Objects_OnObjectProperties(Simulator simulator, LLObject.ObjectProperties properties)
|
||||||
|
{
|
||||||
|
lock (PrimsWaiting)
|
||||||
|
{
|
||||||
|
Primitive prim;
|
||||||
|
if (PrimsWaiting.TryGetValue(properties.ObjectID, out prim))
|
||||||
|
{
|
||||||
|
prim.Properties = properties;
|
||||||
|
}
|
||||||
|
PrimsWaiting.Remove(properties.ObjectID);
|
||||||
|
|
||||||
|
if (PrimsWaiting.Count == 0)
|
||||||
|
AllPropertiesReceived.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class ExportOutfitCommand : Command
|
||||||
|
{
|
||||||
|
public ExportOutfitCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "exportoutfit";
|
||||||
|
Description = "Exports an avatars outfit to an xml file. Usage: exportoutfit avataruuid outputfile.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length != 2)
|
||||||
|
return "Usage: exportoutfit avataruuid outputfile.xml";
|
||||||
|
|
||||||
|
LLUUID id;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
id = new LLUUID(args[0]);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return "Usage: exportoutfit avataruuid outputfile.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (Client.Appearances)
|
||||||
|
{
|
||||||
|
if (Client.Appearances.ContainsKey(id))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
XmlWriterSettings settings = new XmlWriterSettings();
|
||||||
|
settings.Indent = true;
|
||||||
|
XmlWriter writer = XmlWriter.Create(args[1], settings);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Client.Appearances[id].ToXml(writer);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
writer.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return e.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Exported appearance for avatar " + id.ToString() + " to " + args[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Couldn't find an appearance for avatar " + id.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class ExportParticlesCommand : Command
|
||||||
|
{
|
||||||
|
public ExportParticlesCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "exportparticles";
|
||||||
|
Description = "Reverse engineers a prim with a particle system to an LSL script. Usage: exportscript [prim-uuid]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
return "Usage: exportparticles [prim-uuid]";
|
||||||
|
|
||||||
|
LLUUID id;
|
||||||
|
if (!LLUUID.TryParse(args[0], out id))
|
||||||
|
return "Usage: exportparticles [prim-uuid]";
|
||||||
|
|
||||||
|
lock (Client.SimPrims)
|
||||||
|
{
|
||||||
|
if (Client.SimPrims.ContainsKey(Client.Network.CurrentSim))
|
||||||
|
{
|
||||||
|
foreach (Primitive prim in Client.SimPrims[Client.Network.CurrentSim].Values)
|
||||||
|
{
|
||||||
|
if (prim.ID == id)
|
||||||
|
{
|
||||||
|
if (prim.ParticleSys.CRC != 0)
|
||||||
|
{
|
||||||
|
StringBuilder lsl = new StringBuilder();
|
||||||
|
|
||||||
|
lsl.Append("default" + Environment.NewLine);
|
||||||
|
lsl.Append("{" + Environment.NewLine);
|
||||||
|
lsl.Append(" state_entry()" + Environment.NewLine);
|
||||||
|
lsl.Append(" {" + Environment.NewLine);
|
||||||
|
lsl.Append(" llParticleSystem([" + Environment.NewLine);
|
||||||
|
|
||||||
|
lsl.Append(" PSYS_PART_FLAGS, 0");
|
||||||
|
|
||||||
|
if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.InterpColor) != 0)
|
||||||
|
lsl.Append(" | PSYS_PART_INTERP_COLOR_MASK");
|
||||||
|
if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.InterpScale) != 0)
|
||||||
|
lsl.Append(" | PSYS_PART_INTERP_SCALE_MASK");
|
||||||
|
if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Bounce) != 0)
|
||||||
|
lsl.Append(" | PSYS_PART_BOUNCE_MASK");
|
||||||
|
if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.Wind) != 0)
|
||||||
|
lsl.Append(" | PSYS_PART_WIND_MASK");
|
||||||
|
if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.FollowSrc) != 0)
|
||||||
|
lsl.Append(" | PSYS_PART_FOLLOW_SRC_MASK");
|
||||||
|
if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.FollowVelocity) != 0)
|
||||||
|
lsl.Append(" | PSYS_PART_FOLLOW_VELOCITY_MASK");
|
||||||
|
if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.TargetPos) != 0)
|
||||||
|
lsl.Append(" | PSYS_PART_TARGET_POS_MASK");
|
||||||
|
if ((prim.ParticleSys.PartDataFlags & Primitive.ParticleSystem.ParticleDataFlags.TargetLinear) != 0)
|
||||||
|
lsl.Append(" | PSYS_PART_TARGET_LINEAR_MASK");
|
||||||
|
if ((prim.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)
|
||||||
|
lsl.Append(" | PSYS_SRC_PATTERN_DROP");
|
||||||
|
if ((prim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Explode) != 0)
|
||||||
|
lsl.Append(" | PSYS_SRC_PATTERN_EXPLODE");
|
||||||
|
if ((prim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.Angle) != 0)
|
||||||
|
lsl.Append(" | PSYS_SRC_PATTERN_ANGLE");
|
||||||
|
if ((prim.ParticleSys.Pattern & Primitive.ParticleSystem.SourcePattern.AngleCone) != 0)
|
||||||
|
lsl.Append(" | PSYS_SRC_PATTERN_ANGLE_CONE");
|
||||||
|
if ((prim.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(" ]);" + Environment.NewLine);
|
||||||
|
lsl.Append(" }" + Environment.NewLine);
|
||||||
|
lsl.Append("}" + Environment.NewLine);
|
||||||
|
|
||||||
|
return lsl.ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Prim " + prim.LocalID + " does not have a particle system";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Couldn't find prim " + id.ToStringHyphenated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class FindSimCommand : Command
|
||||||
|
{
|
||||||
|
public FindSimCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "findsim";
|
||||||
|
Description = "Searches for a simulator and returns information about it. Usage: findsim [Simulator Name]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length < 1)
|
||||||
|
return "Usage: findsim [Simulator Name]";
|
||||||
|
|
||||||
|
// Build the simulator name from the args list
|
||||||
|
string simName = string.Empty;
|
||||||
|
for (int i = 0; i < args.Length; i++)
|
||||||
|
simName += args[i] + " ";
|
||||||
|
simName = simName.TrimEnd().ToLower();
|
||||||
|
|
||||||
|
//if (!GridDataCached[Client])
|
||||||
|
//{
|
||||||
|
// Client.Grid.RequestAllSims(GridManager.MapLayerType.Objects);
|
||||||
|
// System.Threading.Thread.Sleep(5000);
|
||||||
|
// GridDataCached[Client] = true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
GridRegion region;
|
||||||
|
|
||||||
|
if (Client.Grid.GetGridRegion(simName, out region))
|
||||||
|
return String.Format("{0}: handle={1} ({2},{3})", region.Name, region.RegionHandle, region.X, region.Y);
|
||||||
|
else
|
||||||
|
return "Lookup of " + simName + " failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class HelpCommand: Command
|
||||||
|
{
|
||||||
|
public HelpCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "help";
|
||||||
|
Description = "Lists available commands.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
result.AppendFormat("\n\nHELP\nClient accept teleport lures from master and group members.\n");
|
||||||
|
foreach (Command c in Client.Commands.Values)
|
||||||
|
{
|
||||||
|
result.AppendFormat(" * {0} - {1}\n", c.Name, c.Description);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,246 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using System.Threading;
|
||||||
|
using System.IO;
|
||||||
|
using libsecondlife;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
enum ImporterState
|
||||||
|
{
|
||||||
|
RezzingParent,
|
||||||
|
RezzingChildren,
|
||||||
|
Linking,
|
||||||
|
Idle
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Linkset
|
||||||
|
{
|
||||||
|
public Primitive RootPrim;
|
||||||
|
public List<Primitive> Children = new List<Primitive>();
|
||||||
|
|
||||||
|
public Linkset()
|
||||||
|
{
|
||||||
|
RootPrim = new Primitive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Linkset(Primitive rootPrim)
|
||||||
|
{
|
||||||
|
RootPrim = rootPrim;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ImportCommand : Command
|
||||||
|
{
|
||||||
|
Primitive currentPrim;
|
||||||
|
LLVector3 currentPosition;
|
||||||
|
SecondLife currentClient;
|
||||||
|
AutoResetEvent primDone;
|
||||||
|
List<Primitive> primsCreated;
|
||||||
|
List<uint> linkQueue;
|
||||||
|
uint rootLocalID = 0;
|
||||||
|
bool registeredCreateEvent = false;
|
||||||
|
|
||||||
|
ImporterState state = ImporterState.Idle;
|
||||||
|
|
||||||
|
public ImportCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "import";
|
||||||
|
Description = "Import prims from an exported xml file. Usage: import inputfile.xml";
|
||||||
|
primDone = new AutoResetEvent(false);
|
||||||
|
registeredCreateEvent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
return "Usage: import inputfile.xml";
|
||||||
|
|
||||||
|
string filename = args[0];
|
||||||
|
Dictionary<uint, Primitive> prims;
|
||||||
|
|
||||||
|
currentClient = Client;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
XmlReader reader = XmlReader.Create(filename);
|
||||||
|
List<Primitive> listprims = Helpers.PrimListFromXml(reader);
|
||||||
|
reader.Close();
|
||||||
|
|
||||||
|
// Create a dictionary indexed by the old local ID of the prims
|
||||||
|
prims = new Dictionary<uint, Primitive>();
|
||||||
|
foreach (Primitive prim in listprims)
|
||||||
|
{
|
||||||
|
prims.Add(prim.LocalID, prim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return "Failed to import the object XML file, maybe it doesn't exist or is in the wrong format?";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!registeredCreateEvent)
|
||||||
|
{
|
||||||
|
Client.OnPrimCreated += new TestClient.PrimCreatedCallback(TestClient_OnPrimCreated);
|
||||||
|
registeredCreateEvent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build an organized structure from the imported prims
|
||||||
|
Dictionary<uint, Linkset> linksets = new Dictionary<uint, Linkset>();
|
||||||
|
foreach (Primitive prim in prims.Values)
|
||||||
|
{
|
||||||
|
if (prim.ParentID == 0)
|
||||||
|
{
|
||||||
|
if (linksets.ContainsKey(prim.LocalID))
|
||||||
|
linksets[prim.LocalID].RootPrim = prim;
|
||||||
|
else
|
||||||
|
linksets[prim.LocalID] = new Linkset(prim);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!linksets.ContainsKey(prim.ParentID))
|
||||||
|
linksets[prim.ParentID] = new Linkset();
|
||||||
|
|
||||||
|
linksets[prim.ParentID].Children.Add(prim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
primsCreated = new List<Primitive>();
|
||||||
|
Console.WriteLine("Importing " + linksets.Count + " structures.");
|
||||||
|
|
||||||
|
foreach (Linkset linkset in linksets.Values)
|
||||||
|
{
|
||||||
|
if (linkset.RootPrim.LocalID != 0)
|
||||||
|
{
|
||||||
|
state = ImporterState.RezzingParent;
|
||||||
|
currentPrim = linkset.RootPrim;
|
||||||
|
// HACK: Offset the root prim position so it's not lying on top of the original
|
||||||
|
// We need a more elaborate solution for importing with relative or absolute offsets
|
||||||
|
linkset.RootPrim.Position = Client.Self.Position;
|
||||||
|
linkset.RootPrim.Position.Z += 3.0f;
|
||||||
|
currentPosition = linkset.RootPrim.Position;
|
||||||
|
// A better solution would move the bot to the desired position.
|
||||||
|
// or to check if we are within a certain distance of the desired position.
|
||||||
|
|
||||||
|
// Rez the root prim with no rotation
|
||||||
|
LLQuaternion rootRotation = linkset.RootPrim.Rotation;
|
||||||
|
linkset.RootPrim.Rotation = LLQuaternion.Identity;
|
||||||
|
|
||||||
|
Client.Objects.AddPrim(Client.Network.CurrentSim, linkset.RootPrim.Data, LLUUID.Zero,
|
||||||
|
linkset.RootPrim.Position, linkset.RootPrim.Scale, linkset.RootPrim.Rotation);
|
||||||
|
|
||||||
|
if (!primDone.WaitOne(10000, false))
|
||||||
|
return "Rez failed, timed out while creating the root prim.";
|
||||||
|
|
||||||
|
state = ImporterState.RezzingChildren;
|
||||||
|
|
||||||
|
// Rez the child prims
|
||||||
|
foreach (Primitive prim in linkset.Children)
|
||||||
|
{
|
||||||
|
currentPrim = prim;
|
||||||
|
currentPosition = prim.Position + linkset.RootPrim.Position;
|
||||||
|
|
||||||
|
Client.Objects.AddPrim(Client.Network.CurrentSim, prim.Data, LLUUID.Zero, currentPosition,
|
||||||
|
prim.Scale, prim.Rotation);
|
||||||
|
|
||||||
|
if (!primDone.WaitOne(10000, false))
|
||||||
|
return "Rez failed, timed out while creating child prim.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linkset.Children.Count != 0)
|
||||||
|
{
|
||||||
|
// Create a list of the local IDs of the newly created prims
|
||||||
|
List<uint> primIDs = new List<uint>(primsCreated.Count);
|
||||||
|
primIDs.Add(rootLocalID); // Root prim is first in list.
|
||||||
|
foreach (Primitive prim in primsCreated)
|
||||||
|
{
|
||||||
|
if (prim.LocalID != rootLocalID)
|
||||||
|
primIDs.Add(prim.LocalID);
|
||||||
|
}
|
||||||
|
linkQueue = new List<uint>(primIDs.Count);
|
||||||
|
linkQueue.AddRange(primIDs);
|
||||||
|
|
||||||
|
// Link and set the permissions + rotation
|
||||||
|
state = ImporterState.Linking;
|
||||||
|
Client.Objects.LinkPrims(Client.Network.CurrentSim, linkQueue);
|
||||||
|
if (primDone.WaitOne(100000 * linkset.Children.Count, false))
|
||||||
|
{
|
||||||
|
Client.Objects.SetPermissions(Client.Network.CurrentSim, primIDs,
|
||||||
|
Helpers.PermissionWho.Everyone | Helpers.PermissionWho.Group | Helpers.PermissionWho.NextOwner,
|
||||||
|
Helpers.PermissionType.Copy | Helpers.PermissionType.Modify | Helpers.PermissionType.Move |
|
||||||
|
Helpers.PermissionType.Transfer, true);
|
||||||
|
|
||||||
|
Client.Objects.SetRotation(Client.Network.CurrentSim, rootLocalID, rootRotation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Warning: Failed to link {0} prims", linkQueue.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Client.Objects.SetRotation(Client.Network.CurrentSim, rootLocalID, rootRotation);
|
||||||
|
}
|
||||||
|
state = ImporterState.Idle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Skip linksets with a missing root prim
|
||||||
|
Console.WriteLine("WARNING: Skipping a linkset with a missing root prim");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset everything for the next linkset
|
||||||
|
primsCreated.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Import complete.";
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestClient_OnPrimCreated(Simulator simulator, Primitive prim)
|
||||||
|
{
|
||||||
|
if ((prim.Flags & LLObject.ObjectFlags.CreateSelected) == 0)
|
||||||
|
return; // We received an update for an object we didn't create
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case ImporterState.RezzingParent:
|
||||||
|
rootLocalID = prim.LocalID;
|
||||||
|
goto case ImporterState.RezzingChildren;
|
||||||
|
case ImporterState.RezzingChildren:
|
||||||
|
if (!primsCreated.Contains(prim))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Setting properties for " + prim.LocalID);
|
||||||
|
// TODO: Is there a way to set all of this at once, and update more ObjectProperties stuff?
|
||||||
|
currentClient.Objects.SetPosition(simulator, prim.LocalID, currentPosition);
|
||||||
|
currentClient.Objects.SetTextures(simulator, prim.LocalID, currentPrim.Textures);
|
||||||
|
currentClient.Objects.SetLight(simulator, prim.LocalID, currentPrim.Light);
|
||||||
|
currentClient.Objects.SetFlexible(simulator, prim.LocalID, currentPrim.Flexible);
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(currentPrim.Properties.Name))
|
||||||
|
currentClient.Objects.SetName(simulator, prim.LocalID, currentPrim.Properties.Name);
|
||||||
|
if (!String.IsNullOrEmpty(currentPrim.Properties.Description))
|
||||||
|
currentClient.Objects.SetDescription(simulator, prim.LocalID,
|
||||||
|
currentPrim.Properties.Description);
|
||||||
|
|
||||||
|
primsCreated.Add(prim);
|
||||||
|
primDone.Set();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ImporterState.Linking:
|
||||||
|
lock (linkQueue)
|
||||||
|
{
|
||||||
|
int index = linkQueue.IndexOf(prim.LocalID);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
linkQueue.RemoveAt(index);
|
||||||
|
if (linkQueue.Count == 0)
|
||||||
|
primDone.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class ImportOutfitCommand : Command
|
||||||
|
{
|
||||||
|
private uint SerialNum = 1;
|
||||||
|
|
||||||
|
public ImportOutfitCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "importoutfit";
|
||||||
|
Description = "Imports an appearance from an xml file. Usage: importoutfit inputfile.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
return "Usage: importoutfit inputfile.xml";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
XmlReader reader = XmlReader.Create(args[0]);
|
||||||
|
XmlSerializer serializer = new XmlSerializer(typeof(Packet));
|
||||||
|
AvatarAppearancePacket appearance = (AvatarAppearancePacket)serializer.Deserialize(reader);
|
||||||
|
reader.Close();
|
||||||
|
|
||||||
|
AgentSetAppearancePacket set = new AgentSetAppearancePacket();
|
||||||
|
|
||||||
|
set.AgentData.AgentID = Client.Network.AgentID;
|
||||||
|
set.AgentData.SessionID = Client.Network.SessionID;
|
||||||
|
set.AgentData.SerialNum = SerialNum++;
|
||||||
|
|
||||||
|
float AV_Height_Range = 2.025506f - 1.50856f;
|
||||||
|
float AV_Height = 1.50856f + (((float)appearance.VisualParam[25].ParamValue / 255.0f) * AV_Height_Range);
|
||||||
|
set.AgentData.Size = new LLVector3(0.45f, 0.6f, AV_Height);
|
||||||
|
|
||||||
|
set.ObjectData.TextureEntry = appearance.ObjectData.TextureEntry;
|
||||||
|
set.VisualParam = new AgentSetAppearancePacket.VisualParamBlock[appearance.VisualParam.Length];
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
foreach (AvatarAppearancePacket.VisualParamBlock block in appearance.VisualParam)
|
||||||
|
{
|
||||||
|
set.VisualParam[i] = new AgentSetAppearancePacket.VisualParamBlock();
|
||||||
|
set.VisualParam[i].ParamValue = block.ParamValue;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
set.WearableData = new AgentSetAppearancePacket.WearableDataBlock[0];
|
||||||
|
|
||||||
|
Client.Network.SendPacket(set);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return "Failed to import the appearance XML file, maybe it doesn't exist or is in the wrong format?";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Imported " + args[0] + " and sent an AgentSetAppearance packet";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class BalanceCommand: Command
|
||||||
|
{
|
||||||
|
public BalanceCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "balance";
|
||||||
|
Description = "Shows the amount of L$.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
return Client.ToString() + " has L$: " + Client.Self.Balance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
using libsecondlife.InventorySystem;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class DeleteFolderCommand : Command
|
||||||
|
{
|
||||||
|
public DeleteFolderCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "deleteFolder";
|
||||||
|
Description = "Deletes a folder from inventory.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
return "Broken until someone fixes me";
|
||||||
|
|
||||||
|
//string target = String.Empty;
|
||||||
|
//for (int ct = 0; ct < args.Length; ct++)
|
||||||
|
// target = target + args[ct] + " ";
|
||||||
|
//target = target.TrimEnd();
|
||||||
|
|
||||||
|
//Client.Inventory.DownloadInventory();
|
||||||
|
//InventoryFolder folder = Client.Inventory.getFolder(target);
|
||||||
|
//if (folder != null)
|
||||||
|
//{
|
||||||
|
// folder.Delete();
|
||||||
|
// return "Folder " + target + " deleted.";
|
||||||
|
//}
|
||||||
|
|
||||||
|
//return "Unable to find: " + target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class GiveAllCommand: Command
|
||||||
|
{
|
||||||
|
public GiveAllCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "giveAll";
|
||||||
|
Description = "Gives you all it's money.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (fromAgentID == null)
|
||||||
|
return "Unable to send money to console. This command only works when IMed.";
|
||||||
|
|
||||||
|
int amount = Client.Self.Balance;
|
||||||
|
Client.Self.GiveMoney(fromAgentID, Client.Self.Balance, String.Empty);
|
||||||
|
return "Gave $" + amount + " to " + fromAgentID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
using libsecondlife.InventorySystem;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class InventoryCommand : Command
|
||||||
|
{
|
||||||
|
public InventoryCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "i";
|
||||||
|
Description = "Prints out inventory.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
return "Broken until someone fixes me";
|
||||||
|
|
||||||
|
//Client.Inventory.DownloadInventory();
|
||||||
|
//StringBuilder result = new StringBuilder();
|
||||||
|
//PrintFolder(Client.Inventory.GetRootFolder(), result, 0);
|
||||||
|
//return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
//void PrintFolder(InventoryFolder folder, StringBuilder output, int indenting)
|
||||||
|
//{
|
||||||
|
// Indent(output, indenting);
|
||||||
|
// output.Append(folder.Name);
|
||||||
|
// output.Append("\n");
|
||||||
|
// foreach (InventoryBase b in folder.GetContents())
|
||||||
|
// {
|
||||||
|
// InventoryItem item = b as InventoryItem;
|
||||||
|
// if (item != null)
|
||||||
|
// {
|
||||||
|
// Indent(output, indenting + 1);
|
||||||
|
// output.Append(item.Name);
|
||||||
|
// output.Append("\n");
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// InventoryFolder subFolder = b as InventoryFolder;
|
||||||
|
// if (subFolder != null)
|
||||||
|
// PrintFolder(subFolder, output, indenting + 1);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//void Indent(StringBuilder output, int indenting)
|
||||||
|
//{
|
||||||
|
// for (int count = 0; count < indenting; count++)
|
||||||
|
// {
|
||||||
|
// output.Append(" ");
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
using libsecondlife.InventorySystem;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class WearCommand : Command
|
||||||
|
{
|
||||||
|
public WearCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "wear";
|
||||||
|
Description = "Wear an outfit folder from inventory.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
return "Broken until someone fixes me";
|
||||||
|
|
||||||
|
//string target = String.Empty;
|
||||||
|
//for (int ct = 0; ct < args.Length; ct++)
|
||||||
|
// target = target + args[ct] + " ";
|
||||||
|
//target = target.TrimEnd();
|
||||||
|
|
||||||
|
//Client.Inventory.DownloadInventory();
|
||||||
|
//InventoryFolder folder = Client.Inventory.getFolder(target);
|
||||||
|
//if (folder != null)
|
||||||
|
//{
|
||||||
|
// Client.Appearance.WearOutfit(folder);
|
||||||
|
// return "Outfit " + target + " worn.";
|
||||||
|
//}
|
||||||
|
|
||||||
|
//return "Unable to find: " + target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Reflection;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class LoadCommand: Command
|
||||||
|
{
|
||||||
|
public LoadCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "load";
|
||||||
|
Description = "Loads commands from a dll. (Usage: load AssemblyNameWithoutExtension)";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length < 1)
|
||||||
|
return "Usage: load AssemblyNameWithoutExtension";
|
||||||
|
|
||||||
|
string filename = AppDomain.CurrentDomain.BaseDirectory + args[0] + ".dll";
|
||||||
|
Client.RegisterAllCommands(Assembly.LoadFile(filename));
|
||||||
|
return "Assembly " + filename + " loaded.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class LoginCommand : Command
|
||||||
|
{
|
||||||
|
public LoginCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "login";
|
||||||
|
Description = "Logs in another avatar";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length != 3 && args.Length != 4)
|
||||||
|
return "usage: login firstname lastname password [simname]";
|
||||||
|
|
||||||
|
SecondLife newClient = Client.ClientManager.Login(args);
|
||||||
|
|
||||||
|
if (newClient.Network.Connected)
|
||||||
|
{
|
||||||
|
return "Logged in " + newClient.ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Failed to login: " + newClient.Network.LoginStatusMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class LogoutCommand : Command
|
||||||
|
{
|
||||||
|
public LogoutCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "logout";
|
||||||
|
Description = "Log this avatar out";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
string name = Client.ToString();
|
||||||
|
Client.ClientManager.Logout(Client);
|
||||||
|
return "Logged " + name + " out";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
using System;
|
||||||
|
using libsecondlife;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class MD5Command : Command
|
||||||
|
{
|
||||||
|
public MD5Command(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "md5";
|
||||||
|
Description = "Creates an MD5 hash from a given password. Usage: md5 [password]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length == 1)
|
||||||
|
return Helpers.MD5(args[0]);
|
||||||
|
else
|
||||||
|
return "Usage: md5 [password]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class FollowCommand: Command
|
||||||
|
{
|
||||||
|
public FollowCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "follow";
|
||||||
|
Description = "Follow another avatar. (usage: follow [FirstName LastName]) If no target is set then will follow master.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
string target = String.Empty;
|
||||||
|
for (int ct = 0; ct < args.Length; ct++)
|
||||||
|
target = target + args[ct] + " ";
|
||||||
|
target = target.TrimEnd();
|
||||||
|
|
||||||
|
if (target.Length > 0)
|
||||||
|
{
|
||||||
|
if (Follow(target))
|
||||||
|
return "Following " + target;
|
||||||
|
else
|
||||||
|
return "Unable to follow " + target + ". Client may not be able to see that avatar.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Follow(Client.MasterKey))
|
||||||
|
return "Following " + Client.MasterKey;
|
||||||
|
else
|
||||||
|
return "No target specified and no master not found. usage: follow [FirstName LastName])";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const float DISTANCE_BUFFER = 3.0f;
|
||||||
|
Avatar followAvatar;
|
||||||
|
|
||||||
|
bool Follow(string name)
|
||||||
|
{
|
||||||
|
foreach (Avatar av in Client.AvatarList.Values)
|
||||||
|
{
|
||||||
|
if (av.Name == name)
|
||||||
|
{
|
||||||
|
followAvatar = av;
|
||||||
|
Active = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Follow(LLUUID id)
|
||||||
|
{
|
||||||
|
foreach (Avatar av in Client.AvatarList.Values)
|
||||||
|
{
|
||||||
|
if (av.ID == id)
|
||||||
|
{
|
||||||
|
followAvatar = av;
|
||||||
|
Active = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Think()
|
||||||
|
{
|
||||||
|
if (Helpers.VecDist(followAvatar.Position, Client.Self.Position) > DISTANCE_BUFFER)
|
||||||
|
{
|
||||||
|
//move toward target
|
||||||
|
LLVector3 avPos = followAvatar.Position;
|
||||||
|
Client.Self.AutoPilot((ulong)avPos.X + (ulong)Client.regionX, (ulong)avPos.Y + (ulong)Client.regionY, avPos.Z);
|
||||||
|
}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// //stop at current position
|
||||||
|
// LLVector3 myPos = client.Self.Position;
|
||||||
|
// client.Self.AutoPilot((ulong)myPos.x, (ulong)myPos.y, myPos.Z);
|
||||||
|
//}
|
||||||
|
|
||||||
|
base.Think();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class GotoCommand: Command
|
||||||
|
{
|
||||||
|
public GotoCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "goto";
|
||||||
|
Description = "Teleport to a location (e.g. \"goto Hooper/100/100/30\")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length < 1)
|
||||||
|
return "usage: Destination should be specified as sim/x/y/z";
|
||||||
|
|
||||||
|
string destination = String.Empty;
|
||||||
|
|
||||||
|
// Handle multi-word sim names by combining the arguments
|
||||||
|
foreach (string arg in args)
|
||||||
|
{
|
||||||
|
destination += arg + " ";
|
||||||
|
}
|
||||||
|
destination = destination.Trim();
|
||||||
|
|
||||||
|
string[] tokens = destination.Split(new char[] { '/' });
|
||||||
|
if (tokens.Length != 4)
|
||||||
|
return "usage: Destination should be specified as sim/x/y/z";
|
||||||
|
|
||||||
|
string sim = tokens[0];
|
||||||
|
float x = Client.Self.Position.X;
|
||||||
|
float y = Client.Self.Position.Y;
|
||||||
|
float z = Client.Self.Position.Z;
|
||||||
|
float.TryParse(tokens[1], out x);
|
||||||
|
float.TryParse(tokens[2], out y);
|
||||||
|
float.TryParse(tokens[3], out z);
|
||||||
|
|
||||||
|
if (Client.Self.Teleport(sim, new LLVector3(x, y, z)))
|
||||||
|
{
|
||||||
|
return "Teleported to " + Client.Network.CurrentSim;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Teleport failed: " + Client.Self.TeleportMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class JumpCommand: Command
|
||||||
|
{
|
||||||
|
public JumpCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "jump";
|
||||||
|
Description = "Teleports to the specified height. (e.g. \"jump 1000\")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
return "usage: jump 1000";
|
||||||
|
|
||||||
|
float height = 0;
|
||||||
|
float.TryParse(args[0], out height);
|
||||||
|
|
||||||
|
Client.Self.Teleport
|
||||||
|
(
|
||||||
|
Client.Network.CurrentSim.Name,
|
||||||
|
new LLVector3(Client.Self.Position.X, Client.Self.Position.Y, Client.Self.Position.Z + height)
|
||||||
|
);
|
||||||
|
|
||||||
|
return "Jumped " + height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class LocationCommand: Command
|
||||||
|
{
|
||||||
|
public LocationCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "location";
|
||||||
|
Description = "Show the location.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
return "CurrentSim: '" + Client.Network.CurrentSim.ToString() + "' Position: " +
|
||||||
|
Client.Self.Position.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient.Commands.Movement {
|
||||||
|
class MovetoCommand : Command {
|
||||||
|
public MovetoCommand(TestClient client) {
|
||||||
|
Name = "moveto";
|
||||||
|
Description = "Moves the avatar to the specified global position using simulator autopilot.";
|
||||||
|
}
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID) {
|
||||||
|
if (args.Length != 3)
|
||||||
|
return "usage: moveto x y z";
|
||||||
|
float x = Client.Self.Position.X + Client.regionX;
|
||||||
|
float y = Client.Self.Position.Y + Client.regionY;
|
||||||
|
float z = Client.Self.Position.Z;
|
||||||
|
float.TryParse(args[0], out x);
|
||||||
|
float.TryParse(args[1], out y);
|
||||||
|
float.TryParse(args[2], out z);
|
||||||
|
Client.Self.AutoPilot((ulong)x, (ulong)y, z);
|
||||||
|
return "Attempting to move to <" + x + ", " + y + ", " + z + ">";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class SitCommand: Command
|
||||||
|
{
|
||||||
|
public SitCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "sit";
|
||||||
|
Description = "Attempt to sit on the closest prim";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
Primitive closest = null;
|
||||||
|
double closestDistance = Double.MaxValue;
|
||||||
|
|
||||||
|
lock (Client.SimPrims)
|
||||||
|
{
|
||||||
|
if (Client.SimPrims.ContainsKey(Client.Network.CurrentSim))
|
||||||
|
{
|
||||||
|
foreach (Primitive p in Client.SimPrims[Client.Network.CurrentSim].Values)
|
||||||
|
{
|
||||||
|
float distance = Helpers.VecDist(Client.Self.Position, p.Position);
|
||||||
|
|
||||||
|
if (closest == null || distance < closestDistance)
|
||||||
|
{
|
||||||
|
closest = p;
|
||||||
|
closestDistance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closest != null)
|
||||||
|
{
|
||||||
|
Client.Self.RequestSit(closest.ID, LLVector3.Zero);
|
||||||
|
Client.Self.Sit();
|
||||||
|
|
||||||
|
return "Sat on " + closest.ID + ". Distance: " + closestDistance;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Couldn't find a nearby prim to sit on";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class SitOnCommand: Command
|
||||||
|
{
|
||||||
|
public SitOnCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "siton";
|
||||||
|
Description = "Attempt to sit on a particular prim, with specified UUID";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
LLObject targetSeat = null;
|
||||||
|
|
||||||
|
lock (Client.SimPrims)
|
||||||
|
{
|
||||||
|
if (Client.SimPrims.ContainsKey(Client.Network.CurrentSim))
|
||||||
|
{
|
||||||
|
foreach (LLObject p in Client.SimPrims[Client.Network.CurrentSim].Values)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (p.ID == args[0])
|
||||||
|
targetSeat = p;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// handle exception
|
||||||
|
return "Sorry, I don't think " + args[0] + " is a valid UUID. I'm unable to sit there.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetSeat != null)
|
||||||
|
{
|
||||||
|
Client.Self.RequestSit(targetSeat.ID, LLVector3.Zero);
|
||||||
|
Client.Self.Sit();
|
||||||
|
|
||||||
|
return "Sat on prim " + targetSeat.ID + ".";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Couldn't find specified prim to sit on";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class StandCommand: Command
|
||||||
|
{
|
||||||
|
public StandCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "stand";
|
||||||
|
Description = "Stand";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
Client.Self.Status.StandUp = true;
|
||||||
|
stand(Client);
|
||||||
|
return "Standing up.";
|
||||||
|
}
|
||||||
|
|
||||||
|
void stand(SecondLife client)
|
||||||
|
{
|
||||||
|
SendAgentUpdate(client, (uint)MainAvatar.ControlFlags.AGENT_CONTROL_STAND_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float DRAW_DISTANCE = 96.0f;
|
||||||
|
void SendAgentUpdate(SecondLife client, uint ControlID)
|
||||||
|
{
|
||||||
|
AgentUpdatePacket p = new AgentUpdatePacket();
|
||||||
|
p.AgentData.Far = DRAW_DISTANCE;
|
||||||
|
//LLVector3 myPos = client.Self.Position;
|
||||||
|
p.AgentData.CameraCenter = new LLVector3(0, 0, 0);
|
||||||
|
p.AgentData.CameraAtAxis = new LLVector3(0, 0, 0);
|
||||||
|
p.AgentData.CameraLeftAxis = new LLVector3(0, 0, 0);
|
||||||
|
p.AgentData.CameraUpAxis = new LLVector3(0, 0, 0);
|
||||||
|
p.AgentData.HeadRotation = new LLQuaternion(0, 0, 0, 1); ;
|
||||||
|
p.AgentData.BodyRotation = new LLQuaternion(0, 0, 0, 1); ;
|
||||||
|
p.AgentData.AgentID = client.Network.AgentID;
|
||||||
|
p.AgentData.SessionID = client.Network.SessionID;
|
||||||
|
p.AgentData.ControlFlags = ControlID;
|
||||||
|
client.Network.SendPacket(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Xml;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class PacketLogCommand : Command
|
||||||
|
{
|
||||||
|
List<Packet> Packets = new List<Packet>();
|
||||||
|
bool Done = false;
|
||||||
|
int Count = 0;
|
||||||
|
int Total = 0;
|
||||||
|
|
||||||
|
public PacketLogCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "packetlog";
|
||||||
|
Description = "Logs a given number of packets to an xml file. Usage: packetlog 10 tenpackets.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length != 2)
|
||||||
|
return "Usage: packetlog 10 tenpackets.xml";
|
||||||
|
|
||||||
|
XmlWriter writer;
|
||||||
|
NetworkManager.PacketCallback callback = new NetworkManager.PacketCallback(OnPacket);
|
||||||
|
|
||||||
|
Packets.Clear();
|
||||||
|
Done = false;
|
||||||
|
Count = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Total = Int32.Parse(args[0]);
|
||||||
|
writer = XmlWriter.Create(args[1]);
|
||||||
|
|
||||||
|
Client.Network.RegisterCallback(PacketType.Default, callback);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return "Usage: packetlog 10 tenpackets.xml (" + e + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!Done)
|
||||||
|
{
|
||||||
|
System.Threading.Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
Client.Network.UnregisterCallback(PacketType.Default, callback);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Helpers.PacketListToXml(Packets, writer);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return "Serialization failed: " + e.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Close();
|
||||||
|
Packets.Clear();
|
||||||
|
|
||||||
|
return "Exported " + Count + " packets to " + args[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPacket(Packet packet, Simulator simulator)
|
||||||
|
{
|
||||||
|
lock (Packets)
|
||||||
|
{
|
||||||
|
if (Count >= Total)
|
||||||
|
{
|
||||||
|
Done = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Packets.Add(packet);
|
||||||
|
Count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Utilities;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class ParcelInfoCommand : Command
|
||||||
|
{
|
||||||
|
private ParcelDownloader ParcelDownloader;
|
||||||
|
private ManualResetEvent ParcelsDownloaded = new ManualResetEvent(false);
|
||||||
|
private int ParcelCount = 0;
|
||||||
|
|
||||||
|
public ParcelInfoCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "parcelinfo";
|
||||||
|
Description = "Prints out info about all the parcels in this simulator";
|
||||||
|
|
||||||
|
ParcelDownloader = new ParcelDownloader(testClient);
|
||||||
|
ParcelDownloader.OnParcelsDownloaded += new ParcelDownloader.ParcelsDownloadedCallback(Parcels_OnParcelsDownloaded);
|
||||||
|
testClient.Network.OnDisconnected += new NetworkManager.DisconnectedCallback(Network_OnDisconnected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
ParcelDownloader.DownloadSimParcels(Client.Network.CurrentSim);
|
||||||
|
|
||||||
|
ParcelsDownloaded.Reset();
|
||||||
|
ParcelsDownloaded.WaitOne(20000, false);
|
||||||
|
|
||||||
|
if (Client.Network.CurrentSim != null)
|
||||||
|
return "Downloaded information for " + ParcelCount + " parcels in " + Client.Network.CurrentSim.Name;
|
||||||
|
else
|
||||||
|
return String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parcels_OnParcelsDownloaded(Simulator simulator, Dictionary<int, Parcel> Parcels, int[,] map)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<int, Parcel> parcel in Parcels)
|
||||||
|
{
|
||||||
|
WaterType type = ParcelDownloader.GetWaterType(map, parcel.Value.LocalID);
|
||||||
|
float delta = ParcelDownloader.GetHeightRange(map, parcel.Value.LocalID);
|
||||||
|
int deviation = ParcelDownloader.GetRectangularDeviation(parcel.Value.AABBMin, parcel.Value.AABBMax,
|
||||||
|
parcel.Value.Area);
|
||||||
|
|
||||||
|
Console.WriteLine("Parcels[{0}]: Name: \"{1}\", Description: \"{2}\" ACL Count: {3}, " +
|
||||||
|
"Location: {4}, Height Range: {5}, Shape Deviation: {6}", parcel.Key, parcel.Value.Name,
|
||||||
|
parcel.Value.Desc, parcel.Value.AccessList.Count, type.ToString(), delta, deviation);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParcelCount = Parcels.Count;
|
||||||
|
|
||||||
|
ParcelsDownloaded.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Network_OnDisconnected(NetworkManager.DisconnectType reason, string message)
|
||||||
|
{
|
||||||
|
ParcelsDownloaded.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class PrimCountCommand: Command
|
||||||
|
{
|
||||||
|
public PrimCountCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "primcount";
|
||||||
|
Description = "Shows the number of prims that have been received.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
lock (Client.SimPrims)
|
||||||
|
{
|
||||||
|
foreach (Dictionary<uint, Primitive> prims in Client.SimPrims.Values)
|
||||||
|
{
|
||||||
|
count += prims.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class QuitCommand: Command
|
||||||
|
{
|
||||||
|
public QuitCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "quit";
|
||||||
|
Description = "Log all avatars out and shut down";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
Client.ClientManager.LogoutAll();
|
||||||
|
Client.ClientManager.Running = false;
|
||||||
|
return "All avatars logged out";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class RegionInfoCommand : Command
|
||||||
|
{
|
||||||
|
public RegionInfoCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "regioninfo";
|
||||||
|
Description = "Prints out info about all the current region";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
output.AppendLine(Client.Network.CurrentSim.ToString());
|
||||||
|
output.Append("Access: ");
|
||||||
|
output.AppendLine(Client.Network.CurrentSim.Access.ToString());
|
||||||
|
output.Append("Flags: ");
|
||||||
|
output.AppendLine(Client.Network.CurrentSim.Flags.ToString());
|
||||||
|
output.Append("TerrainBase0: ");
|
||||||
|
output.AppendLine(Client.Network.CurrentSim.TerrainBase0.ToStringHyphenated());
|
||||||
|
output.Append("TerrainBase1: ");
|
||||||
|
output.AppendLine(Client.Network.CurrentSim.TerrainBase1.ToStringHyphenated());
|
||||||
|
output.Append("TerrainBase2: ");
|
||||||
|
output.AppendLine(Client.Network.CurrentSim.TerrainBase2.ToStringHyphenated());
|
||||||
|
output.Append("TerrainBase3: ");
|
||||||
|
output.AppendLine(Client.Network.CurrentSim.TerrainBase3.ToStringHyphenated());
|
||||||
|
output.Append("TerrainDetail0: ");
|
||||||
|
output.AppendLine(Client.Network.CurrentSim.TerrainDetail0.ToStringHyphenated());
|
||||||
|
output.Append("TerrainDetail1: ");
|
||||||
|
output.AppendLine(Client.Network.CurrentSim.TerrainDetail1.ToStringHyphenated());
|
||||||
|
output.Append("TerrainDetail2: ");
|
||||||
|
output.AppendLine(Client.Network.CurrentSim.TerrainDetail2.ToStringHyphenated());
|
||||||
|
output.Append("TerrainDetail3: ");
|
||||||
|
output.AppendLine(Client.Network.CurrentSim.TerrainDetail3.ToStringHyphenated());
|
||||||
|
output.Append("Water Height: ");
|
||||||
|
output.AppendLine(Client.Network.CurrentSim.WaterHeight.ToString());
|
||||||
|
|
||||||
|
return output.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class SetMasterCommand: Command
|
||||||
|
{
|
||||||
|
public DateTime Created = DateTime.Now;
|
||||||
|
private LLUUID resolvedMasterKey = LLUUID.Zero;
|
||||||
|
private ManualResetEvent keyResolution = new ManualResetEvent(false);
|
||||||
|
private LLUUID query = LLUUID.Zero;
|
||||||
|
|
||||||
|
public SetMasterCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "setMaster";
|
||||||
|
Description = "Sets the user name of the master user. The master user can IM to run commands.";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
string masterName = String.Empty;
|
||||||
|
for (int ct = 0; ct < args.Length;ct++)
|
||||||
|
masterName = masterName + args[ct] + " ";
|
||||||
|
masterName = masterName.TrimEnd();
|
||||||
|
|
||||||
|
if (masterName.Length == 0)
|
||||||
|
return "Usage setMaster name";
|
||||||
|
|
||||||
|
DirectoryManager.DirPeopleReplyCallback callback = new DirectoryManager.DirPeopleReplyCallback(KeyResolvHandler);
|
||||||
|
Client.Directory.OnDirPeopleReply += callback;
|
||||||
|
query = Client.Directory.StartPeopleSearch(DirectoryManager.DirFindFlags.People, masterName);
|
||||||
|
if (keyResolution.WaitOne(TimeSpan.FromMinutes(1), false))
|
||||||
|
{
|
||||||
|
Client.MasterKey = resolvedMasterKey;
|
||||||
|
keyResolution.Reset();
|
||||||
|
Client.Directory.OnDirPeopleReply -= callback;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
keyResolution.Reset();
|
||||||
|
Client.Directory.OnDirPeopleReply -= callback;
|
||||||
|
return "Unable to obtain UUID for \"" + masterName + "\". Master unchanged.";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (Avatar av in Client.AvatarList.Values)
|
||||||
|
{
|
||||||
|
if (av.ID == Client.MasterKey)
|
||||||
|
{
|
||||||
|
Client.Self.InstantMessage(av.ID, "You are now my master. IM me with \"help\" for a command list.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Master set to " + masterName + " (" + Client.MasterKey.ToStringHyphenated() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void KeyResolvHandler(LLUUID queryid, List<DirectoryManager.AgentSearchData> matches)
|
||||||
|
{
|
||||||
|
if (query != queryid)
|
||||||
|
return;
|
||||||
|
// We can't handle ambiguities here as nicely as we can in ClientManager.
|
||||||
|
resolvedMasterKey = matches[0].AgentID;
|
||||||
|
keyResolution.Set();
|
||||||
|
query = LLUUID.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class SetMasterKeyCommand : Command
|
||||||
|
{
|
||||||
|
public DateTime Created = DateTime.Now;
|
||||||
|
|
||||||
|
public SetMasterKeyCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "setMasterKey";
|
||||||
|
Description = "Sets the key of the master user. The master user can IM to run commands.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
Client.MasterKey = LLUUID.Parse(args[0]);
|
||||||
|
|
||||||
|
foreach (Avatar av in Client.AvatarList.Values)
|
||||||
|
{
|
||||||
|
if (av.ID == Client.MasterKey)
|
||||||
|
{
|
||||||
|
Client.Self.InstantMessage(av.ID, "You are now my master. IM me with \"help\" for a command list.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Master set to " + Client.MasterKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
using System;
|
||||||
|
using libsecondlife;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class ShowEffectsCommand : Command
|
||||||
|
{
|
||||||
|
bool ShowEffects = false;
|
||||||
|
|
||||||
|
public ShowEffectsCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "showeffects";
|
||||||
|
Description = "Prints out information for every viewer effect that is received. Usage: showeffects [on/off]";
|
||||||
|
|
||||||
|
testClient.Avatars.OnEffect += new AvatarManager.EffectCallback(Avatars_OnEffect);
|
||||||
|
testClient.Avatars.OnLookAt += new AvatarManager.LookAtCallback(Avatars_OnLookAt);
|
||||||
|
testClient.Avatars.OnPointAt += new AvatarManager.PointAtCallback(Avatars_OnPointAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length == 0)
|
||||||
|
{
|
||||||
|
ShowEffects = true;
|
||||||
|
return "Viewer effects will be shown on the console";
|
||||||
|
}
|
||||||
|
else if (args.Length == 1)
|
||||||
|
{
|
||||||
|
if (args[0] == "on")
|
||||||
|
{
|
||||||
|
ShowEffects = true;
|
||||||
|
return "Viewer effects will be shown on the console";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ShowEffects = false;
|
||||||
|
return "Viewer effects will not be shown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Usage: showeffects [on/off]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Avatars_OnPointAt(LLUUID sourceID, LLUUID targetID, LLVector3d targetPos,
|
||||||
|
MainAvatar.PointAtType pointType, float duration, LLUUID id)
|
||||||
|
{
|
||||||
|
if (ShowEffects)
|
||||||
|
Console.WriteLine(
|
||||||
|
"ViewerEffect [PointAt]: SourceID: {0} TargetID: {1} TargetPos: {2} Type: {3} Duration: {4} ID: {5}",
|
||||||
|
sourceID.ToStringHyphenated(), targetID.ToStringHyphenated(), targetPos, pointType, duration,
|
||||||
|
id.ToStringHyphenated());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Avatars_OnLookAt(LLUUID sourceID, LLUUID targetID, LLVector3d targetPos,
|
||||||
|
MainAvatar.LookAtType lookType, float duration, LLUUID id)
|
||||||
|
{
|
||||||
|
if (ShowEffects)
|
||||||
|
Console.WriteLine(
|
||||||
|
"ViewerEffect [LookAt]: SourceID: {0} TargetID: {1} TargetPos: {2} Type: {3} Duration: {4} ID: {5}",
|
||||||
|
sourceID.ToStringHyphenated(), targetID.ToStringHyphenated(), targetPos, lookType, duration,
|
||||||
|
id.ToStringHyphenated());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Avatars_OnEffect(MainAvatar.EffectType type, LLUUID sourceID, LLUUID targetID,
|
||||||
|
LLVector3d targetPos, float duration, LLUUID id)
|
||||||
|
{
|
||||||
|
if (ShowEffects)
|
||||||
|
Console.WriteLine(
|
||||||
|
"ViewerEffect [{0}]: SourceID: {1} TargetID: {2} TargetPos: {3} Duration: {4} ID: {5}",
|
||||||
|
type, sourceID.ToStringHyphenated(), targetID.ToStringHyphenated(), targetPos, duration,
|
||||||
|
id.ToStringHyphenated());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class StatsCommand : Command
|
||||||
|
{
|
||||||
|
public StatsCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "stats";
|
||||||
|
Description = "Provide connection figures and statistics";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
|
||||||
|
lock (Client.Network.Simulators)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Client.Network.Simulators.Count; i++)
|
||||||
|
{
|
||||||
|
Simulator sim = Client.Network.Simulators[i];
|
||||||
|
|
||||||
|
output.AppendLine(String.Format(
|
||||||
|
"[{0}] Dilation: {1} InBPS: {2} OutBPS: {3} ResentOut: {4} ResentIn: {5}",
|
||||||
|
sim.ToString(), sim.Dilation, sim.IncomingBPS, sim.OutgoingBPS, sim.ResentPackets,
|
||||||
|
sim.ReceivedResends));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output.Append("Packets in the queue: " + Client.Network.InboxCount);
|
||||||
|
|
||||||
|
return output.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class TouchCommand: Command
|
||||||
|
{
|
||||||
|
public TouchCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "touch";
|
||||||
|
Description = "Attempt to touch a prim with specified UUID";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
Primitive target = null;
|
||||||
|
|
||||||
|
lock (Client.SimPrims)
|
||||||
|
{
|
||||||
|
if (Client.SimPrims.ContainsKey(Client.Network.CurrentSim))
|
||||||
|
{
|
||||||
|
foreach (Primitive p in Client.SimPrims[Client.Network.CurrentSim].Values)
|
||||||
|
{
|
||||||
|
if (args.Length == 0)
|
||||||
|
return "You must specify a UUID of the prim.";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (p.ID == args[0])
|
||||||
|
target = p;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// handle exception
|
||||||
|
return "Sorry, I don't think " + args[0] + " is a valid UUID. I'm unable to touch it.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target != null)
|
||||||
|
{
|
||||||
|
Client.Self.Touch(target.LocalID);
|
||||||
|
return "Touched prim " + target.ID + ".";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Couldn't find that prim.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class TreeCommand: Command
|
||||||
|
{
|
||||||
|
public TreeCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "tree";
|
||||||
|
Description = "Rez a tree.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
if (args.Length == 1)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string treeName = args[0].Trim(new char[] { ' ' });
|
||||||
|
ObjectManager.Tree tree = (ObjectManager.Tree)Enum.Parse(typeof(ObjectManager.Tree), treeName);
|
||||||
|
|
||||||
|
LLVector3 treePosition = new LLVector3(Client.Self.Position.X, Client.Self.Position.Y,
|
||||||
|
Client.Self.Position.Z);
|
||||||
|
treePosition.Z += 3.0f;
|
||||||
|
|
||||||
|
Client.Objects.AddTree(Client.Network.CurrentSim, new LLVector3(0.5f, 0.5f, 0.5f),
|
||||||
|
LLQuaternion.Identity, treePosition, tree, Client.GroupID, false);
|
||||||
|
|
||||||
|
return "Attempted to rez a " + treeName + " tree";
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return "Type !tree for usage";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string usage = "Usage: !tree [";
|
||||||
|
foreach (string value in Enum.GetNames(typeof(ObjectManager.Tree)))
|
||||||
|
{
|
||||||
|
usage += value + ",";
|
||||||
|
}
|
||||||
|
usage = usage.TrimEnd(new char[] { ',' });
|
||||||
|
usage += "]";
|
||||||
|
return usage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class UptimeCommand : Command
|
||||||
|
{
|
||||||
|
public DateTime Created = DateTime.Now;
|
||||||
|
|
||||||
|
public UptimeCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "uptime";
|
||||||
|
Description = "Shows the login name, login time and length of time logged on.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
string name = Client.ToString();
|
||||||
|
return "I am " + name + ", Up Since: " + Created + " (" + (DateTime.Now - Created) + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class WhoCommand: Command
|
||||||
|
{
|
||||||
|
public WhoCommand(TestClient testClient)
|
||||||
|
{
|
||||||
|
Name = "who";
|
||||||
|
Description = "Lists seen avatars.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Execute(string[] args, LLUUID fromAgentID)
|
||||||
|
{
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
foreach (Avatar av in Client.AvatarList.Values)
|
||||||
|
{
|
||||||
|
result.AppendFormat("\n{0} {1} {2}/{3} ID: {4}", av.Name, av.GroupName,
|
||||||
|
(av.CurrentSim != null ? av.CurrentSim.Name : String.Empty), av.Position, av.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient {
|
||||||
|
class Parsing {
|
||||||
|
public static string[] ParseArguments(string str) {
|
||||||
|
List<string> list = new List<string>();
|
||||||
|
string current = "";
|
||||||
|
string trimmed = null;
|
||||||
|
bool withinQuote = false;
|
||||||
|
bool escaped = false;
|
||||||
|
foreach (char c in str) {
|
||||||
|
if (c == '"') {
|
||||||
|
if (escaped) {
|
||||||
|
current += '"';
|
||||||
|
escaped = false;
|
||||||
|
} else {
|
||||||
|
current += '"';
|
||||||
|
withinQuote = !withinQuote;
|
||||||
|
}
|
||||||
|
} else if (c == ' ' || c == '\t') {
|
||||||
|
if (escaped || withinQuote) {
|
||||||
|
current += c;
|
||||||
|
escaped = false;
|
||||||
|
} else {
|
||||||
|
trimmed = current.Trim();
|
||||||
|
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 = "";
|
||||||
|
}
|
||||||
|
} else if (c == '\\') {
|
||||||
|
if (escaped) {
|
||||||
|
current += '\\';
|
||||||
|
escaped = false;
|
||||||
|
} else {
|
||||||
|
escaped = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (escaped)
|
||||||
|
throw new FormatException(c.ToString() + " is not an escapable character.");
|
||||||
|
current += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trimmed = current.Trim();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using CommandLine.Utility;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
private static void Usage()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Usage: " + Environment.NewLine +
|
||||||
|
"TestClient.exe --first firstname --last lastname --pass password --contact \"youremail\" [--startpos \"sim/x/y/z\"] [--master \"master name\"] [--masterkey \"master uuid\"]" +
|
||||||
|
Environment.NewLine + "TestClient.exe --file filename --contact \"youremail\" [--master \"master name\"] [--masterkey \"master uuid\"]");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Arguments arguments = new Arguments(args);
|
||||||
|
|
||||||
|
ClientManager manager;
|
||||||
|
List<LoginDetails> accounts = new List<LoginDetails>();
|
||||||
|
LoginDetails account;
|
||||||
|
string masterName = String.Empty;
|
||||||
|
LLUUID masterKey = LLUUID.Zero;
|
||||||
|
string file = String.Empty;
|
||||||
|
string contact = String.Empty;
|
||||||
|
|
||||||
|
if (arguments["masterkey"] != null)
|
||||||
|
{
|
||||||
|
masterKey = LLUUID.Parse(arguments["masterkey"]);
|
||||||
|
}
|
||||||
|
if (arguments["master"] != null)
|
||||||
|
{
|
||||||
|
masterName = arguments["master"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments["contact"] != null)
|
||||||
|
{
|
||||||
|
contact = arguments["contact"];
|
||||||
|
if (arguments["file"] != null)
|
||||||
|
{
|
||||||
|
file = arguments["file"];
|
||||||
|
|
||||||
|
// Loading names from a file
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (StreamReader reader = new StreamReader(file))
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
int lineNumber = 0;
|
||||||
|
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
lineNumber++;
|
||||||
|
string[] tokens = line.Trim().Split(new char[] { ' ', ',' });
|
||||||
|
|
||||||
|
if (tokens.Length >= 3)
|
||||||
|
{
|
||||||
|
account = new LoginDetails();
|
||||||
|
account.FirstName = tokens[0];
|
||||||
|
account.LastName = tokens[1];
|
||||||
|
account.Password = tokens[2];
|
||||||
|
|
||||||
|
accounts.Add(account);
|
||||||
|
|
||||||
|
// Leaving this out until we have per-account masters (if that
|
||||||
|
// is desirable). For now the command-line option can
|
||||||
|
// specify the single master that TestClient supports
|
||||||
|
|
||||||
|
//if (tokens.Length == 5)
|
||||||
|
//{
|
||||||
|
// master = tokens[3] + " " + tokens[4];
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Invalid data on line " + lineNumber +
|
||||||
|
", must be in the format of: FirstName LastName Password");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error reading from " + args[1]);
|
||||||
|
Console.WriteLine(e.ToString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (arguments["first"] != null && arguments["last"] != null && arguments["pass"] != null)
|
||||||
|
{
|
||||||
|
// Taking a single login off the command-line
|
||||||
|
account = new LoginDetails();
|
||||||
|
account.FirstName = arguments["first"];
|
||||||
|
account.LastName = arguments["last"];
|
||||||
|
account.Password = arguments["pass"];
|
||||||
|
|
||||||
|
accounts.Add(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (LoginDetails a in accounts)
|
||||||
|
{
|
||||||
|
a.MasterName = masterName;
|
||||||
|
a.MasterKey = masterKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login the accounts and run the input loop
|
||||||
|
if ( arguments["start"] != null ) {
|
||||||
|
manager = new ClientManager(accounts, contact, arguments["start"]);
|
||||||
|
} else {
|
||||||
|
manager = new ClientManager(accounts, contact);
|
||||||
|
}
|
||||||
|
manager.Run();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("TestClient")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("TestClient")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2006")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("0563f706-7fa9-42f6-bf23-c6acd1175964")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,5 @@
|
||||||
|
This is a fork of TestClient from the main libsl project, it is not built as a normal part of the opensim release and is designed to allow migration of avatars from the main LL grid to an OGS system.
|
||||||
|
|
||||||
|
It's essentially a "good CopyBot" - before an avatar is copied consent is taken and the avatar data is exported out to the remote grid via a web service. The bot is designed to be evoked remotely via an LSL script in-world.
|
||||||
|
|
||||||
|
THE AUTHOR ACCEPTS NO RESPONSIBILITY WHATSOEVER FOR ANY POTENTIALLY INFRINGING USES OF THIS CODE.
|
|
@ -0,0 +1,125 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<project
|
||||||
|
name="libsecondlife"
|
||||||
|
default="build">
|
||||||
|
|
||||||
|
<!-- global framework settings -->
|
||||||
|
<property
|
||||||
|
name="target.framework"
|
||||||
|
value="${framework::get-target-framework()}" />
|
||||||
|
<property
|
||||||
|
name="assembly.dir"
|
||||||
|
value="${framework::get-assembly-directory(target.framework)}" />
|
||||||
|
|
||||||
|
<!-- global project settings -->
|
||||||
|
<xmlpeek
|
||||||
|
file="../../../libsecondlife.build"
|
||||||
|
xpath="/project/property[@name = 'project.version']/@value"
|
||||||
|
property="project.version" />
|
||||||
|
<property
|
||||||
|
name="build.number"
|
||||||
|
value="${math::abs(math::floor(timespan::get-total-days(datetime::now()
|
||||||
|
- datetime::parse('01/01/2002'))))}" />
|
||||||
|
<property
|
||||||
|
name="assembly"
|
||||||
|
value="TestClient"/>
|
||||||
|
<property
|
||||||
|
name="bin_dir"
|
||||||
|
value="../../../bin" />
|
||||||
|
|
||||||
|
<!-- default configuration -->
|
||||||
|
<property
|
||||||
|
name="project.config"
|
||||||
|
value="debug" /> <!-- debug|release -->
|
||||||
|
|
||||||
|
<!-- named configurations -->
|
||||||
|
<target
|
||||||
|
name="init"
|
||||||
|
description="Initializes build properties">
|
||||||
|
<call target="${project.config}" />
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target
|
||||||
|
name="debug"
|
||||||
|
description="configures a debug build">
|
||||||
|
<property
|
||||||
|
name="build.debug"
|
||||||
|
value="true" />
|
||||||
|
<property
|
||||||
|
name="package.name"
|
||||||
|
value="${project::get-name()}-${project.version}-${project.config}" />
|
||||||
|
<property
|
||||||
|
name="assembly.configuration"
|
||||||
|
value="${framework::get-target-framework()}.${platform::get-name()} [${project.config}]" />
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target
|
||||||
|
name="release"
|
||||||
|
description="configures a release build">
|
||||||
|
<property
|
||||||
|
name="project.config"
|
||||||
|
value="release" />
|
||||||
|
<property
|
||||||
|
name="build.debug"
|
||||||
|
value="false" />
|
||||||
|
<property
|
||||||
|
name="package.name"
|
||||||
|
value="${project::get-name()}-${project.version}" />
|
||||||
|
<property
|
||||||
|
name="assembly.configuration"
|
||||||
|
value="${framework::get-target-framework()}.${platform::get-name()}" />
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- build tasks -->
|
||||||
|
<target
|
||||||
|
name="build"
|
||||||
|
depends="init"
|
||||||
|
description="Builds the binaries for the current configuration">
|
||||||
|
<echo message="Build Directory is ${bin_dir}/" />
|
||||||
|
<mkdir
|
||||||
|
dir="${bin_dir}"
|
||||||
|
failonerror="false" />
|
||||||
|
<csc
|
||||||
|
target="exe"
|
||||||
|
debug="${build.debug}"
|
||||||
|
output="${bin_dir}/${assembly}.exe">
|
||||||
|
<sources failonempty="true">
|
||||||
|
<include name="*.cs" />
|
||||||
|
<include name="Commands/**.cs" />
|
||||||
|
<exclude name="Commands/Communication/TtsCommand.cs" />
|
||||||
|
</sources>
|
||||||
|
<references basedir="${bin_dir}/">
|
||||||
|
<include name="libsecondlife.dll"/>
|
||||||
|
<include name="openjpegnet.dll"/>
|
||||||
|
</references>
|
||||||
|
</csc>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target
|
||||||
|
name="build-dll"
|
||||||
|
description="Builds libsecondlife dll">
|
||||||
|
<nant
|
||||||
|
buildfile="../../libsecondlife-cs/libsecondlife.build"
|
||||||
|
target="${project.config} build"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target
|
||||||
|
name="clean"
|
||||||
|
depends="init"
|
||||||
|
description="Deletes the current configuration">
|
||||||
|
<delete failonerror="false">
|
||||||
|
<fileset basedir="${bin_dir}/">
|
||||||
|
<include name="${assembly}.exe" />
|
||||||
|
<include name="${assembly}.pdb" />
|
||||||
|
<include name="**/${assembly}.*.resources" />
|
||||||
|
</fileset>
|
||||||
|
</delete>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target
|
||||||
|
name="*"
|
||||||
|
description="Handles unknown targets">
|
||||||
|
<echo message="skip" />
|
||||||
|
</target>
|
||||||
|
</project>
|
|
@ -0,0 +1,328 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Xml;
|
||||||
|
using libsecondlife;
|
||||||
|
using libsecondlife.Packets;
|
||||||
|
using libsecondlife.AssetSystem;
|
||||||
|
|
||||||
|
namespace libsecondlife.TestClient
|
||||||
|
{
|
||||||
|
public class TestClient : SecondLife
|
||||||
|
{
|
||||||
|
public delegate void PrimCreatedCallback(Simulator simulator, Primitive prim);
|
||||||
|
|
||||||
|
public event PrimCreatedCallback OnPrimCreated;
|
||||||
|
|
||||||
|
public Dictionary<Simulator, Dictionary<uint, Primitive>> SimPrims;
|
||||||
|
public LLUUID GroupID = LLUUID.Zero;
|
||||||
|
public Dictionary<LLUUID, GroupMember> GroupMembers;
|
||||||
|
public Dictionary<uint, Avatar> AvatarList = new Dictionary<uint,Avatar>();
|
||||||
|
public Dictionary<LLUUID, AvatarAppearancePacket> Appearances = new Dictionary<LLUUID, AvatarAppearancePacket>();
|
||||||
|
public Dictionary<string, Command> Commands = new Dictionary<string,Command>();
|
||||||
|
public bool Running = true;
|
||||||
|
public string MasterName = String.Empty;
|
||||||
|
public LLUUID MasterKey = LLUUID.Zero;
|
||||||
|
public ClientManager ClientManager;
|
||||||
|
public int regionX;
|
||||||
|
public int regionY;
|
||||||
|
|
||||||
|
//internal libsecondlife.InventorySystem.InventoryFolder currentDirectory;
|
||||||
|
|
||||||
|
private LLQuaternion bodyRotation = LLQuaternion.Identity;
|
||||||
|
private LLVector3 forward = new LLVector3(0, 0.9999f, 0);
|
||||||
|
private LLVector3 left = new LLVector3(0.9999f, 0, 0);
|
||||||
|
private LLVector3 up = new LLVector3(0, 0, 0.9999f);
|
||||||
|
private System.Timers.Timer updateTimer;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public TestClient(ClientManager manager)
|
||||||
|
{
|
||||||
|
ClientManager = manager;
|
||||||
|
|
||||||
|
updateTimer = new System.Timers.Timer(1000);
|
||||||
|
updateTimer.Elapsed += new System.Timers.ElapsedEventHandler(updateTimer_Elapsed);
|
||||||
|
|
||||||
|
RegisterAllCommands(Assembly.GetExecutingAssembly());
|
||||||
|
|
||||||
|
Settings.DEBUG = true;
|
||||||
|
Settings.STORE_LAND_PATCHES = true;
|
||||||
|
Settings.ALWAYS_REQUEST_OBJECTS = true;
|
||||||
|
|
||||||
|
Network.RegisterCallback(PacketType.AgentDataUpdate, new NetworkManager.PacketCallback(AgentDataUpdateHandler));
|
||||||
|
|
||||||
|
Objects.OnNewPrim += new ObjectManager.NewPrimCallback(Objects_OnNewPrim);
|
||||||
|
Objects.OnObjectUpdated += new ObjectManager.ObjectUpdatedCallback(Objects_OnObjectUpdated);
|
||||||
|
Objects.OnObjectKilled += new ObjectManager.KillObjectCallback(Objects_OnObjectKilled);
|
||||||
|
Objects.OnNewAvatar += new ObjectManager.NewAvatarCallback(Objects_OnNewAvatar);
|
||||||
|
Self.OnInstantMessage += new MainAvatar.InstantMessageCallback(Self_OnInstantMessage);
|
||||||
|
Groups.OnGroupMembers += new GroupManager.GroupMembersCallback(GroupMembersHandler);
|
||||||
|
this.OnLogMessage += new LogCallback(TestClient_OnLogMessage);
|
||||||
|
|
||||||
|
Network.RegisterCallback(PacketType.AvatarAppearance, new NetworkManager.PacketCallback(AvatarAppearanceHandler));
|
||||||
|
|
||||||
|
updateTimer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterAllCommands(Assembly assembly)
|
||||||
|
{
|
||||||
|
foreach (Type t in assembly.GetTypes())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (t.IsSubclassOf(typeof(Command)))
|
||||||
|
{
|
||||||
|
ConstructorInfo info = t.GetConstructor(new Type[] { typeof(TestClient) });
|
||||||
|
Command command = (Command)info.Invoke(new object[] { this });
|
||||||
|
RegisterCommand(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterCommand(Command command)
|
||||||
|
{
|
||||||
|
command.Client = this;
|
||||||
|
if (!Commands.ContainsKey(command.Name.ToLower()))
|
||||||
|
{
|
||||||
|
Commands.Add(command.Name.ToLower(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//breaks up large responses to deal with the max IM size
|
||||||
|
private void SendResponseIM(SecondLife client, LLUUID fromAgentID, string data, LLUUID imSessionID)
|
||||||
|
{
|
||||||
|
for ( int i = 0 ; i < data.Length ; i += 1024 ) {
|
||||||
|
int y;
|
||||||
|
if ((i + 1023) > data.Length)
|
||||||
|
{
|
||||||
|
y = data.Length - i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
y = 1023;
|
||||||
|
}
|
||||||
|
string message = data.Substring(i, y);
|
||||||
|
client.Self.InstantMessage(fromAgentID, message, imSessionID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoCommand(string cmd, LLUUID fromAgentID, LLUUID imSessionID)
|
||||||
|
{
|
||||||
|
string[] tokens = Parsing.ParseArguments(cmd);
|
||||||
|
|
||||||
|
if (tokens.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string firstToken = tokens[0].ToLower();
|
||||||
|
|
||||||
|
// "all balance" will send the balance command to all currently logged in bots
|
||||||
|
if (firstToken == "all" && tokens.Length > 1)
|
||||||
|
{
|
||||||
|
cmd = String.Empty;
|
||||||
|
|
||||||
|
// Reserialize all of the arguments except for "all"
|
||||||
|
for (int i = 1; i < tokens.Length; i++)
|
||||||
|
{
|
||||||
|
cmd += tokens[i] + " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientManager.DoCommandAll(cmd, fromAgentID, imSessionID);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Commands.ContainsKey(firstToken))
|
||||||
|
{
|
||||||
|
string[] args = new string[tokens.Length - 1];
|
||||||
|
Array.Copy(tokens, 1, args, 0, args.Length);
|
||||||
|
string response = response = Commands[firstToken].Execute(args, fromAgentID);
|
||||||
|
|
||||||
|
if (response.Length > 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine(response);
|
||||||
|
|
||||||
|
if (fromAgentID != null && Network.Connected)
|
||||||
|
{
|
||||||
|
// IMs don't like \r\n line endings, clean them up first
|
||||||
|
response = response.Replace("\r", "");
|
||||||
|
SendResponseIM(this, fromAgentID, response, imSessionID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||||
|
{
|
||||||
|
foreach (Command c in Commands.Values)
|
||||||
|
if (c.Active)
|
||||||
|
c.Think();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AgentDataUpdateHandler(Packet packet, Simulator sim)
|
||||||
|
{
|
||||||
|
AgentDataUpdatePacket p = (AgentDataUpdatePacket)packet;
|
||||||
|
if (p.AgentData.AgentID == sim.Client.Network.AgentID)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Got the group ID for " + sim.Client.ToString() + ", requesting group members...");
|
||||||
|
GroupID = p.AgentData.ActiveGroupID;
|
||||||
|
|
||||||
|
sim.Client.Groups.BeginGetGroupMembers(GroupID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TestClient_OnLogMessage(string message, Helpers.LogLevel level)
|
||||||
|
{
|
||||||
|
Console.WriteLine("<" + this.ToString() + "> " + level.ToString() + ": " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GroupMembersHandler(Dictionary<LLUUID, GroupMember> members)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Got " + members.Count + " group members.");
|
||||||
|
GroupMembers = members;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Objects_OnObjectKilled(Simulator simulator, uint objectID)
|
||||||
|
{
|
||||||
|
lock (SimPrims)
|
||||||
|
{
|
||||||
|
if (SimPrims.ContainsKey(simulator) && SimPrims[simulator].ContainsKey(objectID))
|
||||||
|
SimPrims[simulator].Remove(objectID);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (AvatarList)
|
||||||
|
{
|
||||||
|
if (AvatarList.ContainsKey(objectID))
|
||||||
|
AvatarList.Remove(objectID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Objects_OnObjectUpdated(Simulator simulator, ObjectUpdate update, ulong regionHandle, ushort timeDilation)
|
||||||
|
{
|
||||||
|
regionX = (int)(regionHandle >> 32);
|
||||||
|
regionY = (int)(regionHandle & 0xFFFFFFFF);
|
||||||
|
|
||||||
|
if (update.Avatar)
|
||||||
|
{
|
||||||
|
lock (AvatarList)
|
||||||
|
{
|
||||||
|
// TODO: We really need a solid avatar and object tracker in Utilities to use here
|
||||||
|
if (AvatarList.ContainsKey(update.LocalID))
|
||||||
|
{
|
||||||
|
AvatarList[update.LocalID].CollisionPlane = update.CollisionPlane;
|
||||||
|
AvatarList[update.LocalID].Position = update.Position;
|
||||||
|
AvatarList[update.LocalID].Velocity = update.Velocity;
|
||||||
|
AvatarList[update.LocalID].Acceleration = update.Acceleration;
|
||||||
|
AvatarList[update.LocalID].Rotation = update.Rotation;
|
||||||
|
AvatarList[update.LocalID].AngularVelocity = update.AngularVelocity;
|
||||||
|
AvatarList[update.LocalID].Textures = update.Textures;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lock (SimPrims)
|
||||||
|
{
|
||||||
|
if (SimPrims.ContainsKey(simulator) && SimPrims[simulator].ContainsKey(update.LocalID))
|
||||||
|
{
|
||||||
|
SimPrims[simulator][update.LocalID].Position = update.Position;
|
||||||
|
SimPrims[simulator][update.LocalID].Velocity = update.Velocity;
|
||||||
|
SimPrims[simulator][update.LocalID].Acceleration = update.Acceleration;
|
||||||
|
SimPrims[simulator][update.LocalID].Rotation = update.Rotation;
|
||||||
|
SimPrims[simulator][update.LocalID].AngularVelocity = update.AngularVelocity;
|
||||||
|
SimPrims[simulator][update.LocalID].Textures = update.Textures;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Objects_OnNewPrim(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation)
|
||||||
|
{
|
||||||
|
lock (SimPrims)
|
||||||
|
{
|
||||||
|
if (!SimPrims.ContainsKey(simulator))
|
||||||
|
{
|
||||||
|
SimPrims[simulator] = new Dictionary<uint, Primitive>(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
SimPrims[simulator][prim.LocalID] = prim;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((prim.Flags & LLObject.ObjectFlags.CreateSelected) != 0 && OnPrimCreated != null)
|
||||||
|
{
|
||||||
|
OnPrimCreated(simulator, prim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation)
|
||||||
|
{
|
||||||
|
lock (AvatarList)
|
||||||
|
{
|
||||||
|
AvatarList[avatar.LocalID] = avatar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AvatarAppearanceHandler(Packet packet, Simulator simulator)
|
||||||
|
{
|
||||||
|
AvatarAppearancePacket appearance = (AvatarAppearancePacket)packet;
|
||||||
|
|
||||||
|
lock (Appearances) Appearances[appearance.Sender.ID] = appearance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Self_OnInstantMessage(LLUUID fromAgentID, string fromAgentName, LLUUID toAgentID,
|
||||||
|
uint parentEstateID, LLUUID regionID, LLVector3 position, MainAvatar.InstantMessageDialog dialog,
|
||||||
|
bool groupIM, LLUUID imSessionID, DateTime timestamp, string message,
|
||||||
|
MainAvatar.InstantMessageOnline offline, byte[] binaryBucket)
|
||||||
|
{
|
||||||
|
if (MasterKey != LLUUID.Zero)
|
||||||
|
{
|
||||||
|
if (fromAgentID != MasterKey)
|
||||||
|
{
|
||||||
|
// Received an IM from someone that is not the bot's master, ignore
|
||||||
|
Console.WriteLine("<IM>" + fromAgentName + " (not master): " + message + "@" + regionID.ToString() + ":" + position.ToString() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GroupMembers != null && !GroupMembers.ContainsKey(fromAgentID))
|
||||||
|
{
|
||||||
|
// Received an IM from someone outside the bot's group, ignore
|
||||||
|
Console.WriteLine("<IM>" + fromAgentName + " (not in group): " + message + "@" + regionID.ToString() + ":" + position.ToString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("<IM>" + fromAgentName + ": " + message);
|
||||||
|
|
||||||
|
if (dialog == MainAvatar.InstantMessageDialog.RequestTeleport)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Accepting teleport lure.");
|
||||||
|
Self.TeleportLureRespond(fromAgentID, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (dialog == MainAvatar.InstantMessageDialog.InventoryOffered)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Accepting inventory offer.");
|
||||||
|
|
||||||
|
Self.InstantMessage(Self.FirstName + " " + Self.LastName, fromAgentID, String.Empty,
|
||||||
|
imSessionID, MainAvatar.InstantMessageDialog.InventoryAccepted,
|
||||||
|
MainAvatar.InstantMessageOnline.Offline, Self.Position, LLUUID.Zero,
|
||||||
|
Self.InventoryRootFolderUUID.GetBytes());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DoCommand(message, fromAgentID, imSessionID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProductVersion>8.0.50727</ProductVersion>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<ProjectGuid>{B87682F6-B2D7-4C4D-A529-400C24FD4880}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>libsecondlife.TestClient</RootNamespace>
|
||||||
|
<AssemblyName>TestClient</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>..\..\..\bin\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>..\..\bin\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Speech, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Arguments.cs" />
|
||||||
|
<Compile Include="Command.cs" />
|
||||||
|
<Compile Include="Commands\AppearanceCommand.cs" />
|
||||||
|
<Compile Include="Commands\CloneProfileCommand.cs" />
|
||||||
|
<Compile Include="Commands\DebugCommand.cs" />
|
||||||
|
<Compile Include="Commands\DumpOutfitCommand.cs" />
|
||||||
|
<Compile Include="Commands\ExportParticlesCommand.cs" />
|
||||||
|
<Compile Include="Commands\Inventory\BalanceCommand.cs" />
|
||||||
|
<Compile Include="Commands\Inventory\DeleteFolderCommand.cs" />
|
||||||
|
<Compile Include="Commands\Inventory\GiveAllCommand.cs" />
|
||||||
|
<Compile Include="Commands\Inventory\WearCommand.cs" />
|
||||||
|
<Compile Include="Commands\Inventory\InventoryCommand.cs" />
|
||||||
|
<Compile Include="Commands\ExportOutfitCommand.cs" />
|
||||||
|
<Compile Include="Commands\FindSimCommand.cs" />
|
||||||
|
<Compile Include="Commands\ImportOutfitCommand.cs" />
|
||||||
|
<Compile Include="Commands\LoginCommand.cs">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Commands\LogoutCommand.cs">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Commands\Communication\EchoMasterCommand.cs" />
|
||||||
|
<Compile Include="Commands\Communication\IMCommand.cs" />
|
||||||
|
<Compile Include="Commands\Communication\SayCommand.cs" />
|
||||||
|
<Compile Include="Commands\Communication\ShoutCommand.cs" />
|
||||||
|
<Compile Include="Commands\Communication\WhisperCommand.cs" />
|
||||||
|
<Compile Include="Commands\MD5Command.cs" />
|
||||||
|
<Compile Include="Commands\Movement\FollowCommand.cs" />
|
||||||
|
<Compile Include="Commands\Movement\GotoCommand.cs" />
|
||||||
|
<Compile Include="Commands\Movement\JumpCommand.cs" />
|
||||||
|
<Compile Include="Commands\Movement\LocationCommand.cs" />
|
||||||
|
<Compile Include="Commands\Movement\SitCommand.cs" />
|
||||||
|
<Compile Include="Commands\PacketLogCommand.cs" />
|
||||||
|
<Compile Include="Commands\ParcelInfoCommand.cs" />
|
||||||
|
<Compile Include="Commands\QuitCommand.cs">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Commands\RegionInfoCommand.cs" />
|
||||||
|
<Compile Include="Commands\SetMasterCommand.cs">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Commands\ExportCommand.cs" />
|
||||||
|
<Compile Include="Commands\ImportCommand.cs" />
|
||||||
|
<Compile Include="Commands\LoadCommand.cs" />
|
||||||
|
<Compile Include="Commands\PrimCountCommand.cs" />
|
||||||
|
<Compile Include="Commands\SetMasterKeyCommand.cs" />
|
||||||
|
<Compile Include="Commands\ShowEffectsCommand.cs" />
|
||||||
|
<Compile Include="Commands\StatsCommand.cs" />
|
||||||
|
<Compile Include="Commands\TouchCommand.cs" />
|
||||||
|
<Compile Include="Commands\TreeCommand.cs" />
|
||||||
|
<Compile Include="Commands\UptimeCommand.cs" />
|
||||||
|
<Compile Include="Commands\HelpCommand.cs" />
|
||||||
|
<Compile Include="ClientManager.cs" />
|
||||||
|
<Compile Include="Commands\WhoCommand.cs">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Parsing.cs" />
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="TestClient.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\openjpegnet\openjpegnet.csproj">
|
||||||
|
<Project>{D0DCFDCB-71FA-4343-A8D1-24D4665A94A4}</Project>
|
||||||
|
<Name>openjpegnet</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\libsecondlife.csproj">
|
||||||
|
<Project>{D9CDEDFB-8169-4B03-B57F-0DF638F044EC}</Project>
|
||||||
|
<Name>libsecondlife</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\libsecondlife.Utilities\libsecondlife.Utilities.csproj">
|
||||||
|
<Project>{CE5E06C2-2428-416B-ADC1-F1FE16A0FB27}</Project>
|
||||||
|
<Name>libsecondlife.Utilities</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
Loading…
Reference in New Issue