From 134f86e8d5c414409631b25b8c6f0ee45fbd8631 Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Thu, 3 Nov 2016 21:44:39 +1000 Subject: Initial update to OpenSim 0.8.2.1 source code. --- OpenSim/Tools/pCampBot/BotManager.cs | 903 +++++++++++++++++++++++++++++------ 1 file changed, 752 insertions(+), 151 deletions(-) (limited to 'OpenSim/Tools/pCampBot/BotManager.cs') diff --git a/OpenSim/Tools/pCampBot/BotManager.cs b/OpenSim/Tools/pCampBot/BotManager.cs index d615b3f..0af9592 100644 --- a/OpenSim/Tools/pCampBot/BotManager.cs +++ b/OpenSim/Tools/pCampBot/BotManager.cs @@ -38,10 +38,19 @@ using log4net.Repository; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Console; +using OpenSim.Framework.Monitoring; using pCampBot.Interfaces; namespace pCampBot { + public enum BotManagerBotConnectingState + { + Initializing, + Ready, + Connecting, + Disconnecting + } + /// /// Thread/Bot manager for the application /// @@ -52,6 +61,16 @@ namespace pCampBot public const int DefaultLoginDelay = 5000; /// + /// Is pCampbot ready to connect or currently in the process of connecting or disconnecting bots? + /// + public BotManagerBotConnectingState BotConnectingState { get; private set; } + + /// + /// Used to control locking as we can't lock an enum. + /// + private object BotConnectingStateChangeObject = new object(); + + /// /// Delay between logins of multiple bots. /// /// TODO: This value needs to be configurable by a command line argument. @@ -63,19 +82,24 @@ namespace pCampBot protected CommandConsole m_console; /// - /// Created bots, whether active or inactive. + /// Controls whether bots start out sending agent updates on connection. /// - protected List m_lBot; + public bool InitBotSendAgentUpdates { get; set; } /// - /// Random number generator. + /// Controls whether bots request textures for the object information they receive /// - public Random Rng { get; private set; } + public bool InitBotRequestObjectTextures { get; set; } /// - /// Overall configuration. + /// Created bots, whether active or inactive. /// - public IConfig Config { get; private set; } + protected List m_bots; + + /// + /// Random number generator. + /// + public Random Rng { get; private set; } /// /// Track the assets we have and have not received so we don't endlessly repeat requests. @@ -88,10 +112,68 @@ namespace pCampBot public Dictionary RegionsKnown { get; private set; } /// + /// First name for bots + /// + private string m_firstName; + + /// + /// Last name stem for bots + /// + private string m_lastNameStem; + + /// + /// Password for bots + /// + private string m_password; + + /// + /// Login URI for bots. + /// + private string m_loginUri; + + /// + /// Start location for bots. + /// + private string m_startUri; + + /// + /// Postfix bot number at which bot sequence starts. + /// + private int m_fromBotNumber; + + /// + /// Wear setting for bots. + /// + private string m_wearSetting; + + /// + /// Behaviour switches for bots. + /// + private HashSet m_defaultBehaviourSwitches = new HashSet(); + + /// + /// Collects general information on this server (which reveals this to be a misnamed class). + /// + private ServerStatsCollector m_serverStatsCollector; + + /// /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data /// public BotManager() { + // We set this to avoid issues with bots running out of HTTP connections if many are run from a single machine + // to multiple regions. + Settings.MAX_HTTP_CONNECTIONS = int.MaxValue; + +// System.Threading.ThreadPool.SetMaxThreads(600, 240); +// +// int workerThreads, iocpThreads; +// System.Threading.ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads); +// Console.WriteLine("ThreadPool.GetMaxThreads {0} {1}", workerThreads, iocpThreads); + + InitBotSendAgentUpdates = true; + InitBotRequestObjectTextures = true; + LoginDelay = DefaultLoginDelay; Rng = new Random(Environment.TickCount); @@ -117,30 +199,84 @@ namespace pCampBot } } - m_console.Commands.AddCommand("bot", false, "shutdown", - "shutdown", - "Shutdown bots and exit", HandleShutdown); - - m_console.Commands.AddCommand("bot", false, "quit", - "quit", - "Shutdown bots and exit", - HandleShutdown); - - m_console.Commands.AddCommand("bot", false, "show regions", - "show regions", - "Show regions known to bots", - HandleShowRegions); - - m_console.Commands.AddCommand("bot", false, "show bots", - "show bots", - "Shows the status of all bots", - HandleShowStatus); - -// m_console.Commands.AddCommand("bot", false, "add bots", -// "add bots ", -// "Add more bots", HandleAddBots); - - m_lBot = new List(); + m_console.Commands.AddCommand( + "Bots", false, "shutdown", "shutdown", "Shutdown bots and exit", HandleShutdown); + + m_console.Commands.AddCommand( + "Bots", false, "quit", "quit", "Shutdown bots and exit", HandleShutdown); + + m_console.Commands.AddCommand( + "Bots", false, "connect", "connect []", "Connect bots", + "If an is given, then the first disconnected bots by postfix number are connected.\n" + + "If no is given, then all currently disconnected bots are connected.", + HandleConnect); + + m_console.Commands.AddCommand( + "Bots", false, "disconnect", "disconnect []", "Disconnect bots", + "Disconnecting bots will interupt any bot connection process, including connection on startup.\n" + + "If an is given, then the last connected bots by postfix number are disconnected.\n" + + "If no is given, then all currently connected bots are disconnected.", + HandleDisconnect); + + m_console.Commands.AddCommand( + "Bots", false, "add behaviour", "add behaviour []", + "Add a behaviour to a bot", + "If no bot number is specified then behaviour is added to all bots.\n" + + "Can be performed on connected or disconnected bots.", + HandleAddBehaviour); + + m_console.Commands.AddCommand( + "Bots", false, "remove behaviour", "remove behaviour []", + "Remove a behaviour from a bot", + "If no bot number is specified then behaviour is added to all bots.\n" + + "Can be performed on connected or disconnected bots.", + HandleRemoveBehaviour); + + m_console.Commands.AddCommand( + "Bots", false, "sit", "sit", "Sit all bots on the ground.", + HandleSit); + + m_console.Commands.AddCommand( + "Bots", false, "stand", "stand", "Stand all bots.", + HandleStand); + + m_console.Commands.AddCommand( + "Bots", false, "set bots", "set bots ", "Set a setting for all bots.", HandleSetBots); + + m_console.Commands.AddCommand( + "Bots", false, "show regions", "show regions", "Show regions known to bots", HandleShowRegions); + + m_console.Commands.AddCommand( + "Bots", false, "show bots", "show bots", "Shows the status of all bots.", HandleShowBotsStatus); + + m_console.Commands.AddCommand( + "Bots", false, "show bot", "show bot ", + "Shows the detailed status and settings of a particular bot.", HandleShowBotStatus); + + m_console.Commands.AddCommand( + "Debug", + false, + "debug lludp packet", + "debug lludp packet ", + "Turn on received packet logging.", + "If level > 0 then all received packets that are not duplicates are logged.\n" + + "If level <= 0 then no received packets are logged.", + HandleDebugLludpPacketCommand); + + m_console.Commands.AddCommand( + "Bots", false, "show status", "show status", "Shows pCampbot status.", HandleShowStatus); + + m_bots = new List(); + + Watchdog.Enabled = true; + StatsManager.RegisterConsoleCommands(m_console); + + m_serverStatsCollector = new ServerStatsCollector(); + m_serverStatsCollector.Initialise(null); + m_serverStatsCollector.Enabled = true; + m_serverStatsCollector.Start(); + + BotConnectingState = BotManagerBotConnectingState.Ready; } /// @@ -148,74 +284,195 @@ namespace pCampBot /// /// How many bots to start up /// The configuration for the bots to use - public void dobotStartup(int botcount, IConfig cs) + public void CreateBots(int botcount, IConfig startupConfig) { - Config = cs; + m_firstName = startupConfig.GetString("firstname"); + m_lastNameStem = startupConfig.GetString("lastname"); + m_password = startupConfig.GetString("password"); + m_loginUri = startupConfig.GetString("loginuri"); + m_fromBotNumber = startupConfig.GetInt("from", 0); + m_wearSetting = startupConfig.GetString("wear", "no"); - string firstName = cs.GetString("firstname"); - string lastNameStem = cs.GetString("lastname"); - string password = cs.GetString("password"); - string loginUri = cs.GetString("loginuri"); + m_startUri = ParseInputStartLocationToUri(startupConfig.GetString("start", "last")); - HashSet behaviourSwitches = new HashSet(); Array.ForEach( - cs.GetString("behaviours", "p").Split(new char[] { ',' }), b => behaviourSwitches.Add(b)); + startupConfig.GetString("behaviours", "p").Split(new char[] { ',' }), b => m_defaultBehaviourSwitches.Add(b)); - MainConsole.Instance.OutputFormat( - "[BOT MANAGER]: Starting {0} bots connecting to {1}, named {2} {3}_", - botcount, - loginUri, - firstName, - lastNameStem); + for (int i = 0; i < botcount; i++) + { + lock (m_bots) + { + string lastName = string.Format("{0}_{1}", m_lastNameStem, i + m_fromBotNumber); + + CreateBot( + this, + CreateBehavioursFromAbbreviatedNames(m_defaultBehaviourSwitches), + m_firstName, lastName, m_password, m_loginUri, m_startUri, m_wearSetting); + } + } + } - MainConsole.Instance.OutputFormat("[BOT MANAGER]: Delay between logins is {0}ms", LoginDelay); + private List CreateBehavioursFromAbbreviatedNames(HashSet abbreviatedNames) + { + // We must give each bot its own list of instantiated behaviours since they store state. + List behaviours = new List(); - for (int i = 0; i < botcount; i++) + // Hard-coded for now + foreach (string abName in abbreviatedNames) { - string lastName = string.Format("{0}_{1}", lastNameStem, i); - - // We must give each bot its own list of instantiated behaviours since they store state. - List behaviours = new List(); - - // Hard-coded for now - if (behaviourSwitches.Contains("p")) - behaviours.Add(new PhysicsBehaviour()); - - if (behaviourSwitches.Contains("g")) - behaviours.Add(new GrabbingBehaviour()); - - if (behaviourSwitches.Contains("t")) - behaviours.Add(new TeleportBehaviour()); - - if (behaviourSwitches.Contains("c")) - behaviours.Add(new CrossBehaviour()); - - StartBot(this, behaviours, firstName, lastName, password, loginUri); + IBehaviour newBehaviour = null; + + if (abName == "c") + newBehaviour = new CrossBehaviour(); + + if (abName == "g") + newBehaviour = new GrabbingBehaviour(); + + if (abName == "n") + newBehaviour = new NoneBehaviour(); + + if (abName == "p") + newBehaviour = new PhysicsBehaviour(); + + if (abName == "t") + newBehaviour = new TeleportBehaviour(); + + if (abName == "tw") + newBehaviour = new TwitchyBehaviour(); + + if (abName == "ph2") + newBehaviour = new PhysicsBehaviour2(); + + if (abName == "inv") + newBehaviour = new InventoryDownloadBehaviour(); + + if (newBehaviour != null) + { + behaviours.Add(newBehaviour); + } + else + { + MainConsole.Instance.OutputFormat("No behaviour with abbreviated name {0} found", abName); + } } + + return behaviours; } -// /// -// /// Add additional bots (and threads) to our bot pool -// /// -// /// How Many of them to add -// public void addbots(int botcount) -// { -// int len = m_td.Length; -// Thread[] m_td2 = new Thread[len + botcount]; -// for (int i = 0; i < len; i++) -// { -// m_td2[i] = m_td[i]; -// } -// m_td = m_td2; -// int newlen = len + botcount; -// for (int i = len; i < newlen; i++) -// { -// startupBot(Config); -// } -// } + public void ConnectBots(int botcount) + { + lock (BotConnectingStateChangeObject) + { + if (BotConnectingState != BotManagerBotConnectingState.Ready) + { + MainConsole.Instance.OutputFormat( + "Bot connecting status is {0}. Please wait for previous process to complete.", BotConnectingState); + return; + } + + BotConnectingState = BotManagerBotConnectingState.Connecting; + } + + Thread connectBotThread = new Thread(o => ConnectBotsInternal(botcount)); + + connectBotThread.Name = "Bots connection thread"; + connectBotThread.Start(); + } + + private void ConnectBotsInternal(int botCount) + { + m_log.InfoFormat( + "[BOT MANAGER]: Starting {0} bots connecting to {1}, location {2}, named {3} {4}_", + botCount, + m_loginUri, + m_startUri, + m_firstName, + m_lastNameStem); + + m_log.DebugFormat("[BOT MANAGER]: Delay between logins is {0}ms", LoginDelay); + m_log.DebugFormat("[BOT MANAGER]: BotsSendAgentUpdates is {0}", InitBotSendAgentUpdates); + m_log.DebugFormat("[BOT MANAGER]: InitBotRequestObjectTextures is {0}", InitBotRequestObjectTextures); + + List botsToConnect = new List(); + + lock (m_bots) + { + foreach (Bot bot in m_bots) + { + if (bot.ConnectionState == ConnectionState.Disconnected) + botsToConnect.Add(bot); + + if (botsToConnect.Count >= botCount) + break; + } + } + + foreach (Bot bot in botsToConnect) + { + lock (BotConnectingStateChangeObject) + { + if (BotConnectingState != BotManagerBotConnectingState.Connecting) + { + MainConsole.Instance.Output( + "[BOT MANAGER]: Aborting bot connection due to user-initiated disconnection"); + return; + } + } + + bot.Connect(); + + // Stagger logins + Thread.Sleep(LoginDelay); + } + + lock (BotConnectingStateChangeObject) + { + if (BotConnectingState == BotManagerBotConnectingState.Connecting) + BotConnectingState = BotManagerBotConnectingState.Ready; + } + } + + /// + /// Parses the command line start location to a start string/uri that the login mechanism will recognize. + /// + /// + /// The input start location to URI. + /// + /// + /// Start location. + /// + private string ParseInputStartLocationToUri(string startLocation) + { + if (startLocation == "home" || startLocation == "last") + return startLocation; + + string regionName; + + // Just a region name or only one (!) extra component. Like a viewer, we will stick 128/128/0 on the end + Vector3 startPos = new Vector3(128, 128, 0); + + string[] startLocationComponents = startLocation.Split('/'); + + regionName = startLocationComponents[0]; + + if (startLocationComponents.Length >= 2) + { + float.TryParse(startLocationComponents[1], out startPos.X); + + if (startLocationComponents.Length >= 3) + { + float.TryParse(startLocationComponents[2], out startPos.Y); + + if (startLocationComponents.Length >= 4) + float.TryParse(startLocationComponents[3], out startPos.Z); + } + } + + return string.Format("uri:{0}&{1}&{2}&{3}", regionName, startPos.X, startPos.Y, startPos.Z); + } /// - /// This starts up the bot and stores the thread for the bot in the thread array + /// This creates a bot but does not start it. /// /// /// Behaviours for this bot to perform. @@ -223,30 +480,25 @@ namespace pCampBot /// Last name /// Password /// Login URI - public void StartBot( + /// Location to start the bot. Can be "last", "home" or a specific sim name. + /// + public void CreateBot( BotManager bm, List behaviours, - string firstName, string lastName, string password, string loginUri) + string firstName, string lastName, string password, string loginUri, string startLocation, string wearSetting) { MainConsole.Instance.OutputFormat( - "[BOT MANAGER]: Starting bot {0} {1}, behaviours are {2}", + "[BOT MANAGER]: Creating bot {0} {1}, behaviours are {2}", firstName, lastName, string.Join(",", behaviours.ConvertAll(b => b.Name).ToArray())); - Bot pb = new Bot(bm, behaviours, firstName, lastName, password, loginUri); + Bot pb = new Bot(bm, behaviours, firstName, lastName, password, startLocation, loginUri); + pb.wear = wearSetting; + pb.Client.Settings.SEND_AGENT_UPDATES = InitBotSendAgentUpdates; + pb.RequestObjectTextures = InitBotRequestObjectTextures; pb.OnConnected += handlebotEvent; pb.OnDisconnected += handlebotEvent; - lock (m_lBot) - m_lBot.Add(pb); - - Thread pbThread = new Thread(pb.startup); - pbThread.Name = pb.Name; - pbThread.IsBackground = true; - - pbThread.Start(); - - // Stagger logins - Thread.Sleep(LoginDelay); + m_bots.Add(pb); } /// @@ -259,52 +511,322 @@ namespace pCampBot switch (eventt) { case EventType.CONNECTED: + { m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Connected"); break; + } + case EventType.DISCONNECTED: + { m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Disconnected"); + break; + } + } + } - lock (m_lBot) - { - if (m_lBot.TrueForAll(b => b.ConnectionState == ConnectionState.Disconnected)) - Environment.Exit(0); + /// + /// Standard CreateConsole routine + /// + /// + protected CommandConsole CreateConsole() + { + return new LocalConsole("pCampbot"); + } - break; + private void HandleConnect(string module, string[] cmd) + { + lock (m_bots) + { + int botsToConnect; + int disconnectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Disconnected); + + if (cmd.Length == 1) + { + botsToConnect = disconnectedBots; + } + else + { + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[1], out botsToConnect)) + return; + + botsToConnect = Math.Min(botsToConnect, disconnectedBots); + } + + MainConsole.Instance.OutputFormat("Connecting {0} bots", botsToConnect); + + ConnectBots(botsToConnect); + } + } + + private void HandleAddBehaviour(string module, string[] cmd) + { + if (cmd.Length < 3 || cmd.Length > 4) + { + MainConsole.Instance.OutputFormat("Usage: add behaviour []"); + return; + } + + string rawBehaviours = cmd[2]; + + List botsToEffect = new List(); + + if (cmd.Length == 3) + { + lock (m_bots) + botsToEffect.AddRange(m_bots); + } + else + { + int botNumber; + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber)) + return; + + Bot bot = GetBotFromNumber(botNumber); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber); + return; + } + + botsToEffect.Add(bot); + } + + + HashSet rawAbbreviatedSwitchesToAdd = new HashSet(); + Array.ForEach(rawBehaviours.Split(new char[] { ',' }), b => rawAbbreviatedSwitchesToAdd.Add(b)); + + foreach (Bot bot in botsToEffect) + { + List behavioursAdded = new List(); + + foreach (IBehaviour behaviour in CreateBehavioursFromAbbreviatedNames(rawAbbreviatedSwitchesToAdd)) + { + if (bot.AddBehaviour(behaviour)) + behavioursAdded.Add(behaviour); + } + + MainConsole.Instance.OutputFormat( + "Added behaviours {0} to bot {1}", + string.Join(", ", behavioursAdded.ConvertAll(b => b.Name).ToArray()), bot.Name); + } + } + + private void HandleRemoveBehaviour(string module, string[] cmd) + { + if (cmd.Length < 3 || cmd.Length > 4) + { + MainConsole.Instance.OutputFormat("Usage: remove behaviour []"); + return; + } + + string rawBehaviours = cmd[2]; + + List botsToEffect = new List(); + + if (cmd.Length == 3) + { + lock (m_bots) + botsToEffect.AddRange(m_bots); + } + else + { + int botNumber; + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber)) + return; + + Bot bot = GetBotFromNumber(botNumber); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber); + return; + } + + botsToEffect.Add(bot); + } + + HashSet abbreviatedBehavioursToRemove = new HashSet(); + Array.ForEach(rawBehaviours.Split(new char[] { ',' }), b => abbreviatedBehavioursToRemove.Add(b)); + + foreach (Bot bot in botsToEffect) + { + List behavioursRemoved = new List(); + + foreach (string b in abbreviatedBehavioursToRemove) + { + IBehaviour behaviour; + + if (bot.TryGetBehaviour(b, out behaviour)) + { + bot.RemoveBehaviour(b); + behavioursRemoved.Add(behaviour); } + } + + MainConsole.Instance.OutputFormat( + "Removed behaviours {0} from bot {1}", + string.Join(", ", behavioursRemoved.ConvertAll(b => b.Name).ToArray()), bot.Name); } } - /// - /// Shut down all bots - /// - /// - /// We launch each shutdown on its own thread so that a slow shutting down bot doesn't hold up all the others. - /// - public void doBotShutdown() + private void HandleDisconnect(string module, string[] cmd) + { + List connectedBots; + int botsToDisconnectCount; + + lock (m_bots) + connectedBots = m_bots.FindAll(b => b.ConnectionState == ConnectionState.Connected); + + if (cmd.Length == 1) + { + botsToDisconnectCount = connectedBots.Count; + } + else + { + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[1], out botsToDisconnectCount)) + return; + + botsToDisconnectCount = Math.Min(botsToDisconnectCount, connectedBots.Count); + } + + lock (BotConnectingStateChangeObject) + BotConnectingState = BotManagerBotConnectingState.Disconnecting; + + Thread disconnectBotThread = new Thread(o => DisconnectBotsInternal(connectedBots, botsToDisconnectCount)); + + disconnectBotThread.Name = "Bots disconnection thread"; + disconnectBotThread.Start(); + } + + private void DisconnectBotsInternal(List connectedBots, int disconnectCount) { - lock (m_lBot) + MainConsole.Instance.OutputFormat("Disconnecting {0} bots", disconnectCount); + + int disconnectedBots = 0; + + for (int i = connectedBots.Count - 1; i >= 0; i--) { - foreach (Bot bot in m_lBot) + if (disconnectedBots >= disconnectCount) + break; + + Bot thisBot = connectedBots[i]; + + if (thisBot.ConnectionState == ConnectionState.Connected) { - Bot thisBot = bot; - Util.FireAndForget(o => thisBot.shutdown()); + ThreadPool.QueueUserWorkItem(o => thisBot.Disconnect()); + disconnectedBots++; } } + + lock (BotConnectingStateChangeObject) + BotConnectingState = BotManagerBotConnectingState.Ready; } - /// - /// Standard CreateConsole routine - /// - /// - protected CommandConsole CreateConsole() + private void HandleSit(string module, string[] cmd) { - return new LocalConsole("pCampbot"); + lock (m_bots) + { + foreach (Bot bot in m_bots) + { + if (bot.ConnectionState == ConnectionState.Connected) + { + MainConsole.Instance.OutputFormat("Sitting bot {0} on ground.", bot.Name); + bot.SitOnGround(); + } + } + } + } + + private void HandleStand(string module, string[] cmd) + { + lock (m_bots) + { + foreach (Bot bot in m_bots) + { + if (bot.ConnectionState == ConnectionState.Connected) + { + MainConsole.Instance.OutputFormat("Standing bot {0} from ground.", bot.Name); + bot.Stand(); + } + } + } } private void HandleShutdown(string module, string[] cmd) { - m_log.Info("[BOTMANAGER]: Shutting down bots"); - doBotShutdown(); + lock (m_bots) + { + int connectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Connected); + + if (connectedBots > 0) + { + MainConsole.Instance.OutputFormat("Please disconnect {0} connected bots first", connectedBots); + return; + } + } + + MainConsole.Instance.Output("Shutting down"); + + m_serverStatsCollector.Close(); + + Environment.Exit(0); + } + + private void HandleSetBots(string module, string[] cmd) + { + string key = cmd[2]; + string rawValue = cmd[3]; + + if (key == "SEND_AGENT_UPDATES") + { + bool newSendAgentUpdatesSetting; + + if (!ConsoleUtil.TryParseConsoleBool(MainConsole.Instance, rawValue, out newSendAgentUpdatesSetting)) + return; + + MainConsole.Instance.OutputFormat( + "Setting SEND_AGENT_UPDATES to {0} for all bots", newSendAgentUpdatesSetting); + + lock (m_bots) + m_bots.ForEach(b => b.Client.Settings.SEND_AGENT_UPDATES = newSendAgentUpdatesSetting); + } + else + { + MainConsole.Instance.Output("Error: Only setting currently available is SEND_AGENT_UPDATES"); + } + } + + private void HandleDebugLludpPacketCommand(string module, string[] args) + { + if (args.Length != 6) + { + MainConsole.Instance.OutputFormat("Usage: debug lludp packet "); + return; + } + + int level; + + if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[3], out level)) + return; + + string botFirstName = args[4]; + string botLastName = args[5]; + + Bot bot; + + lock (m_bots) + bot = m_bots.FirstOrDefault(b => b.FirstName == botFirstName && b.LastName == botLastName); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("No bot named {0} {1}", botFirstName, botLastName); + return; + } + + bot.PacketDebugLevel = level; + + MainConsole.Instance.OutputFormat("Set debug level of {0} to {1}", bot.Name, bot.PacketDebugLevel); } private void HandleShowRegions(string module, string[] cmd) @@ -324,41 +846,120 @@ namespace pCampBot private void HandleShowStatus(string module, string[] cmd) { - string outputFormat = "{0,-30} {1, -30} {2,-14}"; - MainConsole.Instance.OutputFormat(outputFormat, "Name", "Region", "Status"); + ConsoleDisplayList cdl = new ConsoleDisplayList(); + cdl.AddRow("Bot connecting state", BotConnectingState); - lock (m_lBot) + MainConsole.Instance.Output(cdl.ToString()); + } + + private void HandleShowBotsStatus(string module, string[] cmd) + { + ConsoleDisplayTable cdt = new ConsoleDisplayTable(); + cdt.AddColumn("Name", 24); + cdt.AddColumn("Region", 24); + cdt.AddColumn("Status", 13); + cdt.AddColumn("Conns", 5); + cdt.AddColumn("Behaviours", 20); + + Dictionary totals = new Dictionary(); + foreach (object o in Enum.GetValues(typeof(ConnectionState))) + totals[(ConnectionState)o] = 0; + + lock (m_bots) { - foreach (Bot pb in m_lBot) + foreach (Bot bot in m_bots) { - Simulator currentSim = pb.Client.Network.CurrentSim; - - MainConsole.Instance.OutputFormat( - outputFormat, - pb.Name, currentSim != null ? currentSim.Name : "(none)", pb.ConnectionState); + Simulator currentSim = bot.Client.Network.CurrentSim; + totals[bot.ConnectionState]++; + + cdt.AddRow( + bot.Name, + currentSim != null ? currentSim.Name : "(none)", + bot.ConnectionState, + bot.SimulatorsCount, + string.Join(",", bot.Behaviours.Keys.ToArray())); } } + + MainConsole.Instance.Output(cdt.ToString()); + + ConsoleDisplayList cdl = new ConsoleDisplayList(); + + foreach (KeyValuePair kvp in totals) + cdl.AddRow(kvp.Key, kvp.Value); + + MainConsole.Instance.Output(cdl.ToString()); } - /* - private void HandleQuit(string module, string[] cmd) + private void HandleShowBotStatus(string module, string[] cmd) { - m_console.Warn("DANGER", "This should only be used to quit the program if you've already used the shutdown command and the program hasn't quit"); - Environment.Exit(0); + if (cmd.Length != 3) + { + MainConsole.Instance.Output("Usage: show bot "); + return; + } + + int botNumber; + + if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, cmd[2], out botNumber)) + return; + + Bot bot = GetBotFromNumber(botNumber); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber); + return; + } + + ConsoleDisplayList cdl = new ConsoleDisplayList(); + cdl.AddRow("Name", bot.Name); + cdl.AddRow("Status", bot.ConnectionState); + + Simulator currentSim = bot.Client.Network.CurrentSim; + cdl.AddRow("Region", currentSim != null ? currentSim.Name : "(none)"); + + List connectedSimulators = bot.Simulators; + List simulatorNames = connectedSimulators.ConvertAll(cs => cs.Name); + cdl.AddRow("Connections", string.Join(", ", simulatorNames.ToArray())); + + MainConsole.Instance.Output(cdl.ToString()); + + MainConsole.Instance.Output("Settings"); + + ConsoleDisplayList statusCdl = new ConsoleDisplayList(); + + statusCdl.AddRow( + "Behaviours", + string.Join(", ", bot.Behaviours.Values.ToList().ConvertAll(b => b.Name).ToArray())); + + GridClient botClient = bot.Client; + statusCdl.AddRow("SEND_AGENT_UPDATES", botClient.Settings.SEND_AGENT_UPDATES); + + MainConsole.Instance.Output(statusCdl.ToString()); + } + + /// + /// Get a specific bot from its number. + /// + /// null if no bot was found + /// + private Bot GetBotFromNumber(int botNumber) + { + string name = GenerateBotNameFromNumber(botNumber); + + Bot bot; + + lock (m_bots) + bot = m_bots.Find(b => b.Name == name); + + return bot; + } + + private string GenerateBotNameFromNumber(int botNumber) + { + return string.Format("{0} {1}_{2}", m_firstName, m_lastNameStem, botNumber); } - */ -// -// private void HandleAddBots(string module, string[] cmd) -// { -// int newbots = 0; -// -// if (cmd.Length > 2) -// { -// Int32.TryParse(cmd[2], out newbots); -// } -// if (newbots > 0) -// addbots(newbots); -// } internal void Grid_GridRegion(object o, GridRegionEventArgs args) { @@ -379,4 +980,4 @@ namespace pCampBot } } } -} +} \ No newline at end of file -- cgit v1.1