2022-02-24 19:36:06 -06:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2006-2016, openmetaverse.co
|
|
|
|
|
* Copyright (c) 2019-2022, Sjofn, LLC
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* - Redistribution and use in source and binary forms, with or without
|
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
|
*
|
|
|
|
|
* - Redistributions of source code must retain the above copyright notice, this
|
|
|
|
|
* list of conditions and the following disclaimer.
|
|
|
|
|
* - Neither the name of the openmetaverse.co nor the names
|
|
|
|
|
* of its contributors may be used to endorse or promote products derived from
|
|
|
|
|
* this software without specific prior written permission.
|
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
*/
|
|
|
|
|
|
2007-04-28 20:54:02 +00:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2022-02-25 19:38:11 -06:00
|
|
|
using System.Linq;
|
2007-04-28 20:54:02 +00:00
|
|
|
using System.Threading;
|
|
|
|
|
|
2008-07-21 21:12:59 +00:00
|
|
|
namespace OpenMetaverse.TestClient
|
2007-04-28 20:54:02 +00:00
|
|
|
{
|
|
|
|
|
public class LoginDetails
|
|
|
|
|
{
|
|
|
|
|
public string FirstName;
|
|
|
|
|
public string LastName;
|
|
|
|
|
public string Password;
|
|
|
|
|
public string StartLocation;
|
2008-02-07 17:32:06 +00:00
|
|
|
public bool GroupCommands;
|
2007-04-28 20:54:02 +00:00
|
|
|
public string MasterName;
|
2008-07-25 05:15:05 +00:00
|
|
|
public UUID MasterKey;
|
2007-07-11 09:17:46 +00:00
|
|
|
public string URI;
|
2007-04-28 20:54:02 +00:00
|
|
|
}
|
|
|
|
|
|
2009-05-08 18:57:45 +00:00
|
|
|
public sealed class ClientManager
|
2008-10-10 19:41:05 +00:00
|
|
|
{
|
2009-05-08 18:57:45 +00:00
|
|
|
const string VERSION = "1.0.0";
|
|
|
|
|
|
|
|
|
|
class Singleton { internal static readonly ClientManager Instance = new ClientManager(); }
|
2019-10-30 20:39:56 -05:00
|
|
|
public static ClientManager Instance => Singleton.Instance;
|
2008-10-10 19:41:05 +00:00
|
|
|
|
2008-10-10 20:15:13 +00:00
|
|
|
public Dictionary<UUID, TestClient> Clients = new Dictionary<UUID, TestClient>();
|
2007-04-28 20:54:02 +00:00
|
|
|
public Dictionary<Simulator, Dictionary<uint, Primitive>> SimPrims = new Dictionary<Simulator, Dictionary<uint, Primitive>>();
|
|
|
|
|
|
|
|
|
|
public bool Running = true;
|
2008-10-09 18:04:06 +00:00
|
|
|
public bool GetTextures = false;
|
2009-05-08 19:19:53 +00:00
|
|
|
public volatile int PendingLogins = 0;
|
2019-10-30 20:39:56 -05:00
|
|
|
public string onlyAvatar = string.Empty;
|
2007-04-28 20:54:02 +00:00
|
|
|
|
2009-05-08 18:57:45 +00:00
|
|
|
ClientManager()
|
2007-04-28 20:54:02 +00:00
|
|
|
{
|
2009-05-08 18:57:45 +00:00
|
|
|
}
|
2008-10-10 19:41:05 +00:00
|
|
|
|
2009-05-08 18:57:45 +00:00
|
|
|
public void Start(List<LoginDetails> accounts, bool getTextures)
|
|
|
|
|
{
|
2008-10-09 21:24:20 +00:00
|
|
|
GetTextures = getTextures;
|
2007-04-28 20:54:02 +00:00
|
|
|
|
|
|
|
|
foreach (LoginDetails account in accounts)
|
|
|
|
|
Login(account);
|
|
|
|
|
}
|
2008-10-09 21:24:20 +00:00
|
|
|
|
2022-02-24 19:36:06 -06:00
|
|
|
/// <summary>
|
|
|
|
|
/// Login command with required args
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="args"></param>
|
|
|
|
|
/// <returns></returns>
|
2009-05-08 18:57:45 +00:00
|
|
|
public TestClient Login(string[] args)
|
|
|
|
|
{
|
|
|
|
|
if (args.Length < 3)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Usage: login firstname lastname password [simname] [login server url]");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2019-10-30 20:39:56 -05:00
|
|
|
|
|
|
|
|
LoginDetails account = new LoginDetails
|
|
|
|
|
{
|
|
|
|
|
FirstName = args[0],
|
|
|
|
|
LastName = args[1],
|
|
|
|
|
Password = args[2]
|
|
|
|
|
};
|
2009-05-08 18:57:45 +00:00
|
|
|
|
|
|
|
|
if (args.Length > 3)
|
|
|
|
|
{
|
|
|
|
|
// If it looks like a full starting position was specified, parse it
|
|
|
|
|
if (args[3].StartsWith("http"))
|
|
|
|
|
{
|
|
|
|
|
account.URI = args[3];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (args[3].IndexOf('/') >= 0)
|
|
|
|
|
{
|
|
|
|
|
char sep = '/';
|
|
|
|
|
string[] startbits = args[3].Split(sep);
|
|
|
|
|
try
|
|
|
|
|
{
|
2019-10-30 20:39:56 -05:00
|
|
|
account.StartLocation = NetworkManager.StartLocation(startbits[0], int.Parse(startbits[1]),
|
|
|
|
|
int.Parse(startbits[2]), int.Parse(startbits[3]));
|
2009-05-08 18:57:45 +00:00
|
|
|
}
|
|
|
|
|
catch (FormatException) { }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, use the center of the named region
|
2022-11-20 00:46:46 -06:00
|
|
|
if (account.StartLocation == null)
|
|
|
|
|
{
|
|
|
|
|
account.StartLocation = NetworkManager.StartLocation(args[3], 128, 128, 40);
|
|
|
|
|
}
|
2009-05-08 18:57:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (args.Length > 4)
|
|
|
|
|
if (args[4].StartsWith("http"))
|
|
|
|
|
account.URI = args[4];
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(account.URI))
|
|
|
|
|
account.URI = Program.LoginURI;
|
2022-02-24 19:36:06 -06:00
|
|
|
Logger.Log($"Using login URI {account.URI}", Helpers.LogLevel.Info);
|
2009-05-08 18:57:45 +00:00
|
|
|
|
|
|
|
|
return Login(account);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-24 19:36:06 -06:00
|
|
|
/// <summary>
|
|
|
|
|
/// Login account with provided <seealso cref="LoginDetails"/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="account"></param>
|
|
|
|
|
/// <returns></returns>
|
2007-04-28 20:54:02 +00:00
|
|
|
public TestClient Login(LoginDetails account)
|
|
|
|
|
{
|
|
|
|
|
// Check if this client is already logged in
|
2022-02-27 22:05:32 -06:00
|
|
|
foreach (var c in Clients.Values.Where(
|
2022-11-20 00:46:46 -06:00
|
|
|
tc => tc.Self.FirstName == account.FirstName
|
|
|
|
|
&& tc.Self.LastName == account.LastName))
|
2007-04-28 20:54:02 +00:00
|
|
|
{
|
2022-02-27 22:05:32 -06:00
|
|
|
Logout(c);
|
|
|
|
|
break;
|
2007-04-28 20:54:02 +00:00
|
|
|
}
|
|
|
|
|
|
2009-05-08 19:19:53 +00:00
|
|
|
++PendingLogins;
|
|
|
|
|
|
2022-11-20 00:46:46 -06:00
|
|
|
TestClient client = new TestClient(this)
|
|
|
|
|
{
|
|
|
|
|
Settings = { MFA_ENABLED = true }
|
|
|
|
|
};
|
2009-10-28 08:01:52 +00:00
|
|
|
client.Network.LoginProgress +=
|
|
|
|
|
delegate(object sender, LoginProgressEventArgs e)
|
2009-05-08 18:57:45 +00:00
|
|
|
{
|
2019-10-30 20:39:56 -05:00
|
|
|
Logger.Log($"Login {e.Status}: {e.Message}", Helpers.LogLevel.Info, client);
|
2009-05-08 18:57:45 +00:00
|
|
|
|
2009-10-28 08:01:52 +00:00
|
|
|
if (e.Status == LoginStatus.Success)
|
2009-05-08 18:57:45 +00:00
|
|
|
{
|
|
|
|
|
Clients[client.Self.AgentID] = client;
|
|
|
|
|
|
|
|
|
|
if (client.MasterKey == UUID.Zero)
|
|
|
|
|
{
|
2009-10-10 06:38:07 +00:00
|
|
|
UUID query = UUID.Zero;
|
2022-02-27 22:05:32 -06:00
|
|
|
|
|
|
|
|
void PeopleDirCallback(object sender2, DirPeopleReplyEventArgs dpe)
|
|
|
|
|
{
|
|
|
|
|
if (dpe.QueryID != query) { return; }
|
|
|
|
|
if (dpe.MatchedPeople.Count != 1)
|
2009-05-08 18:57:45 +00:00
|
|
|
{
|
2022-02-27 22:05:32 -06:00
|
|
|
Logger.Log($"Unable to resolve master key from {client.MasterName}", Helpers.LogLevel.Warning);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
client.MasterKey = dpe.MatchedPeople[0].AgentID;
|
|
|
|
|
Logger.Log($"Master key resolved to {client.MasterKey}", Helpers.LogLevel.Info);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client.Directory.DirPeopleReply += PeopleDirCallback;
|
2009-10-10 06:38:07 +00:00
|
|
|
query = client.Directory.StartPeopleSearch(client.MasterName, 0);
|
2009-05-08 18:57:45 +00:00
|
|
|
}
|
|
|
|
|
|
2022-02-24 19:36:06 -06:00
|
|
|
Logger.Log($"Logged in {client}", Helpers.LogLevel.Info);
|
2009-05-08 19:19:53 +00:00
|
|
|
--PendingLogins;
|
2009-05-08 18:57:45 +00:00
|
|
|
}
|
2009-10-28 08:01:52 +00:00
|
|
|
else if (e.Status == LoginStatus.Failed)
|
2009-05-08 18:57:45 +00:00
|
|
|
{
|
2022-02-24 19:36:06 -06:00
|
|
|
Logger.Log($"Failed to login {account.FirstName} {account.LastName}: {client.Network.LoginMessage}",
|
|
|
|
|
Helpers.LogLevel.Warning);
|
2009-05-08 19:19:53 +00:00
|
|
|
--PendingLogins;
|
2009-05-08 18:57:45 +00:00
|
|
|
}
|
|
|
|
|
};
|
2007-04-28 20:54:02 +00:00
|
|
|
|
|
|
|
|
// Optimize the throttle
|
|
|
|
|
client.Throttle.Wind = 0;
|
|
|
|
|
client.Throttle.Cloud = 0;
|
|
|
|
|
client.Throttle.Land = 1000000;
|
|
|
|
|
client.Throttle.Task = 1000000;
|
|
|
|
|
|
2008-02-07 17:32:06 +00:00
|
|
|
client.GroupCommands = account.GroupCommands;
|
2007-04-28 20:54:02 +00:00
|
|
|
client.MasterName = account.MasterName;
|
|
|
|
|
client.MasterKey = account.MasterKey;
|
2008-07-30 06:40:47 +00:00
|
|
|
client.AllowObjectMaster = client.MasterKey != UUID.Zero; // Require UUID for object master.
|
2007-04-28 20:54:02 +00:00
|
|
|
|
2007-12-21 02:25:36 +00:00
|
|
|
LoginParams loginParams = client.Network.DefaultLoginParams(
|
2009-05-08 18:57:45 +00:00
|
|
|
account.FirstName, account.LastName, account.Password, "TestClient", VERSION);
|
2007-07-11 09:17:46 +00:00
|
|
|
|
2022-02-24 19:36:06 -06:00
|
|
|
if (!string.IsNullOrEmpty(account.StartLocation))
|
2007-07-11 09:17:46 +00:00
|
|
|
loginParams.Start = account.StartLocation;
|
|
|
|
|
|
2022-02-24 19:36:06 -06:00
|
|
|
if (!string.IsNullOrEmpty(account.URI))
|
2007-07-11 09:17:46 +00:00
|
|
|
loginParams.URI = account.URI;
|
2007-04-28 20:54:02 +00:00
|
|
|
|
2009-05-08 18:57:45 +00:00
|
|
|
client.Network.BeginLogin(loginParams);
|
2007-04-28 20:54:02 +00:00
|
|
|
return client;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2022-02-24 19:36:06 -06:00
|
|
|
/// Begin running client
|
2007-04-28 20:54:02 +00:00
|
|
|
/// </summary>
|
2022-02-24 19:36:06 -06:00
|
|
|
/// <param name="noGUI">Run with Gui or nah</param>
|
2010-11-20 14:14:26 +00:00
|
|
|
public void Run(bool noGUI)
|
2007-04-28 20:54:02 +00:00
|
|
|
{
|
2010-11-20 14:14:26 +00:00
|
|
|
if (noGUI)
|
2007-04-28 20:54:02 +00:00
|
|
|
{
|
2010-11-20 14:14:26 +00:00
|
|
|
while (Running)
|
|
|
|
|
{
|
|
|
|
|
Thread.Sleep(2 * 1000);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Console.WriteLine("Type quit to exit. Type help for a command list.");
|
|
|
|
|
|
|
|
|
|
while (Running)
|
|
|
|
|
{
|
|
|
|
|
PrintPrompt();
|
|
|
|
|
string input = Console.ReadLine();
|
|
|
|
|
DoCommandAll(input, UUID.Zero);
|
|
|
|
|
}
|
2007-04-28 20:54:02 +00:00
|
|
|
}
|
|
|
|
|
|
2022-02-25 19:38:11 -06:00
|
|
|
foreach (var client in Clients.Values.Cast<GridClient>().Where(client => client.Network.Connected))
|
2007-04-28 20:54:02 +00:00
|
|
|
{
|
2022-02-25 19:38:11 -06:00
|
|
|
client.Network.Logout();
|
2007-04-28 20:54:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-24 19:36:06 -06:00
|
|
|
/// <summary>
|
|
|
|
|
/// Print GUI prompt to screen
|
|
|
|
|
/// </summary>
|
2007-04-28 20:54:02 +00:00
|
|
|
private void PrintPrompt()
|
|
|
|
|
{
|
2022-02-25 19:38:11 -06:00
|
|
|
int online = Clients.Values.Cast<GridClient>().Count(client => client.Network.Connected);
|
2007-04-28 20:54:02 +00:00
|
|
|
|
2022-02-24 19:36:06 -06:00
|
|
|
Console.Write($"{online} avatars online> ");
|
2007-04-28 20:54:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
///
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="cmd"></param>
|
|
|
|
|
/// <param name="fromAgentID"></param>
|
|
|
|
|
/// <param name="imSessionID"></param>
|
2008-07-25 05:15:05 +00:00
|
|
|
public void DoCommandAll(string cmd, UUID fromAgentID)
|
2007-04-28 20:54:02 +00:00
|
|
|
{
|
2019-10-20 21:26:21 -05:00
|
|
|
if (cmd == null)
|
|
|
|
|
return;
|
2022-02-25 19:38:11 -06:00
|
|
|
string[] tokens = cmd.Trim().Split(' ', '\t');
|
2008-10-10 21:53:47 +00:00
|
|
|
if (tokens.Length == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2007-04-28 20:54:02 +00:00
|
|
|
string firstToken = tokens[0].ToLower();
|
2022-02-24 19:36:06 -06:00
|
|
|
if (string.IsNullOrEmpty(firstToken))
|
2008-10-10 21:53:47 +00:00
|
|
|
return;
|
2007-04-28 20:54:02 +00:00
|
|
|
|
2009-02-17 18:29:00 +00:00
|
|
|
// Allow for comments when cmdline begins with ';' or '#'
|
|
|
|
|
if (firstToken[0] == ';' || firstToken[0] == '#')
|
|
|
|
|
return;
|
2009-07-15 23:23:11 +00:00
|
|
|
|
|
|
|
|
if ('@' == firstToken[0]) {
|
2022-02-25 19:38:11 -06:00
|
|
|
onlyAvatar = string.Empty;
|
2009-07-15 23:23:11 +00:00
|
|
|
if (tokens.Length == 3) {
|
|
|
|
|
onlyAvatar = tokens[1]+" "+tokens[2];
|
2022-02-25 19:38:11 -06:00
|
|
|
bool found = Clients.Values.Any(client => (client.ToString() == onlyAvatar) && (client.Network.Connected));
|
|
|
|
|
|
|
|
|
|
Logger.Log(
|
|
|
|
|
found
|
|
|
|
|
? $"Commanding only {onlyAvatar} now"
|
|
|
|
|
: $"Commanding nobody now. Avatar {onlyAvatar} is offline", Helpers.LogLevel.Info);
|
2009-07-15 23:23:11 +00:00
|
|
|
} else {
|
|
|
|
|
Logger.Log("Commanding all avatars now", Helpers.LogLevel.Info);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-02-17 18:29:00 +00:00
|
|
|
|
2008-10-09 17:42:18 +00:00
|
|
|
string[] args = new string[tokens.Length - 1];
|
2008-10-10 21:53:47 +00:00
|
|
|
if (args.Length > 0)
|
|
|
|
|
Array.Copy(tokens, 1, args, 0, args.Length);
|
2007-04-28 20:54:02 +00:00
|
|
|
|
|
|
|
|
if (firstToken == "login")
|
|
|
|
|
{
|
|
|
|
|
Login(args);
|
|
|
|
|
}
|
|
|
|
|
else if (firstToken == "quit")
|
|
|
|
|
{
|
|
|
|
|
Quit();
|
2008-10-08 20:21:32 +00:00
|
|
|
Logger.Log("All clients logged out and program finished running.", Helpers.LogLevel.Info);
|
2007-04-28 20:54:02 +00:00
|
|
|
}
|
2008-10-09 17:42:18 +00:00
|
|
|
else if (firstToken == "help")
|
|
|
|
|
{
|
2008-10-10 20:15:13 +00:00
|
|
|
if (Clients.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
foreach (TestClient client in Clients.Values)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(client.Commands["help"].Execute(args, UUID.Zero));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("You must login at least one bot to use the help command");
|
|
|
|
|
}
|
2008-10-09 17:42:18 +00:00
|
|
|
}
|
|
|
|
|
else if (firstToken == "script")
|
|
|
|
|
{
|
|
|
|
|
// No reason to pass this to all bots, and we also want to allow it when there are no bots
|
|
|
|
|
ScriptCommand command = new ScriptCommand(null);
|
|
|
|
|
Logger.Log(command.Execute(args, UUID.Zero), Helpers.LogLevel.Info);
|
|
|
|
|
}
|
2009-05-08 19:19:53 +00:00
|
|
|
else if (firstToken == "waitforlogin")
|
|
|
|
|
{
|
|
|
|
|
// Special exception to allow this to run before any bots have logged in
|
|
|
|
|
if (ClientManager.Instance.PendingLogins > 0)
|
|
|
|
|
{
|
|
|
|
|
WaitForLoginCommand command = new WaitForLoginCommand(null);
|
|
|
|
|
Logger.Log(command.Execute(args, UUID.Zero), Helpers.LogLevel.Info);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Logger.Log("No pending logins", Helpers.LogLevel.Info);
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-04-28 20:54:02 +00:00
|
|
|
else
|
|
|
|
|
{
|
2008-10-08 20:21:32 +00:00
|
|
|
// Make an immutable copy of the Clients dictionary to safely iterate over
|
2008-10-10 20:15:13 +00:00
|
|
|
Dictionary<UUID, TestClient> clientsCopy = new Dictionary<UUID, TestClient>(Clients);
|
2007-04-28 20:54:02 +00:00
|
|
|
|
2008-10-08 20:21:32 +00:00
|
|
|
int completed = 0;
|
|
|
|
|
|
2007-04-28 20:54:02 +00:00
|
|
|
foreach (TestClient client in clientsCopy.Values)
|
2008-10-08 20:21:32 +00:00
|
|
|
{
|
2022-02-24 19:36:06 -06:00
|
|
|
ThreadPool.QueueUserWorkItem(
|
2008-10-10 21:02:06 +00:00
|
|
|
delegate(object state)
|
|
|
|
|
{
|
2008-10-10 21:53:47 +00:00
|
|
|
TestClient testClient = (TestClient)state;
|
2022-02-24 19:36:06 -06:00
|
|
|
if ((string.Empty == onlyAvatar) || (testClient.ToString() == onlyAvatar)) {
|
2011-03-14 15:20:11 +00:00
|
|
|
if (testClient.Commands.ContainsKey(firstToken)) {
|
2011-03-14 15:27:06 +00:00
|
|
|
string result;
|
|
|
|
|
try {
|
|
|
|
|
result = testClient.Commands[firstToken].Execute(args, fromAgentID);
|
|
|
|
|
Logger.Log(result, Helpers.LogLevel.Info, testClient);
|
|
|
|
|
} catch(Exception e) {
|
2019-10-30 20:39:56 -05:00
|
|
|
Logger.Log($"{firstToken} raised exception {e}",
|
2011-03-14 15:27:06 +00:00
|
|
|
Helpers.LogLevel.Error,
|
|
|
|
|
testClient);
|
|
|
|
|
}
|
2011-03-14 15:20:11 +00:00
|
|
|
} else
|
2022-02-24 19:36:06 -06:00
|
|
|
Logger.Log($"Unknown command {firstToken}", Helpers.LogLevel.Warning);
|
2009-07-15 23:23:11 +00:00
|
|
|
}
|
2008-10-11 00:17:21 +00:00
|
|
|
|
2008-10-10 21:02:06 +00:00
|
|
|
++completed;
|
2008-10-10 21:53:47 +00:00
|
|
|
},
|
|
|
|
|
client);
|
2008-10-08 20:21:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (completed < clientsCopy.Count)
|
|
|
|
|
Thread.Sleep(50);
|
2007-04-28 20:54:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2022-02-24 19:36:06 -06:00
|
|
|
/// Logout specified <seealso cref="TestClient"/> instance
|
2007-04-28 20:54:02 +00:00
|
|
|
/// </summary>
|
2022-02-24 19:36:06 -06:00
|
|
|
/// <param name="client">Client to perform logout</param>
|
2007-04-28 20:54:02 +00:00
|
|
|
public void Logout(TestClient client)
|
|
|
|
|
{
|
2007-11-06 09:26:10 +00:00
|
|
|
Clients.Remove(client.Self.AgentID);
|
2007-04-28 20:54:02 +00:00
|
|
|
client.Network.Logout();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2022-02-24 19:36:06 -06:00
|
|
|
/// Exit the program
|
2007-04-28 20:54:02 +00:00
|
|
|
/// </summary>
|
|
|
|
|
public void Quit()
|
|
|
|
|
{
|
|
|
|
|
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.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|