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