From 9bd62715704685738c55c6de8127b16cc6695bdb Mon Sep 17 00:00:00 2001
From: Justin Clark-Casey (justincc)
Date: Tue, 3 Sep 2013 18:51:55 +0100
Subject: Add ability to adjust pCampbot bot behaviours whilst running with
 "add behaviour <behaviour-name> <bot-number>" console commad

---
 OpenSim/Tools/pCampBot/Bot.cs        |  51 ++++++++----
 OpenSim/Tools/pCampBot/BotManager.cs | 147 ++++++++++++++++++++++++++++-------
 2 files changed, 157 insertions(+), 41 deletions(-)

(limited to 'OpenSim/Tools')

diff --git a/OpenSim/Tools/pCampBot/Bot.cs b/OpenSim/Tools/pCampBot/Bot.cs
index d418288..6037516 100644
--- a/OpenSim/Tools/pCampBot/Bot.cs
+++ b/OpenSim/Tools/pCampBot/Bot.cs
@@ -72,9 +72,10 @@ namespace pCampBot
         /// Behaviours implemented by this bot.
         /// </summary>
         /// <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>
-        public List<IBehaviour> Behaviours { get; private set; }
+        public Dictionary<string, IBehaviour> Behaviours { get; private set; }
 
         /// <summary>
         /// Objects that the bot has discovered.
@@ -165,8 +166,6 @@ namespace pCampBot
         {
             ConnectionState = ConnectionState.Disconnected;
 
-            behaviours.ForEach(b => b.Initialize(this));
-
             Random = new Random(Environment.TickCount);// We do stuff randomly here
             FirstName = firstName;
             LastName = lastName;
@@ -176,12 +175,31 @@ namespace pCampBot
             StartLocation = startLocation;
 
             Manager = bm;
-            Behaviours = behaviours;
+
+            Behaviours = new Dictionary<string, IBehaviour>();
+            foreach (IBehaviour behaviour in behaviours)
+                AddBehaviour(behaviour);
 
             // Only calling for use as a template.
             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()
         {
             GridClient newClient = new GridClient();
@@ -237,16 +255,21 @@ namespace pCampBot
         private void Action()
         {
             while (ConnectionState != ConnectionState.Disconnecting)
+            {
                 lock (Behaviours)
-                    Behaviours.ForEach(
-                        b =>
-                        {
-                            Thread.Sleep(Random.Next(3000, 10000));
-                        
-                            // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType());
-                            b.Action();
-                        }
-                    );
+                {
+                    foreach (IBehaviour behaviour in Behaviours.Values)
+                    {
+                        Thread.Sleep(Random.Next(3000, 10000));
+                    
+                        // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType());
+                        behaviour.Action();
+                    }
+                }
+
+                // XXX: This is a really shitty way of yielding so that behaviours can be added/removed
+                Thread.Sleep(100);
+            }
         }
 
         /// <summary>
diff --git a/OpenSim/Tools/pCampBot/BotManager.cs b/OpenSim/Tools/pCampBot/BotManager.cs
index 3e446af..51c5ff4 100644
--- a/OpenSim/Tools/pCampBot/BotManager.cs
+++ b/OpenSim/Tools/pCampBot/BotManager.cs
@@ -140,7 +140,7 @@ namespace pCampBot
         /// <summary>
         /// Behaviour switches for bots.
         /// </summary>
-        private HashSet<string> m_behaviourSwitches = new HashSet<string>();
+        private HashSet<string> m_defaultBehaviourSwitches = new HashSet<string>();
 
         /// <summary>
         /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data
@@ -195,6 +195,18 @@ namespace pCampBot
                 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(
                 "bot", false, "sit", "sit", "Sit all bots on the ground.",
                 HandleSit);
 
@@ -235,7 +247,7 @@ namespace pCampBot
             m_startUri = ParseInputStartLocationToUri(startupConfig.GetString("start", "last"));
 
             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++)
             {
@@ -243,28 +255,50 @@ namespace pCampBot
                 {
                     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.
-                    List<IBehaviour> behaviours = new List<IBehaviour>();
-        
-                    // Hard-coded for now        
-                    if (m_behaviourSwitches.Contains("c"))
-                        behaviours.Add(new CrossBehaviour());
+                    CreateBot(
+                        this,
+                        CreateBehavioursFromAbbreviatedNames(m_defaultBehaviourSwitches),
+                        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 (m_behaviourSwitches.Contains("g"))
-                        behaviours.Add(new GrabbingBehaviour());
+                if (abName == "n")
+                    newBehaviour = new NoneBehaviour();
 
-                    if (m_behaviourSwitches.Contains("n"))
-                        behaviours.Add(new NoneBehaviour());
+                if (abName == "p")
+                    newBehaviour = new PhysicsBehaviour();
 
-                    if (m_behaviourSwitches.Contains("p"))
-                        behaviours.Add(new PhysicsBehaviour());
-        
-                    if (m_behaviourSwitches.Contains("t"))
-                        behaviours.Add(new TeleportBehaviour());
+                if (abName == "t")
+                    newBehaviour = new TeleportBehaviour();
 
-                    CreateBot(this, behaviours, m_firstName, lastName, m_password, m_loginUri, m_startUri, m_wearSetting);
+                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)
@@ -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)
         {
             lock (m_bots)
@@ -594,7 +666,7 @@ namespace pCampBot
                         currentSim != null ? currentSim.Name : "(none)", 
                         bot.ConnectionState, 
                         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))
                 return;
 
-            string name = string.Format("{0} {1}_{2}", m_firstName, m_lastNameStem, botNumber);
-
-            Bot bot;
-
-            lock (m_bots)
-                bot = m_bots.Find(b => b.Name == name);
+            Bot bot = GetBotFromNumber(botNumber);
 
             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;
             }
 
@@ -650,13 +717,39 @@ namespace pCampBot
             MainConsole.Instance.Output("Settings");
 
             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;
             statusCdl.AddRow("SEND_AGENT_UPDATES", botClient.Settings.SEND_AGENT_UPDATES);
 
             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)
         {
             lock (RegionsKnown)
-- 
cgit v1.1