From e93869c7a785a4f375685ffa33c913033ed1c85a Mon Sep 17 00:00:00 2001
From: Adam Frisby
Date: Thu, 21 Jun 2007 17:08:21 +0000
Subject: * Importing Ming's mass test client
---
tools/mass test client/Arguments.cs | 111 +++++++
tools/mass test client/ClientManager.cs | 323 ++++++++++++++++++++
tools/mass test client/Command.cs | 29 ++
.../Commands/CloneProfileCommand.cs | 131 +++++++++
.../Commands/Communication/EchoMasterCommand.cs | 42 +++
.../Commands/Communication/IMCommand.cs | 71 +++++
.../Commands/Communication/SayCommand.cs | 44 +++
.../Commands/Communication/ShoutCommand.cs | 49 ++++
.../Commands/Communication/TtsCommand.cs | 51 ++++
.../Commands/Communication/WhisperCommand.cs | 49 ++++
tools/mass test client/Commands/GoHome.cs | 26 ++
tools/mass test client/Commands/GotoLandmark.cs | 32 ++
.../Commands/Inventory/AppearanceCommand.cs | 29 ++
.../Commands/Inventory/BalanceCommand.cs | 21 ++
.../Commands/Inventory/DeleteFolderCommand.cs | 43 +++
.../Commands/Inventory/DumpOutfitCommand.cs | 98 +++++++
.../Commands/Inventory/ExportOutfitCommand.cs | 66 +++++
.../Commands/Inventory/GiveAllCommand.cs | 27 ++
.../Commands/Inventory/ImportOutfitCommand.cs | 66 +++++
.../Commands/Inventory/InventoryCommand.cs | 62 ++++
.../Commands/Inventory/WearCommand.cs | 43 +++
.../Commands/Land/FindSimCommand.cs | 43 +++
.../Commands/Land/ParcelInfoCommand.cs | 62 ++++
.../Commands/Movement/FollowCommand.cs | 90 ++++++
.../Commands/Movement/GotoCommand.cs | 53 ++++
.../Commands/Movement/JumpCommand.cs | 34 +++
.../Commands/Movement/LocationCommand.cs | 23 ++
.../Commands/Movement/MoveToCommand.cs | 24 ++
.../mass test client/Commands/Movement/SetHome.cs | 23 ++
.../Commands/Movement/SitCommand.cs | 52 ++++
.../Commands/Movement/SitOnCommand.cs | 54 ++++
.../Commands/Movement/StandCommand.cs | 47 +++
.../Commands/Prims/ExportCommand.cs | 209 +++++++++++++
.../Commands/Prims/ExportParticlesCommand.cs | 119 ++++++++
.../Commands/Prims/ImportCommand.cs | 246 ++++++++++++++++
.../Commands/Prims/PrimCountCommand.cs | 32 ++
.../Commands/Stats/DilationCommand.cs | 22 ++
.../Commands/Stats/RegionInfoCommand.cs | 45 +++
.../Commands/Stats/StatsCommand.cs | 44 +++
.../Commands/Stats/UptimeCommand.cs | 25 ++
.../Commands/System/DebugCommand.cs | 37 +++
.../Commands/System/HelpCommand.cs | 29 ++
.../Commands/System/LoadCommand.cs | 28 ++
.../Commands/System/LoginCommand.cs | 34 +++
.../Commands/System/LogoutCommand.cs | 24 ++
.../mass test client/Commands/System/MD5Command.cs | 22 ++
.../Commands/System/PacketLogCommand.cs | 84 ++++++
.../Commands/System/QuitCommand.cs | 24 ++
.../Commands/System/SetMasterCommand.cs | 73 +++++
.../Commands/System/SetMasterKeyCommand.cs | 35 +++
.../Commands/System/ShowEffectsCommand.cs | 76 +++++
tools/mass test client/Commands/TouchCommand.cs | 55 ++++
tools/mass test client/Commands/TreeCommand.cs | 51 ++++
tools/mass test client/Commands/WhoCommand.cs | 29 ++
tools/mass test client/MassTestClient.csproj | 121 ++++++++
tools/mass test client/MassTestClient.csproj.user | 10 +
tools/mass test client/MassTestClient.sln | 20 ++
tools/mass test client/MassTestClient.suo | Bin 0 -> 32256 bytes
tools/mass test client/Parsing.cs | 61 ++++
tools/mass test client/Program.cs | 218 ++++++++++++++
tools/mass test client/Properties/AssemblyInfo.cs | 33 +++
tools/mass test client/TestClient.build | 125 ++++++++
tools/mass test client/TestClient.cs | 324 +++++++++++++++++++++
.../bin/libsecondlife.Utilities.dll | Bin 0 -> 65536 bytes
tools/mass test client/bin/libsecondlife.dll | Bin 0 -> 2256896 bytes
tools/mass test client/bin/openjpegnet.dll | Bin 0 -> 32768 bytes
66 files changed, 4173 insertions(+)
create mode 100644 tools/mass test client/Arguments.cs
create mode 100644 tools/mass test client/ClientManager.cs
create mode 100644 tools/mass test client/Command.cs
create mode 100644 tools/mass test client/Commands/CloneProfileCommand.cs
create mode 100644 tools/mass test client/Commands/Communication/EchoMasterCommand.cs
create mode 100644 tools/mass test client/Commands/Communication/IMCommand.cs
create mode 100644 tools/mass test client/Commands/Communication/SayCommand.cs
create mode 100644 tools/mass test client/Commands/Communication/ShoutCommand.cs
create mode 100644 tools/mass test client/Commands/Communication/TtsCommand.cs
create mode 100644 tools/mass test client/Commands/Communication/WhisperCommand.cs
create mode 100644 tools/mass test client/Commands/GoHome.cs
create mode 100644 tools/mass test client/Commands/GotoLandmark.cs
create mode 100644 tools/mass test client/Commands/Inventory/AppearanceCommand.cs
create mode 100644 tools/mass test client/Commands/Inventory/BalanceCommand.cs
create mode 100644 tools/mass test client/Commands/Inventory/DeleteFolderCommand.cs
create mode 100644 tools/mass test client/Commands/Inventory/DumpOutfitCommand.cs
create mode 100644 tools/mass test client/Commands/Inventory/ExportOutfitCommand.cs
create mode 100644 tools/mass test client/Commands/Inventory/GiveAllCommand.cs
create mode 100644 tools/mass test client/Commands/Inventory/ImportOutfitCommand.cs
create mode 100644 tools/mass test client/Commands/Inventory/InventoryCommand.cs
create mode 100644 tools/mass test client/Commands/Inventory/WearCommand.cs
create mode 100644 tools/mass test client/Commands/Land/FindSimCommand.cs
create mode 100644 tools/mass test client/Commands/Land/ParcelInfoCommand.cs
create mode 100644 tools/mass test client/Commands/Movement/FollowCommand.cs
create mode 100644 tools/mass test client/Commands/Movement/GotoCommand.cs
create mode 100644 tools/mass test client/Commands/Movement/JumpCommand.cs
create mode 100644 tools/mass test client/Commands/Movement/LocationCommand.cs
create mode 100644 tools/mass test client/Commands/Movement/MoveToCommand.cs
create mode 100644 tools/mass test client/Commands/Movement/SetHome.cs
create mode 100644 tools/mass test client/Commands/Movement/SitCommand.cs
create mode 100644 tools/mass test client/Commands/Movement/SitOnCommand.cs
create mode 100644 tools/mass test client/Commands/Movement/StandCommand.cs
create mode 100644 tools/mass test client/Commands/Prims/ExportCommand.cs
create mode 100644 tools/mass test client/Commands/Prims/ExportParticlesCommand.cs
create mode 100644 tools/mass test client/Commands/Prims/ImportCommand.cs
create mode 100644 tools/mass test client/Commands/Prims/PrimCountCommand.cs
create mode 100644 tools/mass test client/Commands/Stats/DilationCommand.cs
create mode 100644 tools/mass test client/Commands/Stats/RegionInfoCommand.cs
create mode 100644 tools/mass test client/Commands/Stats/StatsCommand.cs
create mode 100644 tools/mass test client/Commands/Stats/UptimeCommand.cs
create mode 100644 tools/mass test client/Commands/System/DebugCommand.cs
create mode 100644 tools/mass test client/Commands/System/HelpCommand.cs
create mode 100644 tools/mass test client/Commands/System/LoadCommand.cs
create mode 100644 tools/mass test client/Commands/System/LoginCommand.cs
create mode 100644 tools/mass test client/Commands/System/LogoutCommand.cs
create mode 100644 tools/mass test client/Commands/System/MD5Command.cs
create mode 100644 tools/mass test client/Commands/System/PacketLogCommand.cs
create mode 100644 tools/mass test client/Commands/System/QuitCommand.cs
create mode 100644 tools/mass test client/Commands/System/SetMasterCommand.cs
create mode 100644 tools/mass test client/Commands/System/SetMasterKeyCommand.cs
create mode 100644 tools/mass test client/Commands/System/ShowEffectsCommand.cs
create mode 100644 tools/mass test client/Commands/TouchCommand.cs
create mode 100644 tools/mass test client/Commands/TreeCommand.cs
create mode 100644 tools/mass test client/Commands/WhoCommand.cs
create mode 100644 tools/mass test client/MassTestClient.csproj
create mode 100644 tools/mass test client/MassTestClient.csproj.user
create mode 100644 tools/mass test client/MassTestClient.sln
create mode 100644 tools/mass test client/MassTestClient.suo
create mode 100644 tools/mass test client/Parsing.cs
create mode 100644 tools/mass test client/Program.cs
create mode 100644 tools/mass test client/Properties/AssemblyInfo.cs
create mode 100644 tools/mass test client/TestClient.build
create mode 100644 tools/mass test client/TestClient.cs
create mode 100644 tools/mass test client/bin/libsecondlife.Utilities.dll
create mode 100644 tools/mass test client/bin/libsecondlife.dll
create mode 100644 tools/mass test client/bin/openjpegnet.dll
(limited to 'tools')
diff --git a/tools/mass test client/Arguments.cs b/tools/mass test client/Arguments.cs
new file mode 100644
index 0000000..302ae95
--- /dev/null
+++ b/tools/mass test client/Arguments.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Specialized;
+using System.Text.RegularExpressions;
+
+namespace CommandLine.Utility
+{
+ ///
+ /// Arguments class
+ ///
+ 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]);
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/ClientManager.cs b/tools/mass test client/ClientManager.cs
new file mode 100644
index 0000000..a6e784c
--- /dev/null
+++ b/tools/mass test client/ClientManager.cs
@@ -0,0 +1,323 @@
+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 string LoginURI;
+ }
+
+ 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 Clients = new Dictionary();
+ public Dictionary> SimPrims = new Dictionary>();
+
+ public bool Running = true;
+
+ string contactPerson = String.Empty;
+ private LLUUID resolvedMasterKey = LLUUID.Zero;
+ private ManualResetEvent keyResolution = new ManualResetEvent(false);
+
+ ///
+ ///
+ ///
+ ///
+ public ClientManager(List accounts, string c)
+ {
+ this.contactPerson = c;
+ foreach (LoginDetails account in accounts)
+ Login(account);
+ }
+
+ public ClientManager(List 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);
+ }
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ 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;
+
+ libsecondlife.NetworkManager.LoginParams loginParams = new NetworkManager.LoginParams();
+ loginParams.FirstName = account.FirstName;
+ loginParams.LastName = account.LastName;
+ loginParams.Password = account.Password;
+ loginParams.UserAgent = "MassTestClient";
+ loginParams.Start = account.StartLocation;
+ loginParams.Author = contactPerson;
+ loginParams.URI = account.LoginURI;
+
+
+ if (!client.Network.Login(loginParams))
+ {
+ Console.WriteLine("Failed to login " + account.FirstName + " " + account.LastName + ": " +
+ client.Network.LoginMessage);
+ }
+
+
+ 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, 0);
+ 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 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();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ 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);
+ }
+
+ ///
+ ///
+ ///
+ public void Run(List massTestCommands)
+ {
+ Console.WriteLine("Type quit to exit. Type help for a command list.");
+
+ if (massTestCommands.Count == 0)
+ {
+ while (Running)
+ {
+ PrintPrompt();
+ string input = Console.ReadLine();
+ DoCommandAll(input, null, null);
+ }
+ }
+ else
+ {
+ int currentCommand = 0;
+ while (Running)
+ {
+ DoCommandAll(massTestCommands[currentCommand], null, null);
+ currentCommand++;
+ if (massTestCommands.Count >= currentCommand)
+ {
+ currentCommand = 0;
+ }
+ }
+ }
+
+ 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> ");
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ 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 clientsCopy = new Dictionary(Clients);
+
+ foreach (TestClient client in clientsCopy.Values)
+ client.DoCommand(cmd, fromAgentID, imSessionID);
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public void Logout(TestClient client)
+ {
+ Clients.Remove(client.Network.AgentID);
+ client.Network.Logout();
+ }
+
+ ///
+ ///
+ ///
+ public void LogoutAll()
+ {
+ // make a copy of the clients list so that it can be iterated without fear of being changed during iteration
+ Dictionary clientsCopy = new Dictionary(Clients);
+
+ foreach (TestClient client in clientsCopy.Values)
+ Logout(client);
+ }
+
+ ///
+ ///
+ ///
+ 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.
+ }
+ }
+}
diff --git a/tools/mass test client/Command.cs b/tools/mass test client/Command.cs
new file mode 100644
index 0000000..051519d
--- /dev/null
+++ b/tools/mass test client/Command.cs
@@ -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);
+
+ ///
+ /// When set to true, think will be called.
+ ///
+ public bool Active;
+
+ ///
+ /// Called twice per second, when Command.Active is set to true.
+ ///
+ public virtual void Think()
+ {
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/CloneProfileCommand.cs b/tools/mass test client/Commands/CloneProfileCommand.cs
new file mode 100644
index 0000000..e475655
--- /dev/null
+++ b/tools/mass test client/Commands/CloneProfileCommand.cs
@@ -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 Groups = new List();
+ 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);
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Communication/EchoMasterCommand.cs b/tools/mass test client/Commands/Communication/EchoMasterCommand.cs
new file mode 100644
index 0000000..a7c3d3f
--- /dev/null
+++ b/tools/mass test client/Commands/Communication/EchoMasterCommand.cs
@@ -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);
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Communication/IMCommand.cs b/tools/mass test client/Commands/Communication/IMCommand.cs
new file mode 100644
index 0000000..d847291
--- /dev/null
+++ b/tools/mass test client/Commands/Communication/IMCommand.cs
@@ -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 Name2Key = new Dictionary();
+
+ 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 avatars)
+ {
+ foreach (KeyValuePair kvp in avatars)
+ {
+ if (kvp.Value.ToLower() == ToAvatarName.ToLower())
+ {
+ Name2Key[ToAvatarName.ToLower()] = kvp.Key;
+ NameSearchEvent.Set();
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Communication/SayCommand.cs b/tools/mass test client/Commands/Communication/SayCommand.cs
new file mode 100644
index 0000000..2ab9db4
--- /dev/null
+++ b/tools/mass test client/Commands/Communication/SayCommand.cs
@@ -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();
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Communication/ShoutCommand.cs b/tools/mass test client/Commands/Communication/ShoutCommand.cs
new file mode 100644
index 0000000..3533e3d
--- /dev/null
+++ b/tools/mass test client/Commands/Communication/ShoutCommand.cs
@@ -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;
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Communication/TtsCommand.cs b/tools/mass test client/Commands/Communication/TtsCommand.cs
new file mode 100644
index 0000000..52b7a39
--- /dev/null
+++ b/tools/mass test client/Commands/Communication/TtsCommand.cs
@@ -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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/mass test client/Commands/Communication/WhisperCommand.cs b/tools/mass test client/Commands/Communication/WhisperCommand.cs
new file mode 100644
index 0000000..4bfda33
--- /dev/null
+++ b/tools/mass test client/Commands/Communication/WhisperCommand.cs
@@ -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;
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/GoHome.cs b/tools/mass test client/Commands/GoHome.cs
new file mode 100644
index 0000000..0ac04f4
--- /dev/null
+++ b/tools/mass test client/Commands/GoHome.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using libsecondlife;
+using libsecondlife.Packets;
+
+namespace libsecondlife.TestClient
+{
+ public class GoHomeCommand : Command
+ {
+ public GoHomeCommand(TestClient testClient)
+ {
+ Name = "gohome";
+ Description = "Teleports home";
+ }
+
+ public override string Execute(string[] args, LLUUID fromAgentID)
+ {
+ if ( Client.Self.GoHome() ) {
+ return "Teleport Home Succesful";
+ } else {
+ return "Teleport Home Failed";
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/GotoLandmark.cs b/tools/mass test client/Commands/GotoLandmark.cs
new file mode 100644
index 0000000..d32047c
--- /dev/null
+++ b/tools/mass test client/Commands/GotoLandmark.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using libsecondlife;
+using libsecondlife.Packets;
+
+namespace libsecondlife.TestClient
+{
+ public class GotoLandmarkCommand : Command
+ {
+ public GotoLandmarkCommand(TestClient testClient)
+ {
+ Name = "goto_landmark";
+ Description = "Teleports to a Landmark ";
+ }
+
+ public override string Execute(string[] args, LLUUID fromAgentID)
+ {
+ LLUUID landmark = new LLUUID();
+ if ( ! LLUUID.TryParse(args[0], out landmark) ) {
+ return "Invalid LLUID";
+ } else {
+ Console.WriteLine("Teleporting to " + landmark.ToString());
+ }
+ if ( Client.Self.Teleport(landmark) ) {
+ return "Teleport Succesful";
+ } else {
+ return "Teleport Failed";
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Inventory/AppearanceCommand.cs b/tools/mass test client/Commands/Inventory/AppearanceCommand.cs
new file mode 100644
index 0000000..43aa784
--- /dev/null
+++ b/tools/mass test client/Commands/Inventory/AppearanceCommand.cs
@@ -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.";
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Inventory/BalanceCommand.cs b/tools/mass test client/Commands/Inventory/BalanceCommand.cs
new file mode 100644
index 0000000..46044f2
--- /dev/null
+++ b/tools/mass test client/Commands/Inventory/BalanceCommand.cs
@@ -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;
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Inventory/DeleteFolderCommand.cs b/tools/mass test client/Commands/Inventory/DeleteFolderCommand.cs
new file mode 100644
index 0000000..081ff86
--- /dev/null
+++ b/tools/mass test client/Commands/Inventory/DeleteFolderCommand.cs
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/mass test client/Commands/Inventory/DumpOutfitCommand.cs b/tools/mass test client/Commands/Inventory/DumpOutfitCommand.cs
new file mode 100644
index 0000000..8347acc
--- /dev/null
+++ b/tools/mass test client/Commands/Inventory/DumpOutfitCommand.cs
@@ -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 OutfitAssets = new List();
+
+ 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 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());
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Inventory/ExportOutfitCommand.cs b/tools/mass test client/Commands/Inventory/ExportOutfitCommand.cs
new file mode 100644
index 0000000..9a91d00
--- /dev/null
+++ b/tools/mass test client/Commands/Inventory/ExportOutfitCommand.cs
@@ -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();
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/mass test client/Commands/Inventory/GiveAllCommand.cs b/tools/mass test client/Commands/Inventory/GiveAllCommand.cs
new file mode 100644
index 0000000..8890ea0
--- /dev/null
+++ b/tools/mass test client/Commands/Inventory/GiveAllCommand.cs
@@ -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;
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Inventory/ImportOutfitCommand.cs b/tools/mass test client/Commands/Inventory/ImportOutfitCommand.cs
new file mode 100644
index 0000000..595ce98
--- /dev/null
+++ b/tools/mass test client/Commands/Inventory/ImportOutfitCommand.cs
@@ -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";
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Inventory/InventoryCommand.cs b/tools/mass test client/Commands/Inventory/InventoryCommand.cs
new file mode 100644
index 0000000..da5dc73
--- /dev/null
+++ b/tools/mass test client/Commands/Inventory/InventoryCommand.cs
@@ -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(" ");
+ // }
+ //}
+ }
+}
\ No newline at end of file
diff --git a/tools/mass test client/Commands/Inventory/WearCommand.cs b/tools/mass test client/Commands/Inventory/WearCommand.cs
new file mode 100644
index 0000000..d7c1432
--- /dev/null
+++ b/tools/mass test client/Commands/Inventory/WearCommand.cs
@@ -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. Usage: wear [outfit name]";
+ }
+
+ 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();
+
+ InventoryFolder folder = Client.Inventory.getFolder(target);
+
+ if (folder != null)
+ {
+ Client.Appearance.WearOutfit(folder);
+ return "Outfit " + target + " worn.";
+ }
+
+ return "Unable to find: " + target;
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Land/FindSimCommand.cs b/tools/mass test client/Commands/Land/FindSimCommand.cs
new file mode 100644
index 0000000..4fcd35c
--- /dev/null
+++ b/tools/mass test client/Commands/Land/FindSimCommand.cs
@@ -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";
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Land/ParcelInfoCommand.cs b/tools/mass test client/Commands/Land/ParcelInfoCommand.cs
new file mode 100644
index 0000000..7119a47
--- /dev/null
+++ b/tools/mass test client/Commands/Land/ParcelInfoCommand.cs
@@ -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 Parcels, int[,] map)
+ {
+ foreach (KeyValuePair 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();
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Movement/FollowCommand.cs b/tools/mass test client/Commands/Movement/FollowCommand.cs
new file mode 100644
index 0000000..49be106
--- /dev/null
+++ b/tools/mass test client/Commands/Movement/FollowCommand.cs
@@ -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();
+ }
+
+ }
+}
diff --git a/tools/mass test client/Commands/Movement/GotoCommand.cs b/tools/mass test client/Commands/Movement/GotoCommand.cs
new file mode 100644
index 0000000..fbbc2ec
--- /dev/null
+++ b/tools/mass test client/Commands/Movement/GotoCommand.cs
@@ -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;
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Movement/JumpCommand.cs b/tools/mass test client/Commands/Movement/JumpCommand.cs
new file mode 100644
index 0000000..6cead83
--- /dev/null
+++ b/tools/mass test client/Commands/Movement/JumpCommand.cs
@@ -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;
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Movement/LocationCommand.cs b/tools/mass test client/Commands/Movement/LocationCommand.cs
new file mode 100644
index 0000000..1758a48
--- /dev/null
+++ b/tools/mass test client/Commands/Movement/LocationCommand.cs
@@ -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();
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Movement/MoveToCommand.cs b/tools/mass test client/Commands/Movement/MoveToCommand.cs
new file mode 100644
index 0000000..2b3c7d7
--- /dev/null
+++ b/tools/mass test client/Commands/Movement/MoveToCommand.cs
@@ -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 + ">";
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Movement/SetHome.cs b/tools/mass test client/Commands/Movement/SetHome.cs
new file mode 100644
index 0000000..5e14ca6
--- /dev/null
+++ b/tools/mass test client/Commands/Movement/SetHome.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using libsecondlife;
+using libsecondlife.Packets;
+
+namespace libsecondlife.TestClient
+{
+ public class SetHomeCommand : Command
+ {
+ public SetHomeCommand(TestClient testClient)
+ {
+ Name = "sethome";
+ Description = "Sets home to the current location.";
+ }
+
+ public override string Execute(string[] args, LLUUID fromAgentID)
+ {
+ Client.Self.SetHome();
+ return "Home Set";
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Movement/SitCommand.cs b/tools/mass test client/Commands/Movement/SitCommand.cs
new file mode 100644
index 0000000..1735615
--- /dev/null
+++ b/tools/mass test client/Commands/Movement/SitCommand.cs
@@ -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";
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Movement/SitOnCommand.cs b/tools/mass test client/Commands/Movement/SitOnCommand.cs
new file mode 100644
index 0000000..381f50c
--- /dev/null
+++ b/tools/mass test client/Commands/Movement/SitOnCommand.cs
@@ -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";
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/mass test client/Commands/Movement/StandCommand.cs b/tools/mass test client/Commands/Movement/StandCommand.cs
new file mode 100644
index 0000000..bb8542b
--- /dev/null
+++ b/tools/mass test client/Commands/Movement/StandCommand.cs
@@ -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);
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Prims/ExportCommand.cs b/tools/mass test client/Commands/Prims/ExportCommand.cs
new file mode 100644
index 0000000..da4e45f
--- /dev/null
+++ b/tools/mass test client/Commands/Prims/ExportCommand.cs
@@ -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 PrimsWaiting = new Dictionary();
+ 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 prims = new List();
+
+ 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 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();
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Prims/ExportParticlesCommand.cs b/tools/mass test client/Commands/Prims/ExportParticlesCommand.cs
new file mode 100644
index 0000000..8fced68
--- /dev/null
+++ b/tools/mass test client/Commands/Prims/ExportParticlesCommand.cs
@@ -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();
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Prims/ImportCommand.cs b/tools/mass test client/Commands/Prims/ImportCommand.cs
new file mode 100644
index 0000000..5f8723a
--- /dev/null
+++ b/tools/mass test client/Commands/Prims/ImportCommand.cs
@@ -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 Children = new List();
+
+ public Linkset()
+ {
+ RootPrim = new Primitive();
+ }
+
+ public Linkset(Primitive rootPrim)
+ {
+ RootPrim = rootPrim;
+ }
+ }
+
+ public class ImportCommand : Command
+ {
+ Primitive currentPrim;
+ LLVector3 currentPosition;
+ SecondLife currentClient;
+ AutoResetEvent primDone;
+ List primsCreated;
+ List 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 prims;
+
+ currentClient = Client;
+
+ try
+ {
+ XmlReader reader = XmlReader.Create(filename);
+ List listprims = Helpers.PrimListFromXml(reader);
+ reader.Close();
+
+ // Create a dictionary indexed by the old local ID of the prims
+ prims = new Dictionary();
+ 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 linksets = new Dictionary();
+ 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();
+ 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 primIDs = new List(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(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;
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Prims/PrimCountCommand.cs b/tools/mass test client/Commands/Prims/PrimCountCommand.cs
new file mode 100644
index 0000000..5d07e8b
--- /dev/null
+++ b/tools/mass test client/Commands/Prims/PrimCountCommand.cs
@@ -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 prims in Client.SimPrims.Values)
+ {
+ count += prims.Count;
+ }
+ }
+
+ return count.ToString();
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Stats/DilationCommand.cs b/tools/mass test client/Commands/Stats/DilationCommand.cs
new file mode 100644
index 0000000..e6e0a4d
--- /dev/null
+++ b/tools/mass test client/Commands/Stats/DilationCommand.cs
@@ -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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/mass test client/Commands/Stats/RegionInfoCommand.cs b/tools/mass test client/Commands/Stats/RegionInfoCommand.cs
new file mode 100644
index 0000000..750175f
--- /dev/null
+++ b/tools/mass test client/Commands/Stats/RegionInfoCommand.cs
@@ -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();
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Stats/StatsCommand.cs b/tools/mass test client/Commands/Stats/StatsCommand.cs
new file mode 100644
index 0000000..cf3e4e9
--- /dev/null
+++ b/tools/mass test client/Commands/Stats/StatsCommand.cs
@@ -0,0 +1,44 @@
+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);
+ output.AppendLine(String.Format("FPS : {0} PhysicsFPS : {1} AgentUpdates : {2} Objects : {3} Scripted Objects : {4}",
+ Client.Network.CurrentSim.FPS, Client.Network.CurrentSim.PhysicsFPS, Client.Network.CurrentSim.AgentUpdates, Client.Network.CurrentSim.Objects, Client.Network.CurrentSim.ScriptedObjects));
+ output.AppendLine(String.Format("Frame Time : {0} Net Time : {1} Image Time : {2} Physics Time : {3} Script Time : {4} Other Time : {5}",
+ Client.Network.CurrentSim.FrameTime, Client.Network.CurrentSim.NetTime, Client.Network.CurrentSim.ImageTime, Client.Network.CurrentSim.PhysicsTime, Client.Network.CurrentSim.ScriptTime, Client.Network.CurrentSim.OtherTime));
+ output.AppendLine(String.Format("Agents : {0} Child Agents : {1} Active Scripts : {2}",
+ Client.Network.CurrentSim.Agents, Client.Network.CurrentSim.ChildAgents, Client.Network.CurrentSim.ActiveScripts));
+
+ return output.ToString();
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/Stats/UptimeCommand.cs b/tools/mass test client/Commands/Stats/UptimeCommand.cs
new file mode 100644
index 0000000..ac94644
--- /dev/null
+++ b/tools/mass test client/Commands/Stats/UptimeCommand.cs
@@ -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) + ")";
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/mass test client/Commands/System/DebugCommand.cs b/tools/mass test client/Commands/System/DebugCommand.cs
new file mode 100644
index 0000000..07e2a6c
--- /dev/null
+++ b/tools/mass test client/Commands/System/DebugCommand.cs
@@ -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]";
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/System/HelpCommand.cs b/tools/mass test client/Commands/System/HelpCommand.cs
new file mode 100644
index 0000000..a53b3c0
--- /dev/null
+++ b/tools/mass test client/Commands/System/HelpCommand.cs
@@ -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();
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/System/LoadCommand.cs b/tools/mass test client/Commands/System/LoadCommand.cs
new file mode 100644
index 0000000..24d2219
--- /dev/null
+++ b/tools/mass test client/Commands/System/LoadCommand.cs
@@ -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.";
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/System/LoginCommand.cs b/tools/mass test client/Commands/System/LoginCommand.cs
new file mode 100644
index 0000000..6cfc155
--- /dev/null
+++ b/tools/mass test client/Commands/System/LoginCommand.cs
@@ -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.LoginMessage;
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/System/LogoutCommand.cs b/tools/mass test client/Commands/System/LogoutCommand.cs
new file mode 100644
index 0000000..8241626
--- /dev/null
+++ b/tools/mass test client/Commands/System/LogoutCommand.cs
@@ -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";
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/System/MD5Command.cs b/tools/mass test client/Commands/System/MD5Command.cs
new file mode 100644
index 0000000..2084e67
--- /dev/null
+++ b/tools/mass test client/Commands/System/MD5Command.cs
@@ -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]";
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/System/PacketLogCommand.cs b/tools/mass test client/Commands/System/PacketLogCommand.cs
new file mode 100644
index 0000000..694cee1
--- /dev/null
+++ b/tools/mass test client/Commands/System/PacketLogCommand.cs
@@ -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 Packets = new List();
+ 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++;
+ }
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/System/QuitCommand.cs b/tools/mass test client/Commands/System/QuitCommand.cs
new file mode 100644
index 0000000..2cf0418
--- /dev/null
+++ b/tools/mass test client/Commands/System/QuitCommand.cs
@@ -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";
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/System/SetMasterCommand.cs b/tools/mass test client/Commands/System/SetMasterCommand.cs
new file mode 100644
index 0000000..8601865
--- /dev/null
+++ b/tools/mass test client/Commands/System/SetMasterCommand.cs
@@ -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, 0);
+ 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 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;
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/System/SetMasterKeyCommand.cs b/tools/mass test client/Commands/System/SetMasterKeyCommand.cs
new file mode 100644
index 0000000..1fa6336
--- /dev/null
+++ b/tools/mass test client/Commands/System/SetMasterKeyCommand.cs
@@ -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;
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/System/ShowEffectsCommand.cs b/tools/mass test client/Commands/System/ShowEffectsCommand.cs
new file mode 100644
index 0000000..4d46e35
--- /dev/null
+++ b/tools/mass test client/Commands/System/ShowEffectsCommand.cs
@@ -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());
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/TouchCommand.cs b/tools/mass test client/Commands/TouchCommand.cs
new file mode 100644
index 0000000..f585b87
--- /dev/null
+++ b/tools/mass test client/Commands/TouchCommand.cs
@@ -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.";
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/mass test client/Commands/TreeCommand.cs b/tools/mass test client/Commands/TreeCommand.cs
new file mode 100644
index 0000000..60cf234
--- /dev/null
+++ b/tools/mass test client/Commands/TreeCommand.cs
@@ -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;
+ }
+ }
+}
diff --git a/tools/mass test client/Commands/WhoCommand.cs b/tools/mass test client/Commands/WhoCommand.cs
new file mode 100644
index 0000000..3edb649
--- /dev/null
+++ b/tools/mass test client/Commands/WhoCommand.cs
@@ -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();
+ }
+ }
+}
diff --git a/tools/mass test client/MassTestClient.csproj b/tools/mass test client/MassTestClient.csproj
new file mode 100644
index 0000000..0f5b6a6
--- /dev/null
+++ b/tools/mass test client/MassTestClient.csproj
@@ -0,0 +1,121 @@
+
+
+ Debug
+ AnyCPU
+ 8.0.50727
+ 2.0
+ {B87682F6-B2D7-4C4D-A529-400C24FD4880}
+ Exe
+ Properties
+ libsecondlife.MassTestClient
+ MassTestClient
+
+
+ true
+ full
+ false
+ ..\..\..\bin\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\
+ TRACE
+ prompt
+ 4
+
+
+
+ False
+ bin\libsecondlife.dll
+
+
+ False
+ bin\libsecondlife.Utilities.dll
+
+
+ False
+ bin\openjpegnet.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Code
+
+
+ Code
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Code
+
+
+
+ Code
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Code
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/mass test client/MassTestClient.csproj.user b/tools/mass test client/MassTestClient.csproj.user
new file mode 100644
index 0000000..c32889c
--- /dev/null
+++ b/tools/mass test client/MassTestClient.csproj.user
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/mass test client/MassTestClient.sln b/tools/mass test client/MassTestClient.sln
new file mode 100644
index 0000000..cbbeef6
--- /dev/null
+++ b/tools/mass test client/MassTestClient.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual C# Express 2005
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTestClient", "MassTestClient.csproj", "{B87682F6-B2D7-4C4D-A529-400C24FD4880}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B87682F6-B2D7-4C4D-A529-400C24FD4880}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B87682F6-B2D7-4C4D-A529-400C24FD4880}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B87682F6-B2D7-4C4D-A529-400C24FD4880}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B87682F6-B2D7-4C4D-A529-400C24FD4880}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/tools/mass test client/MassTestClient.suo b/tools/mass test client/MassTestClient.suo
new file mode 100644
index 0000000..7f0637c
Binary files /dev/null and b/tools/mass test client/MassTestClient.suo differ
diff --git a/tools/mass test client/Parsing.cs b/tools/mass test client/Parsing.cs
new file mode 100644
index 0000000..371c3cd
--- /dev/null
+++ b/tools/mass test client/Parsing.cs
@@ -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 list = new List();
+ 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();
+ }
+ }
+}
diff --git a/tools/mass test client/Program.cs b/tools/mass test client/Program.cs
new file mode 100644
index 0000000..6274011
--- /dev/null
+++ b/tools/mass test client/Program.cs
@@ -0,0 +1,218 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using CommandLine.Utility;
+
+namespace libsecondlife.TestClient
+{
+ public class CommandLineArgumentsException : Exception
+ {
+ }
+
+ public class Program
+ {
+
+ private static void Usage()
+ {
+ Console.WriteLine("Usage: " + Environment.NewLine +
+ "MassTestClient.exe --first \"firstname\" --last \"lastname\" --pass \"password\" --contact \"youremail\" [--startpos \"sim/x/y/z\"] [--master \"master name\"] [--masterkey \"master uuid\"] [--loginuri \"loginuri\"] [--masscommandfile \"filename\"]" +
+ Environment.NewLine + Environment.NewLine + "MassTestClient.exe --loginfile \"filename\" --contact \"youremail\" [--master \"master name\"] [--masterkey \"master uuid\"] [--loginuri \"loginuri\"] [--masscommandfile \"filename\"]");
+ Console.ReadLine();
+ }
+
+ private static List getMassTestCommands()
+ {
+ List givenCommands = new List();
+ Console.WriteLine("Please enter mass test commands to run in an infinite loop. Press enter to end the current command. Entering a blank command represents that you are done.");
+ Console.WriteLine("");
+
+ int curCommand = 0;
+ string lastCommand = "NULL";
+ while (lastCommand.Length > 0)
+ {
+ Console.Write("Command #" + curCommand + ">");
+ lastCommand = Console.ReadLine().Trim();
+ if (lastCommand.Length > 0)
+ {
+ givenCommands.Add(lastCommand);
+ curCommand++;
+ }
+ }
+
+ return givenCommands;
+ }
+
+ static void Main(string[] args)
+ {
+ Arguments arguments = new Arguments(args);
+
+ ClientManager manager;
+ List accounts = new List();
+ LoginDetails account;
+ string masterName = String.Empty;
+ LLUUID masterKey = LLUUID.Zero;
+ string file = String.Empty;
+ string contact = String.Empty;
+ string loginURI = "https://login.agni.lindenlab.com/cgi-bin/login.cgi";
+ try
+ {
+ if (arguments["masterkey"] != null)
+ {
+ masterKey = LLUUID.Parse(arguments["masterkey"]);
+ }
+
+ if (arguments["master"] != null)
+ {
+ masterName = arguments["master"];
+ }
+
+ if (arguments["contact"] == null)
+ throw new CommandLineArgumentsException();
+
+ 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());
+ Console.ReadLine();
+ 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
+ {
+ throw new CommandLineArgumentsException();
+ }
+ }
+
+ catch (CommandLineArgumentsException)
+ {
+ Usage();
+ return;
+ }
+
+ if(arguments["loginuri"] != null)
+ {
+ loginURI = arguments["loginuri"];
+ }
+
+ List massTestCommands = new List();
+ if(arguments["masscommandfile"] != null)
+ {
+ string massCommandFile = arguments["masscommandfile"];
+ try
+ {
+ using (StreamReader reader = new StreamReader(massCommandFile))
+ {
+ string line;
+
+ while ((line = reader.ReadLine()) != null)
+ {
+
+ line = line.Trim();
+ if(line.Length > 0)
+ {
+ massTestCommands.Add(line);
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Error reading from " + args[1]);
+ Console.WriteLine(e.ToString());
+ Console.ReadLine();
+ return;
+ }
+ }
+ else
+ {
+ Console.Clear();
+ massTestCommands = getMassTestCommands();
+ }
+
+ Console.Clear();
+ if (massTestCommands.Count == 0)
+ {
+ Console.WriteLine("No mass commands entered; Normal 'TestClient' operation will be used");
+ }
+ else
+ {
+ Console.WriteLine("Detected " + massTestCommands.Count + " mass commands; MassTestClient operation will be used");
+ }
+
+ foreach (LoginDetails a in accounts)
+ {
+ a.MasterName = masterName;
+ a.MasterKey = masterKey;
+ a.LoginURI = loginURI;
+ }
+
+ // Login the accounts and run the input loop
+ if (arguments["startpos"] != null)
+ {
+ manager = new ClientManager(accounts, contact, arguments["startpos"]);
+ }
+ else
+ {
+ manager = new ClientManager(accounts, contact);
+ }
+
+
+ manager.Run(massTestCommands);
+ }
+ }
+}
diff --git a/tools/mass test client/Properties/AssemblyInfo.cs b/tools/mass test client/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..ae311f4
--- /dev/null
+++ b/tools/mass test client/Properties/AssemblyInfo.cs
@@ -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("MassTestClient")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MassTestClient")]
+[assembly: AssemblyCopyright("Copyright © 2007")]
+[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")]
diff --git a/tools/mass test client/TestClient.build b/tools/mass test client/TestClient.build
new file mode 100644
index 0000000..803377b
--- /dev/null
+++ b/tools/mass test client/TestClient.build
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/mass test client/TestClient.cs b/tools/mass test client/TestClient.cs
new file mode 100644
index 0000000..cee218c
--- /dev/null
+++ b/tools/mass test client/TestClient.cs
@@ -0,0 +1,324 @@
+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> SimPrims;
+ public LLUUID GroupID = LLUUID.Zero;
+ public Dictionary GroupMembers;
+ public Dictionary AvatarList = new Dictionary();
+ public Dictionary Appearances = new Dictionary();
+ public Dictionary Commands = new Dictionary();
+ 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 System.Timers.Timer updateTimer;
+
+
+ ///
+ ///
+ ///
+ 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 = 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 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(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("" + 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("" + fromAgentName + " (not in group): " + message + "@" + regionID.ToString() + ":" + position.ToString());
+ return;
+ }
+ }
+
+ Console.WriteLine("" + 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);
+ }
+ }
+ }
+ }
+}
diff --git a/tools/mass test client/bin/libsecondlife.Utilities.dll b/tools/mass test client/bin/libsecondlife.Utilities.dll
new file mode 100644
index 0000000..b5a71fc
Binary files /dev/null and b/tools/mass test client/bin/libsecondlife.Utilities.dll differ
diff --git a/tools/mass test client/bin/libsecondlife.dll b/tools/mass test client/bin/libsecondlife.dll
new file mode 100644
index 0000000..60c3151
Binary files /dev/null and b/tools/mass test client/bin/libsecondlife.dll differ
diff --git a/tools/mass test client/bin/openjpegnet.dll b/tools/mass test client/bin/openjpegnet.dll
new file mode 100644
index 0000000..83067b0
Binary files /dev/null and b/tools/mass test client/bin/openjpegnet.dll differ
--
cgit v1.1