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.cs903
1 files changed, 752 insertions, 151 deletions
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;
38using Nini.Config; 38using Nini.Config;
39using OpenSim.Framework; 39using OpenSim.Framework;
40using OpenSim.Framework.Console; 40using OpenSim.Framework.Console;
41using OpenSim.Framework.Monitoring;
41using pCampBot.Interfaces; 42using pCampBot.Interfaces;
42 43
43namespace pCampBot 44namespace pCampBot
44{ 45{
46 public enum BotManagerBotConnectingState
47 {
48 Initializing,
49 Ready,
50 Connecting,
51 Disconnecting
52 }
53
45 /// <summary> 54 /// <summary>
46 /// Thread/Bot manager for the application 55 /// Thread/Bot manager for the application
47 /// </summary> 56 /// </summary>
@@ -52,6 +61,16 @@ namespace pCampBot
52 public const int DefaultLoginDelay = 5000; 61 public const int DefaultLoginDelay = 5000;
53 62
54 /// <summary> 63 /// <summary>
64 /// Is pCampbot ready to connect or currently in the process of connecting or disconnecting bots?
65 /// </summary>
66 public BotManagerBotConnectingState BotConnectingState { get; private set; }
67
68 /// <summary>
69 /// Used to control locking as we can't lock an enum.
70 /// </summary>
71 private object BotConnectingStateChangeObject = new object();
72
73 /// <summary>
55 /// Delay between logins of multiple bots. 74 /// Delay between logins of multiple bots.
56 /// </summary> 75 /// </summary>
57 /// <remarks>TODO: This value needs to be configurable by a command line argument.</remarks> 76 /// <remarks>TODO: This value needs to be configurable by a command line argument.</remarks>
@@ -63,19 +82,24 @@ namespace pCampBot
63 protected CommandConsole m_console; 82 protected CommandConsole m_console;
64 83
65 /// <summary> 84 /// <summary>
66 /// Created bots, whether active or inactive. 85 /// Controls whether bots start out sending agent updates on connection.
67 /// </summary> 86 /// </summary>
68 protected List<Bot> m_lBot; 87 public bool InitBotSendAgentUpdates { get; set; }
69 88
70 /// <summary> 89 /// <summary>
71 /// Random number generator. 90 /// Controls whether bots request textures for the object information they receive
72 /// </summary> 91 /// </summary>
73 public Random Rng { get; private set; } 92 public bool InitBotRequestObjectTextures { get; set; }
74 93
75 /// <summary> 94 /// <summary>
76 /// Overall configuration. 95 /// Created bots, whether active or inactive.
77 /// </summary> 96 /// </summary>
78 public IConfig Config { get; private set; } 97 protected List<Bot> m_bots;
98
99 /// <summary>
100 /// Random number generator.
101 /// </summary>
102 public Random Rng { get; private set; }
79 103
80 /// <summary> 104 /// <summary>
81 /// Track the assets we have and have not received so we don't endlessly repeat requests. 105 /// Track the assets we have and have not received so we don't endlessly repeat requests.
@@ -88,10 +112,68 @@ namespace pCampBot
88 public Dictionary<ulong, GridRegion> RegionsKnown { get; private set; } 112 public Dictionary<ulong, GridRegion> RegionsKnown { get; private set; }
89 113
90 /// <summary> 114 /// <summary>
115 /// First name for bots
116 /// </summary>
117 private string m_firstName;
118
119 /// <summary>
120 /// Last name stem for bots
121 /// </summary>
122 private string m_lastNameStem;
123
124 /// <summary>
125 /// Password for bots
126 /// </summary>
127 private string m_password;
128
129 /// <summary>
130 /// Login URI for bots.
131 /// </summary>
132 private string m_loginUri;
133
134 /// <summary>
135 /// Start location for bots.
136 /// </summary>
137 private string m_startUri;
138
139 /// <summary>
140 /// Postfix bot number at which bot sequence starts.
141 /// </summary>
142 private int m_fromBotNumber;
143
144 /// <summary>
145 /// Wear setting for bots.
146 /// </summary>
147 private string m_wearSetting;
148
149 /// <summary>
150 /// Behaviour switches for bots.
151 /// </summary>
152 private HashSet<string> m_defaultBehaviourSwitches = new HashSet<string>();
153
154 /// <summary>
155 /// Collects general information on this server (which reveals this to be a misnamed class).
156 /// </summary>
157 private ServerStatsCollector m_serverStatsCollector;
158
159 /// <summary>
91 /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data 160 /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data
92 /// </summary> 161 /// </summary>
93 public BotManager() 162 public BotManager()
94 { 163 {
164 // We set this to avoid issues with bots running out of HTTP connections if many are run from a single machine
165 // to multiple regions.
166 Settings.MAX_HTTP_CONNECTIONS = int.MaxValue;
167
168// System.Threading.ThreadPool.SetMaxThreads(600, 240);
169//
170// int workerThreads, iocpThreads;
171// System.Threading.ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads);
172// Console.WriteLine("ThreadPool.GetMaxThreads {0} {1}", workerThreads, iocpThreads);
173
174 InitBotSendAgentUpdates = true;
175 InitBotRequestObjectTextures = true;
176
95 LoginDelay = DefaultLoginDelay; 177 LoginDelay = DefaultLoginDelay;
96 178
97 Rng = new Random(Environment.TickCount); 179 Rng = new Random(Environment.TickCount);
@@ -117,30 +199,84 @@ namespace pCampBot
117 } 199 }
118 } 200 }
119 201
120 m_console.Commands.AddCommand("bot", false, "shutdown", 202 m_console.Commands.AddCommand(
121 "shutdown", 203 "Bots", false, "shutdown", "shutdown", "Shutdown bots and exit", HandleShutdown);
122 "Shutdown bots and exit", HandleShutdown); 204
123 205 m_console.Commands.AddCommand(
124 m_console.Commands.AddCommand("bot", false, "quit", 206 "Bots", false, "quit", "quit", "Shutdown bots and exit", HandleShutdown);
125 "quit", 207
126 "Shutdown bots and exit", 208 m_console.Commands.AddCommand(
127 HandleShutdown); 209 "Bots", false, "connect", "connect [<n>]", "Connect bots",
128 210 "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", 211 + "If no <n> is given, then all currently disconnected bots are connected.",
130 "show regions", 212 HandleConnect);
131 "Show regions known to bots", 213
132 HandleShowRegions); 214 m_console.Commands.AddCommand(
133 215 "Bots", false, "disconnect", "disconnect [<n>]", "Disconnect bots",
134 m_console.Commands.AddCommand("bot", false, "show bots", 216 "Disconnecting bots will interupt any bot connection process, including connection on startup.\n"
135 "show bots", 217 + "If an <n> is given, then the last <n> connected bots by postfix number are disconnected.\n"
136 "Shows the status of all bots", 218 + "If no <n> is given, then all currently connected bots are disconnected.",
137 HandleShowStatus); 219 HandleDisconnect);
138 220
139// m_console.Commands.AddCommand("bot", false, "add bots", 221 m_console.Commands.AddCommand(
140// "add bots <number>", 222 "Bots", false, "add behaviour", "add behaviour <abbreviated-name> [<bot-number>]",
141// "Add more bots", HandleAddBots); 223 "Add a behaviour to a bot",
142 224 "If no bot number is specified then behaviour is added to all bots.\n"
143 m_lBot = new List<Bot>(); 225 + "Can be performed on connected or disconnected bots.",
226 HandleAddBehaviour);
227
228 m_console.Commands.AddCommand(
229 "Bots", false, "remove behaviour", "remove behaviour <abbreviated-name> [<bot-number>]",
230 "Remove a behaviour from a bot",
231 "If no bot number is specified then behaviour is added to all bots.\n"
232 + "Can be performed on connected or disconnected bots.",
233 HandleRemoveBehaviour);
234
235 m_console.Commands.AddCommand(
236 "Bots", false, "sit", "sit", "Sit all bots on the ground.",
237 HandleSit);
238
239 m_console.Commands.AddCommand(
240 "Bots", false, "stand", "stand", "Stand all bots.",
241 HandleStand);
242
243 m_console.Commands.AddCommand(
244 "Bots", false, "set bots", "set bots <key> <value>", "Set a setting for all bots.", HandleSetBots);
245
246 m_console.Commands.AddCommand(
247 "Bots", false, "show regions", "show regions", "Show regions known to bots", HandleShowRegions);
248
249 m_console.Commands.AddCommand(
250 "Bots", false, "show bots", "show bots", "Shows the status of all bots.", HandleShowBotsStatus);
251
252 m_console.Commands.AddCommand(
253 "Bots", false, "show bot", "show bot <bot-number>",
254 "Shows the detailed status and settings of a particular bot.", HandleShowBotStatus);
255
256 m_console.Commands.AddCommand(
257 "Debug",
258 false,
259 "debug lludp packet",
260 "debug lludp packet <level> <avatar-first-name> <avatar-last-name>",
261 "Turn on received packet logging.",
262 "If level > 0 then all received packets that are not duplicates are logged.\n"
263 + "If level <= 0 then no received packets are logged.",
264 HandleDebugLludpPacketCommand);
265
266 m_console.Commands.AddCommand(
267 "Bots", false, "show status", "show status", "Shows pCampbot status.", HandleShowStatus);
268
269 m_bots = new List<Bot>();
270
271 Watchdog.Enabled = true;
272 StatsManager.RegisterConsoleCommands(m_console);
273
274 m_serverStatsCollector = new ServerStatsCollector();
275 m_serverStatsCollector.Initialise(null);
276 m_serverStatsCollector.Enabled = true;
277 m_serverStatsCollector.Start();
278
279 BotConnectingState = BotManagerBotConnectingState.Ready;
144 } 280 }
145 281
146 /// <summary> 282 /// <summary>
@@ -148,74 +284,195 @@ namespace pCampBot
148 /// </summary> 284 /// </summary>
149 /// <param name="botcount">How many bots to start up</param> 285 /// <param name="botcount">How many bots to start up</param>
150 /// <param name="cs">The configuration for the bots to use</param> 286 /// <param name="cs">The configuration for the bots to use</param>
151 public void dobotStartup(int botcount, IConfig cs) 287 public void CreateBots(int botcount, IConfig startupConfig)
152 { 288 {
153 Config = cs; 289 m_firstName = startupConfig.GetString("firstname");
290 m_lastNameStem = startupConfig.GetString("lastname");
291 m_password = startupConfig.GetString("password");
292 m_loginUri = startupConfig.GetString("loginuri");
293 m_fromBotNumber = startupConfig.GetInt("from", 0);
294 m_wearSetting = startupConfig.GetString("wear", "no");
154 295
155 string firstName = cs.GetString("firstname"); 296 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 297
160 HashSet<string> behaviourSwitches = new HashSet<string>();
161 Array.ForEach<string>( 298 Array.ForEach<string>(
162 cs.GetString("behaviours", "p").Split(new char[] { ',' }), b => behaviourSwitches.Add(b)); 299 startupConfig.GetString("behaviours", "p").Split(new char[] { ',' }), b => m_defaultBehaviourSwitches.Add(b));
163 300
164 MainConsole.Instance.OutputFormat( 301 for (int i = 0; i < botcount; i++)
165 "[BOT MANAGER]: Starting {0} bots connecting to {1}, named {2} {3}_<n>", 302 {
166 botcount, 303 lock (m_bots)
167 loginUri, 304 {
168 firstName, 305 string lastName = string.Format("{0}_{1}", m_lastNameStem, i + m_fromBotNumber);
169 lastNameStem); 306
307 CreateBot(
308 this,
309 CreateBehavioursFromAbbreviatedNames(m_defaultBehaviourSwitches),
310 m_firstName, lastName, m_password, m_loginUri, m_startUri, m_wearSetting);
311 }
312 }
313 }
170 314
171 MainConsole.Instance.OutputFormat("[BOT MANAGER]: Delay between logins is {0}ms", LoginDelay); 315 private List<IBehaviour> CreateBehavioursFromAbbreviatedNames(HashSet<string> abbreviatedNames)
316 {
317 // We must give each bot its own list of instantiated behaviours since they store state.
318 List<IBehaviour> behaviours = new List<IBehaviour>();
172 319
173 for (int i = 0; i < botcount; i++) 320 // Hard-coded for now
321 foreach (string abName in abbreviatedNames)
174 { 322 {
175 string lastName = string.Format("{0}_{1}", lastNameStem, i); 323 IBehaviour newBehaviour = null;
176 324
177 // We must give each bot its own list of instantiated behaviours since they store state. 325 if (abName == "c")
178 List<IBehaviour> behaviours = new List<IBehaviour>(); 326 newBehaviour = new CrossBehaviour();
179 327
180 // Hard-coded for now 328 if (abName == "g")
181 if (behaviourSwitches.Contains("p")) 329 newBehaviour = new GrabbingBehaviour();
182 behaviours.Add(new PhysicsBehaviour()); 330
183 331 if (abName == "n")
184 if (behaviourSwitches.Contains("g")) 332 newBehaviour = new NoneBehaviour();
185 behaviours.Add(new GrabbingBehaviour()); 333
186 334 if (abName == "p")
187 if (behaviourSwitches.Contains("t")) 335 newBehaviour = new PhysicsBehaviour();
188 behaviours.Add(new TeleportBehaviour()); 336
189 337 if (abName == "t")
190 if (behaviourSwitches.Contains("c")) 338 newBehaviour = new TeleportBehaviour();
191 behaviours.Add(new CrossBehaviour()); 339
192 340 if (abName == "tw")
193 StartBot(this, behaviours, firstName, lastName, password, loginUri); 341 newBehaviour = new TwitchyBehaviour();
342
343 if (abName == "ph2")
344 newBehaviour = new PhysicsBehaviour2();
345
346 if (abName == "inv")
347 newBehaviour = new InventoryDownloadBehaviour();
348
349 if (newBehaviour != null)
350 {
351 behaviours.Add(newBehaviour);
352 }
353 else
354 {
355 MainConsole.Instance.OutputFormat("No behaviour with abbreviated name {0} found", abName);
356 }
194 } 357 }
358
359 return behaviours;
195 } 360 }
196 361
197// /// <summary> 362 public void ConnectBots(int botcount)
198// /// Add additional bots (and threads) to our bot pool 363 {
199// /// </summary> 364 lock (BotConnectingStateChangeObject)
200// /// <param name="botcount">How Many of them to add</param> 365 {
201// public void addbots(int botcount) 366 if (BotConnectingState != BotManagerBotConnectingState.Ready)
202// { 367 {
203// int len = m_td.Length; 368 MainConsole.Instance.OutputFormat(
204// Thread[] m_td2 = new Thread[len + botcount]; 369 "Bot connecting status is {0}. Please wait for previous process to complete.", BotConnectingState);
205// for (int i = 0; i < len; i++) 370 return;
206// { 371 }
207// m_td2[i] = m_td[i]; 372
208// } 373 BotConnectingState = BotManagerBotConnectingState.Connecting;
209// m_td = m_td2; 374 }
210// int newlen = len + botcount; 375
211// for (int i = len; i < newlen; i++) 376 Thread connectBotThread = new Thread(o => ConnectBotsInternal(botcount));
212// { 377
213// startupBot(Config); 378 connectBotThread.Name = "Bots connection thread";
214// } 379 connectBotThread.Start();
215// } 380 }
381
382 private void ConnectBotsInternal(int botCount)
383 {
384 m_log.InfoFormat(
385 "[BOT MANAGER]: Starting {0} bots connecting to {1}, location {2}, named {3} {4}_<n>",
386 botCount,
387 m_loginUri,
388 m_startUri,
389 m_firstName,
390 m_lastNameStem);
391
392 m_log.DebugFormat("[BOT MANAGER]: Delay between logins is {0}ms", LoginDelay);
393 m_log.DebugFormat("[BOT MANAGER]: BotsSendAgentUpdates is {0}", InitBotSendAgentUpdates);
394 m_log.DebugFormat("[BOT MANAGER]: InitBotRequestObjectTextures is {0}", InitBotRequestObjectTextures);
395
396 List<Bot> botsToConnect = new List<Bot>();
397
398 lock (m_bots)
399 {
400 foreach (Bot bot in m_bots)
401 {
402 if (bot.ConnectionState == ConnectionState.Disconnected)
403 botsToConnect.Add(bot);
404
405 if (botsToConnect.Count >= botCount)
406 break;
407 }
408 }
409
410 foreach (Bot bot in botsToConnect)
411 {
412 lock (BotConnectingStateChangeObject)
413 {
414 if (BotConnectingState != BotManagerBotConnectingState.Connecting)
415 {
416 MainConsole.Instance.Output(
417 "[BOT MANAGER]: Aborting bot connection due to user-initiated disconnection");
418 return;
419 }
420 }
421
422 bot.Connect();
423
424 // Stagger logins
425 Thread.Sleep(LoginDelay);
426 }
427
428 lock (BotConnectingStateChangeObject)
429 {
430 if (BotConnectingState == BotManagerBotConnectingState.Connecting)
431 BotConnectingState = BotManagerBotConnectingState.Ready;
432 }
433 }
434
435 /// <summary>
436 /// Parses the command line start location to a start string/uri that the login mechanism will recognize.
437 /// </summary>
438 /// <returns>
439 /// The input start location to URI.
440 /// </returns>
441 /// <param name='startLocation'>
442 /// Start location.
443 /// </param>
444 private string ParseInputStartLocationToUri(string startLocation)
445 {
446 if (startLocation == "home" || startLocation == "last")
447 return startLocation;
448
449 string regionName;
450
451 // Just a region name or only one (!) extra component. Like a viewer, we will stick 128/128/0 on the end
452 Vector3 startPos = new Vector3(128, 128, 0);
453
454 string[] startLocationComponents = startLocation.Split('/');
455
456 regionName = startLocationComponents[0];
457
458 if (startLocationComponents.Length >= 2)
459 {
460 float.TryParse(startLocationComponents[1], out startPos.X);
461
462 if (startLocationComponents.Length >= 3)
463 {
464 float.TryParse(startLocationComponents[2], out startPos.Y);
465
466 if (startLocationComponents.Length >= 4)
467 float.TryParse(startLocationComponents[3], out startPos.Z);
468 }
469 }
470
471 return string.Format("uri:{0}&{1}&{2}&{3}", regionName, startPos.X, startPos.Y, startPos.Z);
472 }
216 473
217 /// <summary> 474 /// <summary>
218 /// This starts up the bot and stores the thread for the bot in the thread array 475 /// This creates a bot but does not start it.
219 /// </summary> 476 /// </summary>
220 /// <param name="bm"></param> 477 /// <param name="bm"></param>
221 /// <param name="behaviours">Behaviours for this bot to perform.</param> 478 /// <param name="behaviours">Behaviours for this bot to perform.</param>
@@ -223,30 +480,25 @@ namespace pCampBot
223 /// <param name="lastName">Last name</param> 480 /// <param name="lastName">Last name</param>
224 /// <param name="password">Password</param> 481 /// <param name="password">Password</param>
225 /// <param name="loginUri">Login URI</param> 482 /// <param name="loginUri">Login URI</param>
226 public void StartBot( 483 /// <param name="startLocation">Location to start the bot. Can be "last", "home" or a specific sim name.</param>
484 /// <param name="wearSetting"></param>
485 public void CreateBot(
227 BotManager bm, List<IBehaviour> behaviours, 486 BotManager bm, List<IBehaviour> behaviours,
228 string firstName, string lastName, string password, string loginUri) 487 string firstName, string lastName, string password, string loginUri, string startLocation, string wearSetting)
229 { 488 {
230 MainConsole.Instance.OutputFormat( 489 MainConsole.Instance.OutputFormat(
231 "[BOT MANAGER]: Starting bot {0} {1}, behaviours are {2}", 490 "[BOT MANAGER]: Creating bot {0} {1}, behaviours are {2}",
232 firstName, lastName, string.Join(",", behaviours.ConvertAll<string>(b => b.Name).ToArray())); 491 firstName, lastName, string.Join(",", behaviours.ConvertAll<string>(b => b.Name).ToArray()));
233 492
234 Bot pb = new Bot(bm, behaviours, firstName, lastName, password, loginUri); 493 Bot pb = new Bot(bm, behaviours, firstName, lastName, password, startLocation, loginUri);
494 pb.wear = wearSetting;
495 pb.Client.Settings.SEND_AGENT_UPDATES = InitBotSendAgentUpdates;
496 pb.RequestObjectTextures = InitBotRequestObjectTextures;
235 497
236 pb.OnConnected += handlebotEvent; 498 pb.OnConnected += handlebotEvent;
237 pb.OnDisconnected += handlebotEvent; 499 pb.OnDisconnected += handlebotEvent;
238 500
239 lock (m_lBot) 501 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 } 502 }
251 503
252 /// <summary> 504 /// <summary>
@@ -259,52 +511,322 @@ namespace pCampBot
259 switch (eventt) 511 switch (eventt)
260 { 512 {
261 case EventType.CONNECTED: 513 case EventType.CONNECTED:
514 {
262 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Connected"); 515 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Connected");
263 break; 516 break;
517 }
518
264 case EventType.DISCONNECTED: 519 case EventType.DISCONNECTED:
520 {
265 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Disconnected"); 521 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Disconnected");
522 break;
523 }
524 }
525 }
266 526
267 lock (m_lBot) 527 /// <summary>
268 { 528 /// Standard CreateConsole routine
269 if (m_lBot.TrueForAll(b => b.ConnectionState == ConnectionState.Disconnected)) 529 /// </summary>
270 Environment.Exit(0); 530 /// <returns></returns>
531 protected CommandConsole CreateConsole()
532 {
533 return new LocalConsole("pCampbot");
534 }
271 535
272 break; 536 private void HandleConnect(string module, string[] cmd)
537 {
538 lock (m_bots)
539 {
540 int botsToConnect;
541 int disconnectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Disconnected);
542
543 if (cmd.Length == 1)
544 {
545 botsToConnect = disconnectedBots;
546 }
547 else
548 {
549 if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[1], out botsToConnect))
550 return;
551
552 botsToConnect = Math.Min(botsToConnect, disconnectedBots);
553 }
554
555 MainConsole.Instance.OutputFormat("Connecting {0} bots", botsToConnect);
556
557 ConnectBots(botsToConnect);
558 }
559 }
560
561 private void HandleAddBehaviour(string module, string[] cmd)
562 {
563 if (cmd.Length < 3 || cmd.Length > 4)
564 {
565 MainConsole.Instance.OutputFormat("Usage: add behaviour <abbreviated-behaviour> [<bot-number>]");
566 return;
567 }
568
569 string rawBehaviours = cmd[2];
570
571 List<Bot> botsToEffect = new List<Bot>();
572
573 if (cmd.Length == 3)
574 {
575 lock (m_bots)
576 botsToEffect.AddRange(m_bots);
577 }
578 else
579 {
580 int botNumber;
581 if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber))
582 return;
583
584 Bot bot = GetBotFromNumber(botNumber);
585
586 if (bot == null)
587 {
588 MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber);
589 return;
590 }
591
592 botsToEffect.Add(bot);
593 }
594
595
596 HashSet<string> rawAbbreviatedSwitchesToAdd = new HashSet<string>();
597 Array.ForEach<string>(rawBehaviours.Split(new char[] { ',' }), b => rawAbbreviatedSwitchesToAdd.Add(b));
598
599 foreach (Bot bot in botsToEffect)
600 {
601 List<IBehaviour> behavioursAdded = new List<IBehaviour>();
602
603 foreach (IBehaviour behaviour in CreateBehavioursFromAbbreviatedNames(rawAbbreviatedSwitchesToAdd))
604 {
605 if (bot.AddBehaviour(behaviour))
606 behavioursAdded.Add(behaviour);
607 }
608
609 MainConsole.Instance.OutputFormat(
610 "Added behaviours {0} to bot {1}",
611 string.Join(", ", behavioursAdded.ConvertAll<string>(b => b.Name).ToArray()), bot.Name);
612 }
613 }
614
615 private void HandleRemoveBehaviour(string module, string[] cmd)
616 {
617 if (cmd.Length < 3 || cmd.Length > 4)
618 {
619 MainConsole.Instance.OutputFormat("Usage: remove behaviour <abbreviated-behaviour> [<bot-number>]");
620 return;
621 }
622
623 string rawBehaviours = cmd[2];
624
625 List<Bot> botsToEffect = new List<Bot>();
626
627 if (cmd.Length == 3)
628 {
629 lock (m_bots)
630 botsToEffect.AddRange(m_bots);
631 }
632 else
633 {
634 int botNumber;
635 if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber))
636 return;
637
638 Bot bot = GetBotFromNumber(botNumber);
639
640 if (bot == null)
641 {
642 MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber);
643 return;
644 }
645
646 botsToEffect.Add(bot);
647 }
648
649 HashSet<string> abbreviatedBehavioursToRemove = new HashSet<string>();
650 Array.ForEach<string>(rawBehaviours.Split(new char[] { ',' }), b => abbreviatedBehavioursToRemove.Add(b));
651
652 foreach (Bot bot in botsToEffect)
653 {
654 List<IBehaviour> behavioursRemoved = new List<IBehaviour>();
655
656 foreach (string b in abbreviatedBehavioursToRemove)
657 {
658 IBehaviour behaviour;
659
660 if (bot.TryGetBehaviour(b, out behaviour))
661 {
662 bot.RemoveBehaviour(b);
663 behavioursRemoved.Add(behaviour);
273 } 664 }
665 }
666
667 MainConsole.Instance.OutputFormat(
668 "Removed behaviours {0} from bot {1}",
669 string.Join(", ", behavioursRemoved.ConvertAll<string>(b => b.Name).ToArray()), bot.Name);
274 } 670 }
275 } 671 }
276 672
277 /// <summary> 673 private void HandleDisconnect(string module, string[] cmd)
278 /// Shut down all bots 674 {
279 /// </summary> 675 List<Bot> connectedBots;
280 /// <remarks> 676 int botsToDisconnectCount;
281 /// We launch each shutdown on its own thread so that a slow shutting down bot doesn't hold up all the others. 677
282 /// </remarks> 678 lock (m_bots)
283 public void doBotShutdown() 679 connectedBots = m_bots.FindAll(b => b.ConnectionState == ConnectionState.Connected);
680
681 if (cmd.Length == 1)
682 {
683 botsToDisconnectCount = connectedBots.Count;
684 }
685 else
686 {
687 if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[1], out botsToDisconnectCount))
688 return;
689
690 botsToDisconnectCount = Math.Min(botsToDisconnectCount, connectedBots.Count);
691 }
692
693 lock (BotConnectingStateChangeObject)
694 BotConnectingState = BotManagerBotConnectingState.Disconnecting;
695
696 Thread disconnectBotThread = new Thread(o => DisconnectBotsInternal(connectedBots, botsToDisconnectCount));
697
698 disconnectBotThread.Name = "Bots disconnection thread";
699 disconnectBotThread.Start();
700 }
701
702 private void DisconnectBotsInternal(List<Bot> connectedBots, int disconnectCount)
284 { 703 {
285 lock (m_lBot) 704 MainConsole.Instance.OutputFormat("Disconnecting {0} bots", disconnectCount);
705
706 int disconnectedBots = 0;
707
708 for (int i = connectedBots.Count - 1; i >= 0; i--)
286 { 709 {
287 foreach (Bot bot in m_lBot) 710 if (disconnectedBots >= disconnectCount)
711 break;
712
713 Bot thisBot = connectedBots[i];
714
715 if (thisBot.ConnectionState == ConnectionState.Connected)
288 { 716 {
289 Bot thisBot = bot; 717 ThreadPool.QueueUserWorkItem(o => thisBot.Disconnect());
290 Util.FireAndForget(o => thisBot.shutdown()); 718 disconnectedBots++;
291 } 719 }
292 } 720 }
721
722 lock (BotConnectingStateChangeObject)
723 BotConnectingState = BotManagerBotConnectingState.Ready;
293 } 724 }
294 725
295 /// <summary> 726 private void HandleSit(string module, string[] cmd)
296 /// Standard CreateConsole routine
297 /// </summary>
298 /// <returns></returns>
299 protected CommandConsole CreateConsole()
300 { 727 {
301 return new LocalConsole("pCampbot"); 728 lock (m_bots)
729 {
730 foreach (Bot bot in m_bots)
731 {
732 if (bot.ConnectionState == ConnectionState.Connected)
733 {
734 MainConsole.Instance.OutputFormat("Sitting bot {0} on ground.", bot.Name);
735 bot.SitOnGround();
736 }
737 }
738 }
739 }
740
741 private void HandleStand(string module, string[] cmd)
742 {
743 lock (m_bots)
744 {
745 foreach (Bot bot in m_bots)
746 {
747 if (bot.ConnectionState == ConnectionState.Connected)
748 {
749 MainConsole.Instance.OutputFormat("Standing bot {0} from ground.", bot.Name);
750 bot.Stand();
751 }
752 }
753 }
302 } 754 }
303 755
304 private void HandleShutdown(string module, string[] cmd) 756 private void HandleShutdown(string module, string[] cmd)
305 { 757 {
306 m_log.Info("[BOTMANAGER]: Shutting down bots"); 758 lock (m_bots)
307 doBotShutdown(); 759 {
760 int connectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Connected);
761
762 if (connectedBots > 0)
763 {
764 MainConsole.Instance.OutputFormat("Please disconnect {0} connected bots first", connectedBots);
765 return;
766 }
767 }
768
769 MainConsole.Instance.Output("Shutting down");
770
771 m_serverStatsCollector.Close();
772
773 Environment.Exit(0);
774 }
775
776 private void HandleSetBots(string module, string[] cmd)
777 {
778 string key = cmd[2];
779 string rawValue = cmd[3];
780
781 if (key == "SEND_AGENT_UPDATES")
782 {
783 bool newSendAgentUpdatesSetting;
784
785 if (!ConsoleUtil.TryParseConsoleBool(MainConsole.Instance, rawValue, out newSendAgentUpdatesSetting))
786 return;
787
788 MainConsole.Instance.OutputFormat(
789 "Setting SEND_AGENT_UPDATES to {0} for all bots", newSendAgentUpdatesSetting);
790
791 lock (m_bots)
792 m_bots.ForEach(b => b.Client.Settings.SEND_AGENT_UPDATES = newSendAgentUpdatesSetting);
793 }
794 else
795 {
796 MainConsole.Instance.Output("Error: Only setting currently available is SEND_AGENT_UPDATES");
797 }
798 }
799
800 private void HandleDebugLludpPacketCommand(string module, string[] args)
801 {
802 if (args.Length != 6)
803 {
804 MainConsole.Instance.OutputFormat("Usage: debug lludp packet <level> <bot-first-name> <bot-last-name>");
805 return;
806 }
807
808 int level;
809
810 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[3], out level))
811 return;
812
813 string botFirstName = args[4];
814 string botLastName = args[5];
815
816 Bot bot;
817
818 lock (m_bots)
819 bot = m_bots.FirstOrDefault(b => b.FirstName == botFirstName && b.LastName == botLastName);
820
821 if (bot == null)
822 {
823 MainConsole.Instance.OutputFormat("No bot named {0} {1}", botFirstName, botLastName);
824 return;
825 }
826
827 bot.PacketDebugLevel = level;
828
829 MainConsole.Instance.OutputFormat("Set debug level of {0} to {1}", bot.Name, bot.PacketDebugLevel);
308 } 830 }
309 831
310 private void HandleShowRegions(string module, string[] cmd) 832 private void HandleShowRegions(string module, string[] cmd)
@@ -324,41 +846,120 @@ namespace pCampBot
324 846
325 private void HandleShowStatus(string module, string[] cmd) 847 private void HandleShowStatus(string module, string[] cmd)
326 { 848 {
327 string outputFormat = "{0,-30} {1, -30} {2,-14}"; 849 ConsoleDisplayList cdl = new ConsoleDisplayList();
328 MainConsole.Instance.OutputFormat(outputFormat, "Name", "Region", "Status"); 850 cdl.AddRow("Bot connecting state", BotConnectingState);
329 851
330 lock (m_lBot) 852 MainConsole.Instance.Output(cdl.ToString());
853 }
854
855 private void HandleShowBotsStatus(string module, string[] cmd)
856 {
857 ConsoleDisplayTable cdt = new ConsoleDisplayTable();
858 cdt.AddColumn("Name", 24);
859 cdt.AddColumn("Region", 24);
860 cdt.AddColumn("Status", 13);
861 cdt.AddColumn("Conns", 5);
862 cdt.AddColumn("Behaviours", 20);
863
864 Dictionary<ConnectionState, int> totals = new Dictionary<ConnectionState, int>();
865 foreach (object o in Enum.GetValues(typeof(ConnectionState)))
866 totals[(ConnectionState)o] = 0;
867
868 lock (m_bots)
331 { 869 {
332 foreach (Bot pb in m_lBot) 870 foreach (Bot bot in m_bots)
333 { 871 {
334 Simulator currentSim = pb.Client.Network.CurrentSim; 872 Simulator currentSim = bot.Client.Network.CurrentSim;
335 873 totals[bot.ConnectionState]++;
336 MainConsole.Instance.OutputFormat( 874
337 outputFormat, 875 cdt.AddRow(
338 pb.Name, currentSim != null ? currentSim.Name : "(none)", pb.ConnectionState); 876 bot.Name,
877 currentSim != null ? currentSim.Name : "(none)",
878 bot.ConnectionState,
879 bot.SimulatorsCount,
880 string.Join(",", bot.Behaviours.Keys.ToArray()));
339 } 881 }
340 } 882 }
883
884 MainConsole.Instance.Output(cdt.ToString());
885
886 ConsoleDisplayList cdl = new ConsoleDisplayList();
887
888 foreach (KeyValuePair<ConnectionState, int> kvp in totals)
889 cdl.AddRow(kvp.Key, kvp.Value);
890
891 MainConsole.Instance.Output(cdl.ToString());
341 } 892 }
342 893
343 /* 894 private void HandleShowBotStatus(string module, string[] cmd)
344 private void HandleQuit(string module, string[] cmd)
345 { 895 {
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"); 896 if (cmd.Length != 3)
347 Environment.Exit(0); 897 {
898 MainConsole.Instance.Output("Usage: show bot <n>");
899 return;
900 }
901
902 int botNumber;
903
904 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, cmd[2], out botNumber))
905 return;
906
907 Bot bot = GetBotFromNumber(botNumber);
908
909 if (bot == null)
910 {
911 MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber);
912 return;
913 }
914
915 ConsoleDisplayList cdl = new ConsoleDisplayList();
916 cdl.AddRow("Name", bot.Name);
917 cdl.AddRow("Status", bot.ConnectionState);
918
919 Simulator currentSim = bot.Client.Network.CurrentSim;
920 cdl.AddRow("Region", currentSim != null ? currentSim.Name : "(none)");
921
922 List<Simulator> connectedSimulators = bot.Simulators;
923 List<string> simulatorNames = connectedSimulators.ConvertAll<string>(cs => cs.Name);
924 cdl.AddRow("Connections", string.Join(", ", simulatorNames.ToArray()));
925
926 MainConsole.Instance.Output(cdl.ToString());
927
928 MainConsole.Instance.Output("Settings");
929
930 ConsoleDisplayList statusCdl = new ConsoleDisplayList();
931
932 statusCdl.AddRow(
933 "Behaviours",
934 string.Join(", ", bot.Behaviours.Values.ToList().ConvertAll<string>(b => b.Name).ToArray()));
935
936 GridClient botClient = bot.Client;
937 statusCdl.AddRow("SEND_AGENT_UPDATES", botClient.Settings.SEND_AGENT_UPDATES);
938
939 MainConsole.Instance.Output(statusCdl.ToString());
940 }
941
942 /// <summary>
943 /// Get a specific bot from its number.
944 /// </summary>
945 /// <returns>null if no bot was found</returns>
946 /// <param name='botNumber'></param>
947 private Bot GetBotFromNumber(int botNumber)
948 {
949 string name = GenerateBotNameFromNumber(botNumber);
950
951 Bot bot;
952
953 lock (m_bots)
954 bot = m_bots.Find(b => b.Name == name);
955
956 return bot;
957 }
958
959 private string GenerateBotNameFromNumber(int botNumber)
960 {
961 return string.Format("{0} {1}_{2}", m_firstName, m_lastNameStem, botNumber);
348 } 962 }
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 963
363 internal void Grid_GridRegion(object o, GridRegionEventArgs args) 964 internal void Grid_GridRegion(object o, GridRegionEventArgs args)
364 { 965 {
@@ -379,4 +980,4 @@ namespace pCampBot
379 } 980 }
380 } 981 }
381 } 982 }
382} 983} \ No newline at end of file