aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Tools/pCampBot/BotManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Tools/pCampBot/BotManager.cs')
-rw-r--r--OpenSim/Tools/pCampBot/BotManager.cs768
1 files changed, 617 insertions, 151 deletions
diff --git a/OpenSim/Tools/pCampBot/BotManager.cs b/OpenSim/Tools/pCampBot/BotManager.cs
index d615b3f..abc71cb 100644
--- a/OpenSim/Tools/pCampBot/BotManager.cs
+++ b/OpenSim/Tools/pCampBot/BotManager.cs
@@ -52,6 +52,16 @@ namespace pCampBot
52 public const int DefaultLoginDelay = 5000; 52 public const int DefaultLoginDelay = 5000;
53 53
54 /// <summary> 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>
55 /// Delay between logins of multiple bots. 65 /// Delay between logins of multiple bots.
56 /// </summary> 66 /// </summary>
57 /// <remarks>TODO: This value needs to be configurable by a command line argument.</remarks> 67 /// <remarks>TODO: This value needs to be configurable by a command line argument.</remarks>
@@ -63,19 +73,24 @@ namespace pCampBot
63 protected CommandConsole m_console; 73 protected CommandConsole m_console;
64 74
65 /// <summary> 75 /// <summary>
66 /// Created bots, whether active or inactive. 76 /// Controls whether bots start out sending agent updates on connection.
67 /// </summary> 77 /// </summary>
68 protected List<Bot> m_lBot; 78 public bool InitBotSendAgentUpdates { get; set; }
69 79
70 /// <summary> 80 /// <summary>
71 /// Random number generator. 81 /// Controls whether bots request textures for the object information they receive
72 /// </summary> 82 /// </summary>
73 public Random Rng { get; private set; } 83 public bool InitBotRequestObjectTextures { get; set; }
74 84
75 /// <summary> 85 /// <summary>
76 /// Overall configuration. 86 /// Created bots, whether active or inactive.
77 /// </summary> 87 /// </summary>
78 public IConfig Config { get; private set; } 88 protected List<Bot> m_bots;
89
90 /// <summary>
91 /// Random number generator.
92 /// </summary>
93 public Random Rng { get; private set; }
79 94
80 /// <summary> 95 /// <summary>
81 /// Track the assets we have and have not received so we don't endlessly repeat requests. 96 /// Track the assets we have and have not received so we don't endlessly repeat requests.
@@ -88,10 +103,53 @@ namespace pCampBot
88 public Dictionary<ulong, GridRegion> RegionsKnown { get; private set; } 103 public Dictionary<ulong, GridRegion> RegionsKnown { get; private set; }
89 104
90 /// <summary> 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>
91 /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data 146 /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data
92 /// </summary> 147 /// </summary>
93 public BotManager() 148 public BotManager()
94 { 149 {
150 InitBotSendAgentUpdates = true;
151 InitBotRequestObjectTextures = true;
152
95 LoginDelay = DefaultLoginDelay; 153 LoginDelay = DefaultLoginDelay;
96 154
97 Rng = new Random(Environment.TickCount); 155 Rng = new Random(Environment.TickCount);
@@ -117,30 +175,61 @@ namespace pCampBot
117 } 175 }
118 } 176 }
119 177
120 m_console.Commands.AddCommand("bot", false, "shutdown", 178 m_console.Commands.AddCommand(
121 "shutdown", 179 "Bots", false, "shutdown", "shutdown", "Shutdown bots and exit", HandleShutdown);
122 "Shutdown bots and exit", HandleShutdown); 180
123 181 m_console.Commands.AddCommand(
124 m_console.Commands.AddCommand("bot", false, "quit", 182 "Bots", false, "quit", "quit", "Shutdown bots and exit", HandleShutdown);
125 "quit", 183
126 "Shutdown bots and exit", 184 m_console.Commands.AddCommand(
127 HandleShutdown); 185 "Bots", false, "connect", "connect [<n>]", "Connect bots",
128 186 "If an <n> is given, then the first <n> disconnected bots by postfix number are connected.\n"
129 m_console.Commands.AddCommand("bot", false, "show regions", 187 + "If no <n> is given, then all currently disconnected bots are connected.",
130 "show regions", 188 HandleConnect);
131 "Show regions known to bots", 189
132 HandleShowRegions); 190 m_console.Commands.AddCommand(
133 191 "Bots", false, "disconnect", "disconnect [<n>]", "Disconnect bots",
134 m_console.Commands.AddCommand("bot", false, "show bots", 192 "Disconnecting bots will interupt any bot connection process, including connection on startup.\n"
135 "show bots", 193 + "If an <n> is given, then the last <n> connected bots by postfix number are disconnected.\n"
136 "Shows the status of all bots", 194 + "If no <n> is given, then all currently connected bots are disconnected.",
137 HandleShowStatus); 195 HandleDisconnect);
138 196
139// m_console.Commands.AddCommand("bot", false, "add bots", 197 m_console.Commands.AddCommand(
140// "add bots <number>", 198 "Bots", false, "add behaviour", "add behaviour <abbreviated-name> [<bot-number>]",
141// "Add more bots", HandleAddBots); 199 "Add a behaviour to a bot",
142 200 "If no bot number is specified then behaviour is added to all bots.\n"
143 m_lBot = new List<Bot>(); 201 + "Can be performed on connected or disconnected bots.",
202 HandleAddBehaviour);
203
204 m_console.Commands.AddCommand(
205 "Bots", 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 "Bots", false, "sit", "sit", "Sit all bots on the ground.",
213 HandleSit);
214
215 m_console.Commands.AddCommand(
216 "Bots", false, "stand", "stand", "Stand all bots.",
217 HandleStand);
218
219 m_console.Commands.AddCommand(
220 "Bots", false, "set bots", "set bots <key> <value>", "Set a setting for all bots.", HandleSetBots);
221
222 m_console.Commands.AddCommand(
223 "Bots", false, "show regions", "show regions", "Show regions known to bots", HandleShowRegions);
224
225 m_console.Commands.AddCommand(
226 "Bots", false, "show bots", "show bots", "Shows the status of all bots", HandleShowBotsStatus);
227
228 m_console.Commands.AddCommand(
229 "Bots", 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>();
144 } 233 }
145 234
146 /// <summary> 235 /// <summary>
@@ -148,74 +237,167 @@ namespace pCampBot
148 /// </summary> 237 /// </summary>
149 /// <param name="botcount">How many bots to start up</param> 238 /// <param name="botcount">How many bots to start up</param>
150 /// <param name="cs">The configuration for the bots to use</param> 239 /// <param name="cs">The configuration for the bots to use</param>
151 public void dobotStartup(int botcount, IConfig cs) 240 public void CreateBots(int botcount, IConfig startupConfig)
152 { 241 {
153 Config = cs; 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");
154 248
155 string firstName = cs.GetString("firstname"); 249 m_startUri = ParseInputStartLocationToUri(startupConfig.GetString("start", "last"));
156 string lastNameStem = cs.GetString("lastname");
157 string password = cs.GetString("password");
158 string loginUri = cs.GetString("loginuri");
159 250
160 HashSet<string> behaviourSwitches = new HashSet<string>();
161 Array.ForEach<string>( 251 Array.ForEach<string>(
162 cs.GetString("behaviours", "p").Split(new char[] { ',' }), b => behaviourSwitches.Add(b)); 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 }
163 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 {
164 MainConsole.Instance.OutputFormat( 318 MainConsole.Instance.OutputFormat(
165 "[BOT MANAGER]: Starting {0} bots connecting to {1}, named {2} {3}_<n>", 319 "[BOT MANAGER]: Starting {0} bots connecting to {1}, location {2}, named {3} {4}_<n>",
166 botcount, 320 botCount,
167 loginUri, 321 m_loginUri,
168 firstName, 322 m_startUri,
169 lastNameStem); 323 m_firstName,
324 m_lastNameStem);
170 325
171 MainConsole.Instance.OutputFormat("[BOT MANAGER]: Delay between logins is {0}ms", LoginDelay); 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);
172 329
173 for (int i = 0; i < botcount; i++) 330 int connectedBots = 0;
331
332 for (int i = 0; i < m_bots.Count; i++)
174 { 333 {
175 string lastName = string.Format("{0}_{1}", lastNameStem, i); 334 lock (m_bots)
176 335 {
177 // We must give each bot its own list of instantiated behaviours since they store state. 336 if (DisconnectingBots)
178 List<IBehaviour> behaviours = new List<IBehaviour>(); 337 {
179 338 MainConsole.Instance.Output(
180 // Hard-coded for now 339 "[BOT MANAGER]: Aborting bot connection due to user-initiated disconnection");
181 if (behaviourSwitches.Contains("p")) 340 break;
182 behaviours.Add(new PhysicsBehaviour()); 341 }
183 342
184 if (behaviourSwitches.Contains("g")) 343 if (m_bots[i].ConnectionState == ConnectionState.Disconnected)
185 behaviours.Add(new GrabbingBehaviour()); 344 {
186 345 m_bots[i].Connect();
187 if (behaviourSwitches.Contains("t")) 346 connectedBots++;
188 behaviours.Add(new TeleportBehaviour()); 347
189 348 if (connectedBots >= botCount)
190 if (behaviourSwitches.Contains("c")) 349 break;
191 behaviours.Add(new CrossBehaviour()); 350
192 351 // Stagger logins
193 StartBot(this, behaviours, firstName, lastName, password, loginUri); 352 Thread.Sleep(LoginDelay);
353 }
354 }
194 } 355 }
356
357 ConnectingBots = false;
195 } 358 }
196 359
197// /// <summary> 360 /// <summary>
198// /// Add additional bots (and threads) to our bot pool 361 /// Parses the command line start location to a start string/uri that the login mechanism will recognize.
199// /// </summary> 362 /// </summary>
200// /// <param name="botcount">How Many of them to add</param> 363 /// <returns>
201// public void addbots(int botcount) 364 /// The input start location to URI.
202// { 365 /// </returns>
203// int len = m_td.Length; 366 /// <param name='startLocation'>
204// Thread[] m_td2 = new Thread[len + botcount]; 367 /// Start location.
205// for (int i = 0; i < len; i++) 368 /// </param>
206// { 369 private string ParseInputStartLocationToUri(string startLocation)
207// m_td2[i] = m_td[i]; 370 {
208// } 371 if (startLocation == "home" || startLocation == "last")
209// m_td = m_td2; 372 return startLocation;
210// int newlen = len + botcount; 373
211// for (int i = len; i < newlen; i++) 374 string regionName;
212// { 375
213// startupBot(Config); 376 // Just a region name or only one (!) extra component. Like a viewer, we will stick 128/128/0 on the end
214// } 377 Vector3 startPos = new Vector3(128, 128, 0);
215// } 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 }
216 398
217 /// <summary> 399 /// <summary>
218 /// This starts up the bot and stores the thread for the bot in the thread array 400 /// This creates a bot but does not start it.
219 /// </summary> 401 /// </summary>
220 /// <param name="bm"></param> 402 /// <param name="bm"></param>
221 /// <param name="behaviours">Behaviours for this bot to perform.</param> 403 /// <param name="behaviours">Behaviours for this bot to perform.</param>
@@ -223,30 +405,25 @@ namespace pCampBot
223 /// <param name="lastName">Last name</param> 405 /// <param name="lastName">Last name</param>
224 /// <param name="password">Password</param> 406 /// <param name="password">Password</param>
225 /// <param name="loginUri">Login URI</param> 407 /// <param name="loginUri">Login URI</param>
226 public void StartBot( 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(
227 BotManager bm, List<IBehaviour> behaviours, 411 BotManager bm, List<IBehaviour> behaviours,
228 string firstName, string lastName, string password, string loginUri) 412 string firstName, string lastName, string password, string loginUri, string startLocation, string wearSetting)
229 { 413 {
230 MainConsole.Instance.OutputFormat( 414 MainConsole.Instance.OutputFormat(
231 "[BOT MANAGER]: Starting bot {0} {1}, behaviours are {2}", 415 "[BOT MANAGER]: Creating bot {0} {1}, behaviours are {2}",
232 firstName, lastName, string.Join(",", behaviours.ConvertAll<string>(b => b.Name).ToArray())); 416 firstName, lastName, string.Join(",", behaviours.ConvertAll<string>(b => b.Name).ToArray()));
233 417
234 Bot pb = new Bot(bm, behaviours, firstName, lastName, password, loginUri); 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;
235 422
236 pb.OnConnected += handlebotEvent; 423 pb.OnConnected += handlebotEvent;
237 pb.OnDisconnected += handlebotEvent; 424 pb.OnDisconnected += handlebotEvent;
238 425
239 lock (m_lBot) 426 m_bots.Add(pb);
240 m_lBot.Add(pb);
241
242 Thread pbThread = new Thread(pb.startup);
243 pbThread.Name = pb.Name;
244 pbThread.IsBackground = true;
245
246 pbThread.Start();
247
248 // Stagger logins
249 Thread.Sleep(LoginDelay);
250 } 427 }
251 428
252 /// <summary> 429 /// <summary>
@@ -259,52 +436,270 @@ namespace pCampBot
259 switch (eventt) 436 switch (eventt)
260 { 437 {
261 case EventType.CONNECTED: 438 case EventType.CONNECTED:
439 {
262 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Connected"); 440 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Connected");
263 break; 441 break;
442 }
443
264 case EventType.DISCONNECTED: 444 case EventType.DISCONNECTED:
445 {
265 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Disconnected"); 446 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Disconnected");
447 break;
448 }
449 }
450 }
266 451
267 lock (m_lBot) 452 /// <summary>
268 { 453 /// Standard CreateConsole routine
269 if (m_lBot.TrueForAll(b => b.ConnectionState == ConnectionState.Disconnected)) 454 /// </summary>
270 Environment.Exit(0); 455 /// <returns></returns>
456 protected CommandConsole CreateConsole()
457 {
458 return new LocalConsole("pCampbot");
459 }
271 460
272 break; 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);
273 } 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);
274 } 601 }
275 } 602 }
276 603
277 /// <summary> 604 private void HandleDisconnect(string module, string[] cmd)
278 /// Shut down all bots
279 /// </summary>
280 /// <remarks>
281 /// We launch each shutdown on its own thread so that a slow shutting down bot doesn't hold up all the others.
282 /// </remarks>
283 public void doBotShutdown()
284 { 605 {
285 lock (m_lBot) 606 lock (m_bots)
286 { 607 {
287 foreach (Bot bot in m_lBot) 608 int botsToDisconnect;
609 int connectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Connected);
610
611 if (cmd.Length == 1)
288 { 612 {
289 Bot thisBot = bot; 613 botsToDisconnect = connectedBots;
290 Util.FireAndForget(o => thisBot.shutdown());
291 } 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;
292 } 644 }
293 } 645 }
294 646
295 /// <summary> 647 private void HandleSit(string module, string[] cmd)
296 /// Standard CreateConsole routine
297 /// </summary>
298 /// <returns></returns>
299 protected CommandConsole CreateConsole()
300 { 648 {
301 return new LocalConsole("pCampbot"); 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 }
302 } 661 }
303 662
304 private void HandleShutdown(string module, string[] cmd) 663 private void HandleShutdown(string module, string[] cmd)
305 { 664 {
306 m_log.Info("[BOTMANAGER]: Shutting down bots"); 665 lock (m_bots)
307 doBotShutdown(); 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 }
308 } 703 }
309 704
310 private void HandleShowRegions(string module, string[] cmd) 705 private void HandleShowRegions(string module, string[] cmd)
@@ -322,43 +717,114 @@ namespace pCampBot
322 } 717 }
323 } 718 }
324 719
325 private void HandleShowStatus(string module, string[] cmd) 720 private void HandleShowBotsStatus(string module, string[] cmd)
326 { 721 {
327 string outputFormat = "{0,-30} {1, -30} {2,-14}"; 722 ConsoleDisplayTable cdt = new ConsoleDisplayTable();
328 MainConsole.Instance.OutputFormat(outputFormat, "Name", "Region", "Status"); 723 cdt.AddColumn("Name", 24);
329 724 cdt.AddColumn("Region", 24);
330 lock (m_lBot) 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)
331 { 734 {
332 foreach (Bot pb in m_lBot) 735 foreach (Bot bot in m_bots)
333 { 736 {
334 Simulator currentSim = pb.Client.Network.CurrentSim; 737 Simulator currentSim = bot.Client.Network.CurrentSim;
335 738 totals[bot.ConnectionState]++;
336 MainConsole.Instance.OutputFormat( 739
337 outputFormat, 740 cdt.AddRow(
338 pb.Name, currentSim != null ? currentSim.Name : "(none)", pb.ConnectionState); 741 bot.Name,
742 currentSim != null ? currentSim.Name : "(none)",
743 bot.ConnectionState,
744 bot.SimulatorsCount,
745 string.Join(",", bot.Behaviours.Keys.ToArray()));
339 } 746 }
340 } 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());
341 } 757 }
342 758
343 /* 759 private void HandleShowBotStatus(string module, string[] cmd)
344 private void HandleQuit(string module, string[] cmd)
345 { 760 {
346 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"); 761 if (cmd.Length != 3)
347 Environment.Exit(0); 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);
348 } 827 }
349 */
350//
351// private void HandleAddBots(string module, string[] cmd)
352// {
353// int newbots = 0;
354//
355// if (cmd.Length > 2)
356// {
357// Int32.TryParse(cmd[2], out newbots);
358// }
359// if (newbots > 0)
360// addbots(newbots);
361// }
362 828
363 internal void Grid_GridRegion(object o, GridRegionEventArgs args) 829 internal void Grid_GridRegion(object o, GridRegionEventArgs args)
364 { 830 {
@@ -379,4 +845,4 @@ namespace pCampBot
379 } 845 }
380 } 846 }
381 } 847 }
382} 848} \ No newline at end of file