Add ability to adjust pCampbot bot behaviours whilst running with "add behaviour <behaviour-name> <bot-number>" console commad

varregion
Justin Clark-Casey (justincc) 2013-09-03 18:51:55 +01:00
parent 01cb8033a4
commit 9bd6271570
2 changed files with 161 additions and 45 deletions

View File

@ -72,9 +72,10 @@ namespace pCampBot
/// Behaviours implemented by this bot. /// Behaviours implemented by this bot.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Lock this list before manipulating it. /// Indexed by abbreviated name. There can only be one instance of a particular behaviour.
/// Lock this structure before manipulating it.
/// </remarks> /// </remarks>
public List<IBehaviour> Behaviours { get; private set; } public Dictionary<string, IBehaviour> Behaviours { get; private set; }
/// <summary> /// <summary>
/// Objects that the bot has discovered. /// Objects that the bot has discovered.
@ -165,8 +166,6 @@ namespace pCampBot
{ {
ConnectionState = ConnectionState.Disconnected; ConnectionState = ConnectionState.Disconnected;
behaviours.ForEach(b => b.Initialize(this));
Random = new Random(Environment.TickCount);// We do stuff randomly here Random = new Random(Environment.TickCount);// We do stuff randomly here
FirstName = firstName; FirstName = firstName;
LastName = lastName; LastName = lastName;
@ -176,12 +175,31 @@ namespace pCampBot
StartLocation = startLocation; StartLocation = startLocation;
Manager = bm; Manager = bm;
Behaviours = behaviours;
Behaviours = new Dictionary<string, IBehaviour>();
foreach (IBehaviour behaviour in behaviours)
AddBehaviour(behaviour);
// Only calling for use as a template. // Only calling for use as a template.
CreateLibOmvClient(); CreateLibOmvClient();
} }
public bool AddBehaviour(IBehaviour behaviour)
{
lock (Behaviours)
{
if (!Behaviours.ContainsKey(behaviour.AbbreviatedName))
{
behaviour.Initialize(this);
Behaviours.Add(behaviour.AbbreviatedName, behaviour);
return true;
}
}
return false;
}
private void CreateLibOmvClient() private void CreateLibOmvClient()
{ {
GridClient newClient = new GridClient(); GridClient newClient = new GridClient();
@ -237,16 +255,21 @@ namespace pCampBot
private void Action() private void Action()
{ {
while (ConnectionState != ConnectionState.Disconnecting) while (ConnectionState != ConnectionState.Disconnecting)
{
lock (Behaviours) lock (Behaviours)
Behaviours.ForEach( {
b => foreach (IBehaviour behaviour in Behaviours.Values)
{ {
Thread.Sleep(Random.Next(3000, 10000)); Thread.Sleep(Random.Next(3000, 10000));
// m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType()); // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType());
b.Action(); behaviour.Action();
} }
); }
// XXX: This is a really shitty way of yielding so that behaviours can be added/removed
Thread.Sleep(100);
}
} }
/// <summary> /// <summary>

View File

@ -140,7 +140,7 @@ namespace pCampBot
/// <summary> /// <summary>
/// Behaviour switches for bots. /// Behaviour switches for bots.
/// </summary> /// </summary>
private HashSet<string> m_behaviourSwitches = new HashSet<string>(); private HashSet<string> m_defaultBehaviourSwitches = new HashSet<string>();
/// <summary> /// <summary>
/// Constructor Creates MainConsole.Instance to take commands and provide the place to write data /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data
@ -194,6 +194,18 @@ namespace pCampBot
+ "If no <n> is given, then all currently connected bots are disconnected.", + "If no <n> is given, then all currently connected bots are disconnected.",
HandleDisconnect); HandleDisconnect);
m_console.Commands.AddCommand(
"bot", false, "add behaviour", "add behaviour <abbreviated-name> <bot-number>",
"Add a behaviour to a bot",
"Can be performed on connected or disconnected bots.",
HandleAddBehaviour);
// m_console.Commands.AddCommand(
// "bot", false, "remove behaviour", "remove behaviour <abbreviated-name> <bot-number>",
// "Remove a behaviour from a bot",
// "Can be performed on connected or disconnected bots.",
// HandleRemoveBehaviour);
m_console.Commands.AddCommand( m_console.Commands.AddCommand(
"bot", false, "sit", "sit", "Sit all bots on the ground.", "bot", false, "sit", "sit", "Sit all bots on the ground.",
HandleSit); HandleSit);
@ -235,7 +247,7 @@ namespace pCampBot
m_startUri = ParseInputStartLocationToUri(startupConfig.GetString("start", "last")); m_startUri = ParseInputStartLocationToUri(startupConfig.GetString("start", "last"));
Array.ForEach<string>( Array.ForEach<string>(
startupConfig.GetString("behaviours", "p").Split(new char[] { ',' }), b => m_behaviourSwitches.Add(b)); startupConfig.GetString("behaviours", "p").Split(new char[] { ',' }), b => m_defaultBehaviourSwitches.Add(b));
for (int i = 0; i < botcount; i++) for (int i = 0; i < botcount; i++)
{ {
@ -243,30 +255,52 @@ namespace pCampBot
{ {
string lastName = string.Format("{0}_{1}", m_lastNameStem, i + m_fromBotNumber); string lastName = string.Format("{0}_{1}", m_lastNameStem, i + m_fromBotNumber);
// We must give each bot its own list of instantiated behaviours since they store state. CreateBot(
List<IBehaviour> behaviours = new List<IBehaviour>(); this,
CreateBehavioursFromAbbreviatedNames(m_defaultBehaviourSwitches),
// Hard-coded for now m_firstName, lastName, m_password, m_loginUri, m_startUri, m_wearSetting);
if (m_behaviourSwitches.Contains("c"))
behaviours.Add(new CrossBehaviour());
if (m_behaviourSwitches.Contains("g"))
behaviours.Add(new GrabbingBehaviour());
if (m_behaviourSwitches.Contains("n"))
behaviours.Add(new NoneBehaviour());
if (m_behaviourSwitches.Contains("p"))
behaviours.Add(new PhysicsBehaviour());
if (m_behaviourSwitches.Contains("t"))
behaviours.Add(new TeleportBehaviour());
CreateBot(this, behaviours, m_firstName, lastName, m_password, m_loginUri, m_startUri, m_wearSetting);
} }
} }
} }
private List<IBehaviour> CreateBehavioursFromAbbreviatedNames(HashSet<string> abbreviatedNames)
{
// We must give each bot its own list of instantiated behaviours since they store state.
List<IBehaviour> behaviours = new List<IBehaviour>();
// Hard-coded for now
foreach (string abName in abbreviatedNames)
{
IBehaviour newBehaviour = null;
if (abName == "c")
newBehaviour = new CrossBehaviour();
if (abName == "g")
newBehaviour = new GrabbingBehaviour();
if (abName == "n")
newBehaviour = new NoneBehaviour();
if (abName == "p")
newBehaviour = new PhysicsBehaviour();
if (abName == "t")
newBehaviour = new TeleportBehaviour();
if (newBehaviour != null)
{
behaviours.Add(newBehaviour);
}
else
{
MainConsole.Instance.OutputFormat("No behaviour with abbreviated name {0} found", abName);
}
}
return behaviours;
}
public void ConnectBots(int botcount) public void ConnectBots(int botcount)
{ {
ConnectingBots = true; ConnectingBots = true;
@ -453,6 +487,44 @@ namespace pCampBot
} }
} }
private void HandleAddBehaviour(string module, string[] cmd)
{
if (cmd.Length != 4)
{
MainConsole.Instance.OutputFormat("Usage: add behaviour <abbreviated-behaviour> <bot-number>");
return;
}
string rawBehaviours = cmd[2];
int botNumber;
if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber))
return;
Bot bot = GetBotFromNumber(botNumber);
if (bot == null)
{
MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber);
return;
}
HashSet<string> rawAbbreviatedSwitchesToAdd = new HashSet<string>();
Array.ForEach<string>(rawBehaviours.Split(new char[] { ',' }), b => rawAbbreviatedSwitchesToAdd.Add(b));
List<IBehaviour> behavioursAdded = new List<IBehaviour>();
foreach (IBehaviour behaviour in CreateBehavioursFromAbbreviatedNames(rawAbbreviatedSwitchesToAdd))
{
if (bot.AddBehaviour(behaviour))
behavioursAdded.Add(behaviour);
}
MainConsole.Instance.OutputFormat(
"Added behaviours {0} to bot {1}",
string.Join(", ", behavioursAdded.ConvertAll<string>(b => b.Name).ToArray()), bot.Name);
}
private void HandleDisconnect(string module, string[] cmd) private void HandleDisconnect(string module, string[] cmd)
{ {
lock (m_bots) lock (m_bots)
@ -594,7 +666,7 @@ namespace pCampBot
currentSim != null ? currentSim.Name : "(none)", currentSim != null ? currentSim.Name : "(none)",
bot.ConnectionState, bot.ConnectionState,
bot.SimulatorsCount, bot.SimulatorsCount,
string.Join(",", bot.Behaviours.ConvertAll<string>(behaviour => behaviour.AbbreviatedName).ToArray())); string.Join(",", bot.Behaviours.Keys.ToArray()));
} }
} }
@ -621,16 +693,11 @@ namespace pCampBot
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, cmd[2], out botNumber)) if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, cmd[2], out botNumber))
return; return;
string name = string.Format("{0} {1}_{2}", m_firstName, m_lastNameStem, botNumber); Bot bot = GetBotFromNumber(botNumber);
Bot bot;
lock (m_bots)
bot = m_bots.Find(b => b.Name == name);
if (bot == null) if (bot == null)
{ {
MainConsole.Instance.Output("No bot found with name {0}", name); MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber);
return; return;
} }
@ -650,13 +717,39 @@ namespace pCampBot
MainConsole.Instance.Output("Settings"); MainConsole.Instance.Output("Settings");
ConsoleDisplayList statusCdl = new ConsoleDisplayList(); ConsoleDisplayList statusCdl = new ConsoleDisplayList();
statusCdl.AddRow("Behaviours", string.Join(", ", bot.Behaviours.ConvertAll<string>(b => b.Name).ToArray()));
statusCdl.AddRow(
"Behaviours",
string.Join(", ", bot.Behaviours.Values.ToList().ConvertAll<string>(b => b.Name).ToArray()));
GridClient botClient = bot.Client; GridClient botClient = bot.Client;
statusCdl.AddRow("SEND_AGENT_UPDATES", botClient.Settings.SEND_AGENT_UPDATES); statusCdl.AddRow("SEND_AGENT_UPDATES", botClient.Settings.SEND_AGENT_UPDATES);
MainConsole.Instance.Output(statusCdl.ToString()); MainConsole.Instance.Output(statusCdl.ToString());
} }
/// <summary>
/// Get a specific bot from its number.
/// </summary>
/// <returns>null if no bot was found</returns>
/// <param name='botNumber'></param>
private Bot GetBotFromNumber(int botNumber)
{
string name = GenerateBotNameFromNumber(botNumber);
Bot bot;
lock (m_bots)
bot = m_bots.Find(b => b.Name == name);
return bot;
}
private string GenerateBotNameFromNumber(int botNumber)
{
return string.Format("{0} {1}_{2}", m_firstName, m_lastNameStem, botNumber);
}
internal void Grid_GridRegion(object o, GridRegionEventArgs args) internal void Grid_GridRegion(object o, GridRegionEventArgs args)
{ {
lock (RegionsKnown) lock (RegionsKnown)