29 using System.Collections.Generic;
31 using System.Reflection;
32 using System.Threading;
35 using log4net.Appender;
37 using log4net.Repository;
39 using OpenSim.Framework;
40 using OpenSim.Framework.Console;
41 using OpenSim.Framework.Monitoring;
42 using pCampBot.Interfaces;
59 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
71 private object BotConnectingStateChangeObject =
new object();
102 public Random
Rng {
get;
private set; }
112 public Dictionary<ulong, GridRegion>
RegionsKnown {
get;
private set; }
117 private string m_firstName;
122 private string m_lastNameStem;
127 private string m_password;
132 private string m_loginUri;
137 private string m_startUri;
142 private int m_fromBotNumber;
147 private string m_wearSetting;
152 private HashSet<string> m_defaultBehaviourSwitches =
new HashSet<string>();
166 Settings.MAX_HTTP_CONNECTIONS = int.MaxValue;
179 Rng =
new Random(Environment.TickCount);
188 ILoggerRepository repository = LogManager.GetRepository();
189 IAppender[] appenders = repository.GetAppenders();
192 foreach (IAppender appender
in appenders)
194 if (appender.Name ==
"Console")
202 m_console.Commands.AddCommand(
203 "Bots",
false,
"shutdown",
"shutdown",
"Shutdown bots and exit", HandleShutdown);
205 m_console.Commands.AddCommand(
206 "Bots",
false,
"quit",
"quit",
"Shutdown bots and exit", HandleShutdown);
208 m_console.Commands.AddCommand(
209 "Bots",
false,
"connect",
"connect [<n>]",
"Connect bots",
210 "If an <n> is given, then the first <n> disconnected bots by postfix number are connected.\n"
211 +
"If no <n> is given, then all currently disconnected bots are connected.",
214 m_console.Commands.AddCommand(
215 "Bots",
false,
"disconnect",
"disconnect [<n>]",
"Disconnect bots",
216 "Disconnecting bots will interupt any bot connection process, including connection on startup.\n"
217 +
"If an <n> is given, then the last <n> connected bots by postfix number are disconnected.\n"
218 +
"If no <n> is given, then all currently connected bots are disconnected.",
221 m_console.Commands.AddCommand(
222 "Bots",
false,
"add behaviour",
"add behaviour <abbreviated-name> [<bot-number>]",
223 "Add a behaviour to a bot",
224 "If no bot number is specified then behaviour is added to all bots.\n"
225 +
"Can be performed on connected or disconnected bots.",
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);
235 m_console.Commands.AddCommand(
236 "Bots",
false,
"sit",
"sit",
"Sit all bots on the ground.",
239 m_console.Commands.AddCommand(
240 "Bots",
false,
"stand",
"stand",
"Stand all bots.",
243 m_console.Commands.AddCommand(
244 "Bots",
false,
"set bots",
"set bots <key> <value>",
"Set a setting for all bots.", HandleSetBots);
246 m_console.Commands.AddCommand(
247 "Bots",
false,
"show regions",
"show regions",
"Show regions known to bots", HandleShowRegions);
249 m_console.Commands.AddCommand(
250 "Bots",
false,
"show bots",
"show bots",
"Shows the status of all bots.", HandleShowBotsStatus);
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);
256 m_console.Commands.AddCommand(
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);
266 m_console.Commands.AddCommand(
267 "Bots",
false,
"show status",
"show status",
"Shows pCampbot status.", HandleShowStatus);
271 Watchdog.Enabled =
true;
272 StatsManager.RegisterConsoleCommands(
m_console);
275 m_serverStatsCollector.Initialise(null);
276 m_serverStatsCollector.Enabled =
true;
277 m_serverStatsCollector.Start();
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");
296 m_startUri = ParseInputStartLocationToUri(startupConfig.GetString(
"start",
"last"));
298 Array.ForEach<
string>(
299 startupConfig.GetString(
"behaviours",
"p").Split(
new char[] {
',' }), b => m_defaultBehaviourSwitches.Add(b));
301 for (
int i = 0; i < botcount; i++)
305 string lastName = string.Format(
"{0}_{1}", m_lastNameStem, i + m_fromBotNumber);
309 CreateBehavioursFromAbbreviatedNames(m_defaultBehaviourSwitches),
310 m_firstName, lastName, m_password, m_loginUri, m_startUri, m_wearSetting);
315 private List<IBehaviour> CreateBehavioursFromAbbreviatedNames(HashSet<string> abbreviatedNames)
318 List<IBehaviour> behaviours =
new List<IBehaviour>();
321 foreach (
string abName
in abbreviatedNames)
349 if (newBehaviour != null)
351 behaviours.Add(newBehaviour);
355 MainConsole.Instance.OutputFormat(
"No behaviour with abbreviated name {0} found", abName);
364 lock (BotConnectingStateChangeObject)
368 MainConsole.Instance.OutputFormat(
369 "Bot connecting status is {0}. Please wait for previous process to complete.",
BotConnectingState);
376 Thread connectBotThread =
new Thread(o => ConnectBotsInternal(botcount));
378 connectBotThread.Name =
"Bots connection thread";
379 connectBotThread.Start();
382 private void ConnectBotsInternal(
int botCount)
385 "[BOT MANAGER]: Starting {0} bots connecting to {1}, location {2}, named {3} {4}_<n>",
392 m_log.DebugFormat(
"[BOT MANAGER]: Delay between logins is {0}ms",
LoginDelay);
396 List<Bot> botsToConnect =
new List<Bot>();
403 botsToConnect.Add(bot);
405 if (botsToConnect.Count >= botCount)
410 foreach (Bot bot
in botsToConnect)
412 lock (BotConnectingStateChangeObject)
416 MainConsole.Instance.Output(
417 "[BOT MANAGER]: Aborting bot connection due to user-initiated disconnection");
428 lock (BotConnectingStateChangeObject)
444 private string ParseInputStartLocationToUri(
string startLocation)
446 if (startLocation ==
"home" || startLocation ==
"last")
447 return startLocation;
452 Vector3 startPos =
new Vector3(128, 128, 0);
454 string[] startLocationComponents = startLocation.Split(
'/');
456 regionName = startLocationComponents[0];
458 if (startLocationComponents.Length >= 2)
460 float.TryParse(startLocationComponents[1], out startPos.X);
462 if (startLocationComponents.Length >= 3)
464 float.TryParse(startLocationComponents[2], out startPos.Y);
466 if (startLocationComponents.Length >= 4)
467 float.TryParse(startLocationComponents[3], out startPos.Z);
471 return string.Format(
"uri:{0}&{1}&{2}&{3}", regionName, startPos.X, startPos.Y, startPos.Z);
487 string firstName,
string lastName,
string password,
string loginUri,
string startLocation,
string wearSetting)
489 MainConsole.Instance.OutputFormat(
490 "[BOT MANAGER]: Creating bot {0} {1}, behaviours are {2}",
491 firstName, lastName, string.Join(
",", behaviours.ConvertAll<
string>(b => b.Name).ToArray()));
493 Bot pb =
new Bot(bm, behaviours, firstName, lastName, password, startLocation, loginUri);
494 pb.wear = wearSetting;
498 pb.OnConnected += handlebotEvent;
499 pb.OnDisconnected += handlebotEvent;
509 private void handlebotEvent(
Bot callbot,
EventType eventt)
513 case EventType.CONNECTED:
515 m_log.Info(
"[" + callbot.FirstName +
" " + callbot.LastName +
"]: Connected");
519 case EventType.DISCONNECTED:
521 m_log.Info(
"[" + callbot.FirstName +
" " + callbot.LastName +
"]: Disconnected");
536 private void HandleConnect(
string module,
string[] cmd)
541 int disconnectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Disconnected);
545 botsToConnect = disconnectedBots;
552 botsToConnect = Math.Min(botsToConnect, disconnectedBots);
555 MainConsole.Instance.OutputFormat(
"Connecting {0} bots", botsToConnect);
561 private void HandleAddBehaviour(
string module,
string[] cmd)
563 if (cmd.Length < 3 || cmd.Length > 4)
565 MainConsole.Instance.OutputFormat(
"Usage: add behaviour <abbreviated-behaviour> [<bot-number>]");
569 string rawBehaviours = cmd[2];
571 List<Bot> botsToEffect =
new List<Bot>();
576 botsToEffect.AddRange(m_bots);
584 Bot bot = GetBotFromNumber(botNumber);
588 MainConsole.Instance.OutputFormat(
"Error: No bot found with number {0}", botNumber);
592 botsToEffect.Add(bot);
596 HashSet<string> rawAbbreviatedSwitchesToAdd =
new HashSet<string>();
597 Array.ForEach<
string>(rawBehaviours.Split(
new char[] {
',' }), b => rawAbbreviatedSwitchesToAdd.Add(b));
599 foreach (Bot bot
in botsToEffect)
601 List<IBehaviour> behavioursAdded =
new List<IBehaviour>();
603 foreach (
IBehaviour behaviour
in CreateBehavioursFromAbbreviatedNames(rawAbbreviatedSwitchesToAdd))
605 if (bot.AddBehaviour(behaviour))
606 behavioursAdded.Add(behaviour);
609 MainConsole.Instance.OutputFormat(
610 "Added behaviours {0} to bot {1}",
611 string.Join(
", ", behavioursAdded.ConvertAll<
string>(b => b.Name).ToArray()), bot.Name);
615 private void HandleRemoveBehaviour(
string module,
string[] cmd)
617 if (cmd.Length < 3 || cmd.Length > 4)
619 MainConsole.Instance.OutputFormat(
"Usage: remove behaviour <abbreviated-behaviour> [<bot-number>]");
623 string rawBehaviours = cmd[2];
625 List<Bot> botsToEffect =
new List<Bot>();
630 botsToEffect.AddRange(m_bots);
638 Bot bot = GetBotFromNumber(botNumber);
642 MainConsole.Instance.OutputFormat(
"Error: No bot found with number {0}", botNumber);
646 botsToEffect.Add(bot);
649 HashSet<string> abbreviatedBehavioursToRemove =
new HashSet<string>();
650 Array.ForEach<
string>(rawBehaviours.Split(
new char[] {
',' }), b => abbreviatedBehavioursToRemove.Add(b));
652 foreach (Bot bot
in botsToEffect)
654 List<IBehaviour> behavioursRemoved =
new List<IBehaviour>();
656 foreach (
string b
in abbreviatedBehavioursToRemove)
660 if (bot.TryGetBehaviour(b, out behaviour))
662 bot.RemoveBehaviour(b);
663 behavioursRemoved.Add(behaviour);
667 MainConsole.Instance.OutputFormat(
668 "Removed behaviours {0} from bot {1}",
669 string.Join(
", ", behavioursRemoved.ConvertAll<
string>(b => b.Name).ToArray()), bot.Name);
673 private void HandleDisconnect(
string module,
string[] cmd)
675 List<Bot> connectedBots;
676 int botsToDisconnectCount;
683 botsToDisconnectCount = connectedBots.Count;
690 botsToDisconnectCount = Math.Min(botsToDisconnectCount, connectedBots.Count);
693 lock (BotConnectingStateChangeObject)
696 Thread disconnectBotThread = new Thread(o => DisconnectBotsInternal(connectedBots, botsToDisconnectCount));
698 disconnectBotThread.Name = "Bots disconnection thread";
699 disconnectBotThread.Start();
702 private
void DisconnectBotsInternal(List<Bot> connectedBots,
int disconnectCount)
704 MainConsole.Instance.OutputFormat(
"Disconnecting {0} bots", disconnectCount);
706 int disconnectedBots = 0;
708 for (
int i = connectedBots.Count - 1; i >= 0; i--)
710 if (disconnectedBots >= disconnectCount)
713 Bot thisBot = connectedBots[i];
717 ThreadPool.QueueUserWorkItem(o => thisBot.Disconnect());
722 lock (BotConnectingStateChangeObject)
726 private
void HandleSit(
string module,
string[] cmd)
730 foreach (Bot bot
in m_bots)
734 MainConsole.Instance.OutputFormat(
"Sitting bot {0} on ground.", bot.Name);
741 private void HandleStand(
string module,
string[] cmd)
745 foreach (Bot bot
in m_bots)
749 MainConsole.Instance.OutputFormat(
"Standing bot {0} from ground.", bot.Name);
756 private void HandleShutdown(
string module,
string[] cmd)
760 int connectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Connected);
762 if (connectedBots > 0)
764 MainConsole.Instance.OutputFormat(
"Please disconnect {0} connected bots first", connectedBots);
769 MainConsole.Instance.Output(
"Shutting down");
771 m_serverStatsCollector.Close();
776 private void HandleSetBots(
string module,
string[] cmd)
779 string rawValue = cmd[3];
781 if (key ==
"SEND_AGENT_UPDATES")
783 bool newSendAgentUpdatesSetting;
788 MainConsole.Instance.OutputFormat(
789 "Setting SEND_AGENT_UPDATES to {0} for all bots", newSendAgentUpdatesSetting);
792 m_bots.ForEach(b => b.Client.Settings.SEND_AGENT_UPDATES = newSendAgentUpdatesSetting);
796 MainConsole.Instance.Output(
"Error: Only setting currently available is SEND_AGENT_UPDATES");
800 private void HandleDebugLludpPacketCommand(
string module,
string[] args)
802 if (args.Length != 6)
804 MainConsole.Instance.OutputFormat(
"Usage: debug lludp packet <level> <bot-first-name> <bot-last-name>");
813 string botFirstName = args[4];
814 string botLastName = args[5];
819 bot = m_bots.FirstOrDefault(b => b.FirstName == botFirstName && b.LastName == botLastName);
823 MainConsole.Instance.OutputFormat(
"No bot named {0} {1}", botFirstName, botLastName);
827 bot.PacketDebugLevel = level;
829 MainConsole.Instance.OutputFormat(
"Set debug level of {0} to {1}", bot.Name, bot.PacketDebugLevel);
832 private void HandleShowRegions(
string module,
string[] cmd)
834 string outputFormat =
"{0,-30} {1, -20} {2, -5} {3, -5}";
835 MainConsole.Instance.OutputFormat(outputFormat,
"Name",
"Handle",
"X",
"Y");
841 MainConsole.Instance.OutputFormat(
842 outputFormat, region.Name, region.RegionHandle, region.X, region.Y);
847 private void HandleShowStatus(
string module,
string[] cmd)
852 MainConsole.Instance.Output(cdl.ToString());
855 private void HandleShowBotsStatus(
string module,
string[] cmd)
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);
864 Dictionary<ConnectionState, int> totals =
new Dictionary<ConnectionState, int>();
870 foreach (Bot bot
in m_bots)
872 Simulator currentSim = bot.Client.Network.CurrentSim;
877 currentSim != null ? currentSim.Name :
"(none)",
880 string.Join(
",", bot.Behaviours.Keys.ToArray()));
884 MainConsole.Instance.Output(cdt.ToString());
888 foreach (KeyValuePair<ConnectionState, int> kvp
in totals)
889 cdl.AddRow(kvp.Key, kvp.Value);
891 MainConsole.Instance.Output(cdl.ToString());
894 private void HandleShowBotStatus(
string module,
string[] cmd)
898 MainConsole.Instance.Output(
"Usage: show bot <n>");
907 Bot bot = GetBotFromNumber(botNumber);
911 MainConsole.Instance.OutputFormat(
"Error: No bot found with number {0}", botNumber);
916 cdl.AddRow(
"Name", bot.Name);
919 Simulator currentSim = bot.Client.Network.CurrentSim;
920 cdl.AddRow(
"Region", currentSim != null ? currentSim.Name :
"(none)");
922 List<Simulator> connectedSimulators = bot.Simulators;
923 List<string> simulatorNames = connectedSimulators.ConvertAll<
string>(cs => cs.Name);
924 cdl.AddRow(
"Connections", string.Join(
", ", simulatorNames.ToArray()));
926 MainConsole.Instance.Output(cdl.ToString());
928 MainConsole.Instance.Output(
"Settings");
934 string.Join(
", ", bot.Behaviours.Values.ToList().ConvertAll<string>(b => b.Name).ToArray()));
936 GridClient botClient = bot.Client;
937 statusCdl.AddRow(
"SEND_AGENT_UPDATES", botClient.Settings.SEND_AGENT_UPDATES);
939 MainConsole.Instance.Output(statusCdl.ToString());
947 private Bot GetBotFromNumber(
int botNumber)
949 string name = GenerateBotNameFromNumber(botNumber);
954 bot = m_bots.Find(b => b.Name == name);
959 private
string GenerateBotNameFromNumber(
int botNumber)
961 return string.Format(
"{0} {1}_{2}", m_firstName, m_lastNameStem, botNumber);
964 internal void Grid_GridRegion(
object o, GridRegionEventArgs args)
977 "[BOT MANAGER]: Adding {0} {1} to known regions", newRegion.Name, newRegion.RegionHandle);
Used to generated a formatted table for the console.
List< Bot > m_bots
Created bots, whether active or inactive.
static bool TryParseConsoleInt(ICommandConsole console, string rawConsoleInt, out int i)
Convert a console input to an int, automatically complaining if a console is given.
Used to generated a formatted table for the console.
void ConnectBots(int botcount)
EventType
Event Types from the BOT. Add new events here
Teleport to a random region on the grid.
int LoginDelay
Delay between logins of multiple bots.
static bool TryParseConsoleBool(ICommandConsole console, string rawConsoleString, out bool b)
Convert a console input to a bool, automatically complaining if a console is given.
OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString key
void CreateBots(int botcount, IConfig startupConfig)
Startup number of bots specified in the starting arguments
Thread/Bot manager for the application
Stress physics by moving and bouncing around bots a whole lot.
Random Rng
Random number generator.
Writes log information out onto the console
bool InitBotRequestObjectTextures
Controls whether bots request textures for the object information they receive
ConnectionState ConnectionState
Is this bot connected to the grid?
This behavior is for the systematic study of some performance improvements made for OSCC'13...
static ICommandConsole Instance
This behavior is for the systematic study of some performance improvements made for OSCC'13...
CommandConsole m_console
Command console
Get the bot to make a region crossing.
void CreateBot(BotManager bm, List< IBehaviour > behaviours, string firstName, string lastName, string password, string loginUri, string startLocation, string wearSetting)
This creates a bot but does not start it.
static bool TryParseConsoleNaturalInt(ICommandConsole console, string rawConsoleInt, out int i)
Convert a console integer to a natural int, automatically complaining if a console is given...
Dictionary< ulong, GridRegion > RegionsKnown
The regions that we know about.
const int DefaultLoginDelay
BotManagerBotConnectingState
BotManager()
Constructor Creates MainConsole.Instance to take commands and provide the place to write data ...
CommandConsole CreateConsole()
Standard CreateConsole routine
OpenSim.Services.Interfaces.GridRegion GridRegion
BotManagerBotConnectingState BotConnectingState
Is pCampbot ready to connect or currently in the process of connecting or disconnecting bots...
bool InitBotSendAgentUpdates
Controls whether bots start out sending agent updates on connection.
A console that uses cursor control and color
Click (grab) on random objects in the scene.
A console that processes commands internally
Dictionary< UUID, bool > AssetsReceived
Track the assets we have and have not received so we don't endlessly repeat requests.