clockwerk-opensim-stable – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27  
28 using System;
29 using System.Collections.Generic;
30 using System.Linq;
31 using System.Reflection;
32 using System.Threading;
33 using OpenMetaverse;
34 using log4net;
35 using log4net.Appender;
36 using log4net.Core;
37 using log4net.Repository;
38 using Nini.Config;
39 using OpenSim.Framework;
40 using OpenSim.Framework.Console;
41 using pCampBot.Interfaces;
42  
43 namespace pCampBot
44 {
45 /// <summary>
46 /// Thread/Bot manager for the application
47 /// </summary>
48 public class BotManager
49 {
50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51  
52 public const int DefaultLoginDelay = 5000;
53  
54 /// <summary>
55 /// Is pCampbot in the process of connecting bots?
56 /// </summary>
57 public bool ConnectingBots { get; private set; }
58  
59 /// <summary>
60 /// Is pCampbot in the process of disconnecting bots?
61 /// </summary>
62 public bool DisconnectingBots { get; private set; }
63  
64 /// <summary>
65 /// Delay between logins of multiple bots.
66 /// </summary>
67 /// <remarks>TODO: This value needs to be configurable by a command line argument.</remarks>
68 public int LoginDelay { get; set; }
69  
70 /// <summary>
71 /// Command console
72 /// </summary>
73 protected CommandConsole m_console;
74  
75 /// <summary>
76 /// Controls whether bots start out sending agent updates on connection.
77 /// </summary>
78 public bool InitBotSendAgentUpdates { get; set; }
79  
80 /// <summary>
81 /// Controls whether bots request textures for the object information they receive
82 /// </summary>
83 public bool InitBotRequestObjectTextures { get; set; }
84  
85 /// <summary>
86 /// Created bots, whether active or inactive.
87 /// </summary>
88 protected List<Bot> m_bots;
89  
90 /// <summary>
91 /// Random number generator.
92 /// </summary>
93 public Random Rng { get; private set; }
94  
95 /// <summary>
96 /// Track the assets we have and have not received so we don't endlessly repeat requests.
97 /// </summary>
98 public Dictionary<UUID, bool> AssetsReceived { get; private set; }
99  
100 /// <summary>
101 /// The regions that we know about.
102 /// </summary>
103 public Dictionary<ulong, GridRegion> RegionsKnown { get; private set; }
104  
105 /// <summary>
106 /// First name for bots
107 /// </summary>
108 private string m_firstName;
109  
110 /// <summary>
111 /// Last name stem for bots
112 /// </summary>
113 private string m_lastNameStem;
114  
115 /// <summary>
116 /// Password for bots
117 /// </summary>
118 private string m_password;
119  
120 /// <summary>
121 /// Login URI for bots.
122 /// </summary>
123 private string m_loginUri;
124  
125 /// <summary>
126 /// Start location for bots.
127 /// </summary>
128 private string m_startUri;
129  
130 /// <summary>
131 /// Postfix bot number at which bot sequence starts.
132 /// </summary>
133 private int m_fromBotNumber;
134  
135 /// <summary>
136 /// Wear setting for bots.
137 /// </summary>
138 private string m_wearSetting;
139  
140 /// <summary>
141 /// Behaviour switches for bots.
142 /// </summary>
143 private HashSet<string> m_defaultBehaviourSwitches = new HashSet<string>();
144  
145 /// <summary>
146 /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data
147 /// </summary>
148 public BotManager()
149 {
150 InitBotSendAgentUpdates = true;
151 InitBotRequestObjectTextures = true;
152  
153 LoginDelay = DefaultLoginDelay;
154  
155 Rng = new Random(Environment.TickCount);
156 AssetsReceived = new Dictionary<UUID, bool>();
157 RegionsKnown = new Dictionary<ulong, GridRegion>();
158  
159 m_console = CreateConsole();
160 MainConsole.Instance = m_console;
161  
162 // Make log4net see the console
163 //
164 ILoggerRepository repository = LogManager.GetRepository();
165 IAppender[] appenders = repository.GetAppenders();
166 OpenSimAppender consoleAppender = null;
167  
168 foreach (IAppender appender in appenders)
169 {
170 if (appender.Name == "Console")
171 {
172 consoleAppender = (OpenSimAppender)appender;
173 consoleAppender.Console = m_console;
174 break;
175 }
176 }
177  
178 m_console.Commands.AddCommand(
179 "bot", false, "shutdown", "shutdown", "Shutdown bots and exit", HandleShutdown);
180  
181 m_console.Commands.AddCommand(
182 "bot", false, "quit", "quit", "Shutdown bots and exit", HandleShutdown);
183  
184 m_console.Commands.AddCommand(
185 "bot", false, "connect", "connect [<n>]", "Connect bots",
186 "If an <n> is given, then the first <n> disconnected bots by postfix number are connected.\n"
187 + "If no <n> is given, then all currently disconnected bots are connected.",
188 HandleConnect);
189  
190 m_console.Commands.AddCommand(
191 "bot", false, "disconnect", "disconnect [<n>]", "Disconnect bots",
192 "Disconnecting bots will interupt any bot connection process, including connection on startup.\n"
193 + "If an <n> is given, then the last <n> connected bots by postfix number are disconnected.\n"
194 + "If no <n> is given, then all currently connected bots are disconnected.",
195 HandleDisconnect);
196  
197 m_console.Commands.AddCommand(
198 "bot", false, "add behaviour", "add behaviour <abbreviated-name> [<bot-number>]",
199 "Add a behaviour to a bot",
200 "If no bot number is specified then behaviour is added to all bots.\n"
201 + "Can be performed on connected or disconnected bots.",
202 HandleAddBehaviour);
203  
204 m_console.Commands.AddCommand(
205 "bot", false, "remove behaviour", "remove behaviour <abbreviated-name> [<bot-number>]",
206 "Remove a behaviour from a bot",
207 "If no bot number is specified then behaviour is added to all bots.\n"
208 + "Can be performed on connected or disconnected bots.",
209 HandleRemoveBehaviour);
210  
211 m_console.Commands.AddCommand(
212 "bot", false, "sit", "sit", "Sit all bots on the ground.",
213 HandleSit);
214  
215 m_console.Commands.AddCommand(
216 "bot", false, "stand", "stand", "Stand all bots.",
217 HandleStand);
218  
219 m_console.Commands.AddCommand(
220 "bot", false, "set bots", "set bots <key> <value>", "Set a setting for all bots.", HandleSetBots);
221  
222 m_console.Commands.AddCommand(
223 "bot", false, "show regions", "show regions", "Show regions known to bots", HandleShowRegions);
224  
225 m_console.Commands.AddCommand(
226 "bot", false, "show bots", "show bots", "Shows the status of all bots", HandleShowBotsStatus);
227  
228 m_console.Commands.AddCommand(
229 "bot", false, "show bot", "show bot <bot-number>",
230 "Shows the detailed status and settings of a particular bot.", HandleShowBotStatus);
231  
232 m_bots = new List<Bot>();
233 }
234  
235 /// <summary>
236 /// Startup number of bots specified in the starting arguments
237 /// </summary>
238 /// <param name="botcount">How many bots to start up</param>
239 /// <param name="cs">The configuration for the bots to use</param>
240 public void CreateBots(int botcount, IConfig startupConfig)
241 {
242 m_firstName = startupConfig.GetString("firstname");
243 m_lastNameStem = startupConfig.GetString("lastname");
244 m_password = startupConfig.GetString("password");
245 m_loginUri = startupConfig.GetString("loginuri");
246 m_fromBotNumber = startupConfig.GetInt("from", 0);
247 m_wearSetting = startupConfig.GetString("wear", "no");
248  
249 m_startUri = ParseInputStartLocationToUri(startupConfig.GetString("start", "last"));
250  
251 Array.ForEach<string>(
252 startupConfig.GetString("behaviours", "p").Split(new char[] { ',' }), b => m_defaultBehaviourSwitches.Add(b));
253  
254 for (int i = 0; i < botcount; i++)
255 {
256 lock (m_bots)
257 {
258 string lastName = string.Format("{0}_{1}", m_lastNameStem, i + m_fromBotNumber);
259  
260 CreateBot(
261 this,
262 CreateBehavioursFromAbbreviatedNames(m_defaultBehaviourSwitches),
263 m_firstName, lastName, m_password, m_loginUri, m_startUri, m_wearSetting);
264 }
265 }
266 }
267  
268 private List<IBehaviour> CreateBehavioursFromAbbreviatedNames(HashSet<string> abbreviatedNames)
269 {
270 // We must give each bot its own list of instantiated behaviours since they store state.
271 List<IBehaviour> behaviours = new List<IBehaviour>();
272  
273 // Hard-coded for now
274 foreach (string abName in abbreviatedNames)
275 {
276 IBehaviour newBehaviour = null;
277  
278 if (abName == "c")
279 newBehaviour = new CrossBehaviour();
280  
281 if (abName == "g")
282 newBehaviour = new GrabbingBehaviour();
283  
284 if (abName == "n")
285 newBehaviour = new NoneBehaviour();
286  
287 if (abName == "p")
288 newBehaviour = new PhysicsBehaviour();
289  
290 if (abName == "t")
291 newBehaviour = new TeleportBehaviour();
292  
293 if (newBehaviour != null)
294 {
295 behaviours.Add(newBehaviour);
296 }
297 else
298 {
299 MainConsole.Instance.OutputFormat("No behaviour with abbreviated name {0} found", abName);
300 }
301 }
302  
303 return behaviours;
304 }
305  
306 public void ConnectBots(int botcount)
307 {
308 ConnectingBots = true;
309  
310 Thread connectBotThread = new Thread(o => ConnectBotsInternal(botcount));
311  
312 connectBotThread.Name = "Bots connection thread";
313 connectBotThread.Start();
314 }
315  
316 private void ConnectBotsInternal(int botCount)
317 {
318 MainConsole.Instance.OutputFormat(
319 "[BOT MANAGER]: Starting {0} bots connecting to {1}, location {2}, named {3} {4}_<n>",
320 botCount,
321 m_loginUri,
322 m_startUri,
323 m_firstName,
324 m_lastNameStem);
325  
326 MainConsole.Instance.OutputFormat("[BOT MANAGER]: Delay between logins is {0}ms", LoginDelay);
327 MainConsole.Instance.OutputFormat("[BOT MANAGER]: BotsSendAgentUpdates is {0}", InitBotSendAgentUpdates);
328 MainConsole.Instance.OutputFormat("[BOT MANAGER]: InitBotRequestObjectTextures is {0}", InitBotRequestObjectTextures);
329  
330 int connectedBots = 0;
331  
332 for (int i = 0; i < m_bots.Count; i++)
333 {
334 lock (m_bots)
335 {
336 if (DisconnectingBots)
337 {
338 MainConsole.Instance.Output(
339 "[BOT MANAGER]: Aborting bot connection due to user-initiated disconnection");
340 break;
341 }
342  
343 if (m_bots[i].ConnectionState == ConnectionState.Disconnected)
344 {
345 m_bots[i].Connect();
346 connectedBots++;
347  
348 if (connectedBots >= botCount)
349 break;
350  
351 // Stagger logins
352 Thread.Sleep(LoginDelay);
353 }
354 }
355 }
356  
357 ConnectingBots = false;
358 }
359  
360 /// <summary>
361 /// Parses the command line start location to a start string/uri that the login mechanism will recognize.
362 /// </summary>
363 /// <returns>
364 /// The input start location to URI.
365 /// </returns>
366 /// <param name='startLocation'>
367 /// Start location.
368 /// </param>
369 private string ParseInputStartLocationToUri(string startLocation)
370 {
371 if (startLocation == "home" || startLocation == "last")
372 return startLocation;
373  
374 string regionName;
375  
376 // Just a region name or only one (!) extra component. Like a viewer, we will stick 128/128/0 on the end
377 Vector3 startPos = new Vector3(128, 128, 0);
378  
379 string[] startLocationComponents = startLocation.Split('/');
380  
381 regionName = startLocationComponents[0];
382  
383 if (startLocationComponents.Length >= 2)
384 {
385 float.TryParse(startLocationComponents[1], out startPos.X);
386  
387 if (startLocationComponents.Length >= 3)
388 {
389 float.TryParse(startLocationComponents[2], out startPos.Y);
390  
391 if (startLocationComponents.Length >= 4)
392 float.TryParse(startLocationComponents[3], out startPos.Z);
393 }
394 }
395  
396 return string.Format("uri:{0}&{1}&{2}&{3}", regionName, startPos.X, startPos.Y, startPos.Z);
397 }
398  
399 /// <summary>
400 /// This creates a bot but does not start it.
401 /// </summary>
402 /// <param name="bm"></param>
403 /// <param name="behaviours">Behaviours for this bot to perform.</param>
404 /// <param name="firstName">First name</param>
405 /// <param name="lastName">Last name</param>
406 /// <param name="password">Password</param>
407 /// <param name="loginUri">Login URI</param>
408 /// <param name="startLocation">Location to start the bot. Can be "last", "home" or a specific sim name.</param>
409 /// <param name="wearSetting"></param>
410 public void CreateBot(
411 BotManager bm, List<IBehaviour> behaviours,
412 string firstName, string lastName, string password, string loginUri, string startLocation, string wearSetting)
413 {
414 MainConsole.Instance.OutputFormat(
415 "[BOT MANAGER]: Creating bot {0} {1}, behaviours are {2}",
416 firstName, lastName, string.Join(",", behaviours.ConvertAll<string>(b => b.Name).ToArray()));
417  
418 Bot pb = new Bot(bm, behaviours, firstName, lastName, password, startLocation, loginUri);
419 pb.wear = wearSetting;
420 pb.Client.Settings.SEND_AGENT_UPDATES = InitBotSendAgentUpdates;
421 pb.RequestObjectTextures = InitBotRequestObjectTextures;
422  
423 pb.OnConnected += handlebotEvent;
424 pb.OnDisconnected += handlebotEvent;
425  
426 m_bots.Add(pb);
427 }
428  
429 /// <summary>
430 /// High level connnected/disconnected events so we can keep track of our threads by proxy
431 /// </summary>
432 /// <param name="callbot"></param>
433 /// <param name="eventt"></param>
434 private void handlebotEvent(Bot callbot, EventType eventt)
435 {
436 switch (eventt)
437 {
438 case EventType.CONNECTED:
439 {
440 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Connected");
441 break;
442 }
443  
444 case EventType.DISCONNECTED:
445 {
446 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Disconnected");
447 break;
448 }
449 }
450 }
451  
452 /// <summary>
453 /// Standard CreateConsole routine
454 /// </summary>
455 /// <returns></returns>
456 protected CommandConsole CreateConsole()
457 {
458 return new LocalConsole("pCampbot");
459 }
460  
461 private void HandleConnect(string module, string[] cmd)
462 {
463 if (ConnectingBots)
464 {
465 MainConsole.Instance.Output("Still connecting bots. Please wait for previous process to complete.");
466 return;
467 }
468  
469 lock (m_bots)
470 {
471 int botsToConnect;
472 int disconnectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Disconnected);
473  
474 if (cmd.Length == 1)
475 {
476 botsToConnect = disconnectedBots;
477 }
478 else
479 {
480 if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[1], out botsToConnect))
481 return;
482  
483 botsToConnect = Math.Min(botsToConnect, disconnectedBots);
484 }
485  
486 MainConsole.Instance.OutputFormat("Connecting {0} bots", botsToConnect);
487  
488 ConnectBots(botsToConnect);
489 }
490 }
491  
492 private void HandleAddBehaviour(string module, string[] cmd)
493 {
494 if (cmd.Length < 3 || cmd.Length > 4)
495 {
496 MainConsole.Instance.OutputFormat("Usage: add behaviour <abbreviated-behaviour> [<bot-number>]");
497 return;
498 }
499  
500 string rawBehaviours = cmd[2];
501  
502 List<Bot> botsToEffect = new List<Bot>();
503  
504 if (cmd.Length == 3)
505 {
506 lock (m_bots)
507 botsToEffect.AddRange(m_bots);
508 }
509 else
510 {
511 int botNumber;
512 if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber))
513 return;
514  
515 Bot bot = GetBotFromNumber(botNumber);
516  
517 if (bot == null)
518 {
519 MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber);
520 return;
521 }
522  
523 botsToEffect.Add(bot);
524 }
525  
526  
527 HashSet<string> rawAbbreviatedSwitchesToAdd = new HashSet<string>();
528 Array.ForEach<string>(rawBehaviours.Split(new char[] { ',' }), b => rawAbbreviatedSwitchesToAdd.Add(b));
529  
530 foreach (Bot bot in botsToEffect)
531 {
532 List<IBehaviour> behavioursAdded = new List<IBehaviour>();
533  
534 foreach (IBehaviour behaviour in CreateBehavioursFromAbbreviatedNames(rawAbbreviatedSwitchesToAdd))
535 {
536 if (bot.AddBehaviour(behaviour))
537 behavioursAdded.Add(behaviour);
538 }
539  
540 MainConsole.Instance.OutputFormat(
541 "Added behaviours {0} to bot {1}",
542 string.Join(", ", behavioursAdded.ConvertAll<string>(b => b.Name).ToArray()), bot.Name);
543 }
544 }
545  
546 private void HandleRemoveBehaviour(string module, string[] cmd)
547 {
548 if (cmd.Length < 3 || cmd.Length > 4)
549 {
550 MainConsole.Instance.OutputFormat("Usage: remove behaviour <abbreviated-behaviour> [<bot-number>]");
551 return;
552 }
553  
554 string rawBehaviours = cmd[2];
555  
556 List<Bot> botsToEffect = new List<Bot>();
557  
558 if (cmd.Length == 3)
559 {
560 lock (m_bots)
561 botsToEffect.AddRange(m_bots);
562 }
563 else
564 {
565 int botNumber;
566 if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber))
567 return;
568  
569 Bot bot = GetBotFromNumber(botNumber);
570  
571 if (bot == null)
572 {
573 MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber);
574 return;
575 }
576  
577 botsToEffect.Add(bot);
578 }
579  
580 HashSet<string> abbreviatedBehavioursToRemove = new HashSet<string>();
581 Array.ForEach<string>(rawBehaviours.Split(new char[] { ',' }), b => abbreviatedBehavioursToRemove.Add(b));
582  
583 foreach (Bot bot in botsToEffect)
584 {
585 List<IBehaviour> behavioursRemoved = new List<IBehaviour>();
586  
587 foreach (string b in abbreviatedBehavioursToRemove)
588 {
589 IBehaviour behaviour;
590  
591 if (bot.TryGetBehaviour(b, out behaviour))
592 {
593 bot.RemoveBehaviour(b);
594 behavioursRemoved.Add(behaviour);
595 }
596 }
597  
598 MainConsole.Instance.OutputFormat(
599 "Removed behaviours {0} to bot {1}",
600 string.Join(", ", behavioursRemoved.ConvertAll<string>(b => b.Name).ToArray()), bot.Name);
601 }
602 }
603  
604 private void HandleDisconnect(string module, string[] cmd)
605 {
606 lock (m_bots)
607 {
608 int botsToDisconnect;
609 int connectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Connected);
610  
611 if (cmd.Length == 1)
612 {
613 botsToDisconnect = connectedBots;
614 }
615 else
616 {
617 if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[1], out botsToDisconnect))
618 return;
619  
620 botsToDisconnect = Math.Min(botsToDisconnect, connectedBots);
621 }
622  
623 DisconnectingBots = true;
624  
625 MainConsole.Instance.OutputFormat("Disconnecting {0} bots", botsToDisconnect);
626  
627 int disconnectedBots = 0;
628  
629 for (int i = m_bots.Count - 1; i >= 0; i--)
630 {
631 if (disconnectedBots >= botsToDisconnect)
632 break;
633  
634 Bot thisBot = m_bots[i];
635  
636 if (thisBot.ConnectionState == ConnectionState.Connected)
637 {
638 Util.FireAndForget(o => thisBot.Disconnect());
639 disconnectedBots++;
640 }
641 }
642  
643 DisconnectingBots = false;
644 }
645 }
646  
647 private void HandleSit(string module, string[] cmd)
648 {
649 lock (m_bots)
650 {
651 m_bots.ForEach(b => b.SitOnGround());
652 }
653 }
654  
655 private void HandleStand(string module, string[] cmd)
656 {
657 lock (m_bots)
658 {
659 m_bots.ForEach(b => b.Stand());
660 }
661 }
662  
663 private void HandleShutdown(string module, string[] cmd)
664 {
665 lock (m_bots)
666 {
667 int connectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Connected);
668  
669 if (connectedBots > 0)
670 {
671 MainConsole.Instance.OutputFormat("Please disconnect {0} connected bots first", connectedBots);
672 return;
673 }
674 }
675  
676 MainConsole.Instance.Output("Shutting down");
677  
678 Environment.Exit(0);
679 }
680  
681 private void HandleSetBots(string module, string[] cmd)
682 {
683 string key = cmd[2];
684 string rawValue = cmd[3];
685  
686 if (key == "SEND_AGENT_UPDATES")
687 {
688 bool newSendAgentUpdatesSetting;
689  
690 if (!ConsoleUtil.TryParseConsoleBool(MainConsole.Instance, rawValue, out newSendAgentUpdatesSetting))
691 return;
692  
693 MainConsole.Instance.OutputFormat(
694 "Setting SEND_AGENT_UPDATES to {0} for all bots", newSendAgentUpdatesSetting);
695  
696 lock (m_bots)
697 m_bots.ForEach(b => b.Client.Settings.SEND_AGENT_UPDATES = newSendAgentUpdatesSetting);
698 }
699 else
700 {
701 MainConsole.Instance.Output("Error: Only setting currently available is SEND_AGENT_UPDATES");
702 }
703 }
704  
705 private void HandleShowRegions(string module, string[] cmd)
706 {
707 string outputFormat = "{0,-30} {1, -20} {2, -5} {3, -5}";
708 MainConsole.Instance.OutputFormat(outputFormat, "Name", "Handle", "X", "Y");
709  
710 lock (RegionsKnown)
711 {
712 foreach (GridRegion region in RegionsKnown.Values)
713 {
714 MainConsole.Instance.OutputFormat(
715 outputFormat, region.Name, region.RegionHandle, region.X, region.Y);
716 }
717 }
718 }
719  
720 private void HandleShowBotsStatus(string module, string[] cmd)
721 {
722 ConsoleDisplayTable cdt = new ConsoleDisplayTable();
723 cdt.AddColumn("Name", 24);
724 cdt.AddColumn("Region", 24);
725 cdt.AddColumn("Status", 13);
726 cdt.AddColumn("Conns", 5);
727 cdt.AddColumn("Behaviours", 20);
728  
729 Dictionary<ConnectionState, int> totals = new Dictionary<ConnectionState, int>();
730 foreach (object o in Enum.GetValues(typeof(ConnectionState)))
731 totals[(ConnectionState)o] = 0;
732  
733 lock (m_bots)
734 {
735 foreach (Bot bot in m_bots)
736 {
737 Simulator currentSim = bot.Client.Network.CurrentSim;
738 totals[bot.ConnectionState]++;
739  
740 cdt.AddRow(
741 bot.Name,
742 currentSim != null ? currentSim.Name : "(none)",
743 bot.ConnectionState,
744 bot.SimulatorsCount,
745 string.Join(",", bot.Behaviours.Keys.ToArray()));
746 }
747 }
748  
749 MainConsole.Instance.Output(cdt.ToString());
750  
751 ConsoleDisplayList cdl = new ConsoleDisplayList();
752  
753 foreach (KeyValuePair<ConnectionState, int> kvp in totals)
754 cdl.AddRow(kvp.Key, kvp.Value);
755  
756 MainConsole.Instance.Output(cdl.ToString());
757 }
758  
759 private void HandleShowBotStatus(string module, string[] cmd)
760 {
761 if (cmd.Length != 3)
762 {
763 MainConsole.Instance.Output("Usage: show bot <n>");
764 return;
765 }
766  
767 int botNumber;
768  
769 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, cmd[2], out botNumber))
770 return;
771  
772 Bot bot = GetBotFromNumber(botNumber);
773  
774 if (bot == null)
775 {
776 MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber);
777 return;
778 }
779  
780 ConsoleDisplayList cdl = new ConsoleDisplayList();
781 cdl.AddRow("Name", bot.Name);
782 cdl.AddRow("Status", bot.ConnectionState);
783  
784 Simulator currentSim = bot.Client.Network.CurrentSim;
785 cdl.AddRow("Region", currentSim != null ? currentSim.Name : "(none)");
786  
787 List<Simulator> connectedSimulators = bot.Simulators;
788 List<string> simulatorNames = connectedSimulators.ConvertAll<string>(cs => cs.Name);
789 cdl.AddRow("Connections", string.Join(", ", simulatorNames.ToArray()));
790  
791 MainConsole.Instance.Output(cdl.ToString());
792  
793 MainConsole.Instance.Output("Settings");
794  
795 ConsoleDisplayList statusCdl = new ConsoleDisplayList();
796  
797 statusCdl.AddRow(
798 "Behaviours",
799 string.Join(", ", bot.Behaviours.Values.ToList().ConvertAll<string>(b => b.Name).ToArray()));
800  
801 GridClient botClient = bot.Client;
802 statusCdl.AddRow("SEND_AGENT_UPDATES", botClient.Settings.SEND_AGENT_UPDATES);
803  
804 MainConsole.Instance.Output(statusCdl.ToString());
805 }
806  
807 /// <summary>
808 /// Get a specific bot from its number.
809 /// </summary>
810 /// <returns>null if no bot was found</returns>
811 /// <param name='botNumber'></param>
812 private Bot GetBotFromNumber(int botNumber)
813 {
814 string name = GenerateBotNameFromNumber(botNumber);
815  
816 Bot bot;
817  
818 lock (m_bots)
819 bot = m_bots.Find(b => b.Name == name);
820  
821 return bot;
822 }
823  
824 private string GenerateBotNameFromNumber(int botNumber)
825 {
826 return string.Format("{0} {1}_{2}", m_firstName, m_lastNameStem, botNumber);
827 }
828  
829 internal void Grid_GridRegion(object o, GridRegionEventArgs args)
830 {
831 lock (RegionsKnown)
832 {
833 GridRegion newRegion = args.Region;
834  
835 if (RegionsKnown.ContainsKey(newRegion.RegionHandle))
836 {
837 return;
838 }
839 else
840 {
841 m_log.DebugFormat(
842 "[BOT MANAGER]: Adding {0} {1} to known regions", newRegion.Name, newRegion.RegionHandle);
843 RegionsKnown[newRegion.RegionHandle] = newRegion;
844 }
845 }
846 }
847 }
848 }