aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Tools/pCampBot
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Tools/pCampBot')
-rw-r--r--OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs7
-rw-r--r--OpenSim/Tools/pCampBot/Behaviours/CrossBehaviour.cs6
-rw-r--r--OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs9
-rw-r--r--OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs47
-rw-r--r--OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs7
-rw-r--r--OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs9
-rw-r--r--OpenSim/Tools/pCampBot/Bot.cs250
-rw-r--r--OpenSim/Tools/pCampBot/BotManager.cs768
-rw-r--r--OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs13
-rw-r--r--OpenSim/Tools/pCampBot/pCampBot.cs89
10 files changed, 969 insertions, 236 deletions
diff --git a/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs
index 9a9371d..9bc8512 100644
--- a/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs
+++ b/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs
@@ -35,6 +35,11 @@ namespace pCampBot
35{ 35{
36 public class AbstractBehaviour : IBehaviour 36 public class AbstractBehaviour : IBehaviour
37 { 37 {
38 /// <summary>
39 /// Abbreviated name of this behaviour.
40 /// </summary>
41 public string AbbreviatedName { get; protected set; }
42
38 public string Name { get; protected set; } 43 public string Name { get; protected set; }
39 44
40 public Bot Bot { get; protected set; } 45 public Bot Bot { get; protected set; }
@@ -45,5 +50,7 @@ namespace pCampBot
45 { 50 {
46 Bot = bot; 51 Bot = bot;
47 } 52 }
53
54 public virtual void Close() {}
48 } 55 }
49} 56}
diff --git a/OpenSim/Tools/pCampBot/Behaviours/CrossBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/CrossBehaviour.cs
index 1e01c64..4d806fc 100644
--- a/OpenSim/Tools/pCampBot/Behaviours/CrossBehaviour.cs
+++ b/OpenSim/Tools/pCampBot/Behaviours/CrossBehaviour.cs
@@ -47,7 +47,11 @@ namespace pCampBot
47 47
48 public const int m_regionCrossingTimeout = 1000 * 60; 48 public const int m_regionCrossingTimeout = 1000 * 60;
49 49
50 public CrossBehaviour() { Name = "Cross"; } 50 public CrossBehaviour()
51 {
52 AbbreviatedName = "c";
53 Name = "Cross";
54 }
51 55
52 public override void Action() 56 public override void Action()
53 { 57 {
diff --git a/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs
index 66a336a..59f6244 100644
--- a/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs
+++ b/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs
@@ -29,6 +29,7 @@ using OpenMetaverse;
29using System; 29using System;
30using System.Collections.Generic; 30using System.Collections.Generic;
31using System.Linq; 31using System.Linq;
32using System.Threading;
32using pCampBot.Interfaces; 33using pCampBot.Interfaces;
33 34
34namespace pCampBot 35namespace pCampBot
@@ -41,7 +42,11 @@ namespace pCampBot
41 /// </remarks> 42 /// </remarks>
42 public class GrabbingBehaviour : AbstractBehaviour 43 public class GrabbingBehaviour : AbstractBehaviour
43 { 44 {
44 public GrabbingBehaviour() { Name = "Grabbing"; } 45 public GrabbingBehaviour()
46 {
47 AbbreviatedName = "g";
48 Name = "Grabbing";
49 }
45 50
46 public override void Action() 51 public override void Action()
47 { 52 {
@@ -56,6 +61,8 @@ namespace pCampBot
56 Bot.Client.Self.Grab(prim.LocalID); 61 Bot.Client.Self.Grab(prim.LocalID);
57 Bot.Client.Self.GrabUpdate(prim.ID, Vector3.Zero); 62 Bot.Client.Self.GrabUpdate(prim.ID, Vector3.Zero);
58 Bot.Client.Self.DeGrab(prim.LocalID); 63 Bot.Client.Self.DeGrab(prim.LocalID);
64
65 Thread.Sleep(1000);
59 } 66 }
60 } 67 }
61} \ No newline at end of file 68} \ No newline at end of file
diff --git a/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs
new file mode 100644
index 0000000..9a3075c
--- /dev/null
+++ b/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs
@@ -0,0 +1,47 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenMetaverse;
29using System;
30using System.Collections.Generic;
31using System.Linq;
32using pCampBot.Interfaces;
33
34namespace pCampBot
35{
36 /// <summary>
37 /// Do nothing
38 /// </summary>
39 public class NoneBehaviour : AbstractBehaviour
40 {
41 public NoneBehaviour()
42 {
43 AbbreviatedName = "n";
44 Name = "None";
45 }
46 }
47} \ No newline at end of file
diff --git a/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs
index daa7485..6fd2b7c 100644
--- a/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs
+++ b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs
@@ -46,6 +46,7 @@ namespace pCampBot
46 46
47 public PhysicsBehaviour() 47 public PhysicsBehaviour()
48 { 48 {
49 AbbreviatedName = "p";
49 Name = "Physics"; 50 Name = "Physics";
50 talkarray = readexcuses(); 51 talkarray = readexcuses();
51 } 52 }
@@ -77,6 +78,12 @@ namespace pCampBot
77 Bot.Client.Self.Chat(randomf, 0, ChatType.Normal); 78 Bot.Client.Self.Chat(randomf, 0, ChatType.Normal);
78 } 79 }
79 80
81 public override void Close()
82 {
83 if (Bot.ConnectionState == ConnectionState.Connected)
84 Bot.Client.Self.Jump(false);
85 }
86
80 private string[] readexcuses() 87 private string[] readexcuses()
81 { 88 {
82 string allexcuses = ""; 89 string allexcuses = "";
diff --git a/OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs
index fbb4e96..81f250d 100644
--- a/OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs
+++ b/OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs
@@ -29,6 +29,7 @@ using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Linq; 30using System.Linq;
31using System.Reflection; 31using System.Reflection;
32using System.Threading;
32using log4net; 33using log4net;
33using OpenMetaverse; 34using OpenMetaverse;
34using pCampBot.Interfaces; 35using pCampBot.Interfaces;
@@ -42,7 +43,11 @@ namespace pCampBot
42 { 43 {
43 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
44 45
45 public TeleportBehaviour() { Name = "Teleport"; } 46 public TeleportBehaviour()
47 {
48 AbbreviatedName = "t";
49 Name = "Teleport";
50 }
46 51
47 public override void Action() 52 public override void Action()
48 { 53 {
@@ -70,6 +75,8 @@ namespace pCampBot
70 Bot.Name, sourceRegion.Name, Bot.Client.Self.SimPosition, destRegion.Name, destPosition); 75 Bot.Name, sourceRegion.Name, Bot.Client.Self.SimPosition, destRegion.Name, destPosition);
71 76
72 Bot.Client.Self.Teleport(destRegion.RegionHandle, destPosition); 77 Bot.Client.Self.Teleport(destRegion.RegionHandle, destPosition);
78
79 Thread.Sleep(Bot.Random.Next(3000, 10000));
73 } 80 }
74 } 81 }
75} \ No newline at end of file 82} \ No newline at end of file
diff --git a/OpenSim/Tools/pCampBot/Bot.cs b/OpenSim/Tools/pCampBot/Bot.cs
index 9821180..d0a4ef3 100644
--- a/OpenSim/Tools/pCampBot/Bot.cs
+++ b/OpenSim/Tools/pCampBot/Bot.cs
@@ -59,22 +59,23 @@ namespace pCampBot
59 public delegate void AnEvent(Bot callbot, EventType someevent); // event delegate for bot events 59 public delegate void AnEvent(Bot callbot, EventType someevent); // event delegate for bot events
60 60
61 /// <summary> 61 /// <summary>
62 /// Bot manager. 62 /// Controls whether bots request textures for the object information they receive
63 /// </summary> 63 /// </summary>
64 public BotManager Manager { get; private set; } 64 public bool RequestObjectTextures { get; set; }
65 65
66 /// <summary> 66 /// <summary>
67 /// Bot config, passed from BotManager. 67 /// Bot manager.
68 /// </summary> 68 /// </summary>
69 private IConfig startupConfig; 69 public BotManager Manager { get; private set; }
70 70
71 /// <summary> 71 /// <summary>
72 /// Behaviours implemented by this bot. 72 /// Behaviours implemented by this bot.
73 /// </summary> 73 /// </summary>
74 /// <remarks> 74 /// <remarks>
75 /// Lock this list before manipulating it. 75 /// Indexed by abbreviated name. There can only be one instance of a particular behaviour.
76 /// Lock this structure before manipulating it.
76 /// </remarks> 77 /// </remarks>
77 public List<IBehaviour> Behaviours { get; private set; } 78 public Dictionary<string, IBehaviour> Behaviours { get; private set; }
78 79
79 /// <summary> 80 /// <summary>
80 /// Objects that the bot has discovered. 81 /// Objects that the bot has discovered.
@@ -97,11 +98,35 @@ namespace pCampBot
97 /// </summary> 98 /// </summary>
98 public ConnectionState ConnectionState { get; private set; } 99 public ConnectionState ConnectionState { get; private set; }
99 100
101 public List<Simulator> Simulators
102 {
103 get
104 {
105 lock (Client.Network.Simulators)
106 return new List<Simulator>(Client.Network.Simulators);
107 }
108 }
109
110 /// <summary>
111 /// The number of connections that this bot has to different simulators.
112 /// </summary>
113 /// <value>Includes both root and child connections.</value>
114 public int SimulatorsCount
115 {
116 get
117 {
118 lock (Client.Network.Simulators)
119 return Client.Network.Simulators.Count;
120 }
121 }
122
100 public string FirstName { get; private set; } 123 public string FirstName { get; private set; }
101 public string LastName { get; private set; } 124 public string LastName { get; private set; }
102 public string Name { get; private set; } 125 public string Name { get; private set; }
103 public string Password { get; private set; } 126 public string Password { get; private set; }
104 public string LoginUri { get; private set; } 127 public string LoginUri { get; private set; }
128 public string StartLocation { get; private set; }
129
105 public string saveDir; 130 public string saveDir;
106 public string wear; 131 public string wear;
107 132
@@ -137,94 +162,177 @@ namespace pCampBot
137 /// <param name="behaviours"></param> 162 /// <param name="behaviours"></param>
138 public Bot( 163 public Bot(
139 BotManager bm, List<IBehaviour> behaviours, 164 BotManager bm, List<IBehaviour> behaviours,
140 string firstName, string lastName, string password, string loginUri) 165 string firstName, string lastName, string password, string startLocation, string loginUri)
141 { 166 {
142 ConnectionState = ConnectionState.Disconnected; 167 ConnectionState = ConnectionState.Disconnected;
143 168
144 behaviours.ForEach(b => b.Initialize(this));
145
146 Client = new GridClient();
147
148 Random = new Random(Environment.TickCount);// We do stuff randomly here 169 Random = new Random(Environment.TickCount);// We do stuff randomly here
149 FirstName = firstName; 170 FirstName = firstName;
150 LastName = lastName; 171 LastName = lastName;
151 Name = string.Format("{0} {1}", FirstName, LastName); 172 Name = string.Format("{0} {1}", FirstName, LastName);
152 Password = password; 173 Password = password;
153 LoginUri = loginUri; 174 LoginUri = loginUri;
175 StartLocation = startLocation;
154 176
155 Manager = bm; 177 Manager = bm;
156 startupConfig = bm.Config;
157 readconfig();
158 178
159 Behaviours = behaviours; 179 Behaviours = new Dictionary<string, IBehaviour>();
180 foreach (IBehaviour behaviour in behaviours)
181 AddBehaviour(behaviour);
182
183 // Only calling for use as a template.
184 CreateLibOmvClient();
185 }
186
187 public bool TryGetBehaviour(string abbreviatedName, out IBehaviour behaviour)
188 {
189 lock (Behaviours)
190 return Behaviours.TryGetValue(abbreviatedName, out behaviour);
191 }
192
193 public bool AddBehaviour(IBehaviour behaviour)
194 {
195 lock (Behaviours)
196 {
197 if (!Behaviours.ContainsKey(behaviour.AbbreviatedName))
198 {
199 behaviour.Initialize(this);
200 Behaviours.Add(behaviour.AbbreviatedName, behaviour);
201
202 return true;
203 }
204 }
205
206 return false;
207 }
208
209 public bool RemoveBehaviour(string abbreviatedName)
210 {
211 lock (Behaviours)
212 {
213 IBehaviour behaviour;
214
215 if (!Behaviours.TryGetValue(abbreviatedName, out behaviour))
216 return false;
217
218 behaviour.Close();
219 Behaviours.Remove(abbreviatedName);
220
221 return true;
222 }
223 }
224
225 private void CreateLibOmvClient()
226 {
227 GridClient newClient = new GridClient();
228
229 if (Client != null)
230 {
231 newClient.Settings.LOGIN_SERVER = Client.Settings.LOGIN_SERVER;
232 newClient.Settings.ALWAYS_DECODE_OBJECTS = Client.Settings.ALWAYS_DECODE_OBJECTS;
233 newClient.Settings.AVATAR_TRACKING = Client.Settings.AVATAR_TRACKING;
234 newClient.Settings.OBJECT_TRACKING = Client.Settings.OBJECT_TRACKING;
235 newClient.Settings.SEND_AGENT_THROTTLE = Client.Settings.SEND_AGENT_THROTTLE;
236 newClient.Settings.SEND_AGENT_UPDATES = Client.Settings.SEND_AGENT_UPDATES;
237 newClient.Settings.SEND_PINGS = Client.Settings.SEND_PINGS;
238 newClient.Settings.STORE_LAND_PATCHES = Client.Settings.STORE_LAND_PATCHES;
239 newClient.Settings.USE_ASSET_CACHE = Client.Settings.USE_ASSET_CACHE;
240 newClient.Settings.MULTIPLE_SIMS = Client.Settings.MULTIPLE_SIMS;
241 newClient.Throttle.Asset = Client.Throttle.Asset;
242 newClient.Throttle.Land = Client.Throttle.Land;
243 newClient.Throttle.Task = Client.Throttle.Task;
244 newClient.Throttle.Texture = Client.Throttle.Texture;
245 newClient.Throttle.Wind = Client.Throttle.Wind;
246 newClient.Throttle.Total = Client.Throttle.Total;
247 }
248 else
249 {
250 newClient.Settings.LOGIN_SERVER = LoginUri;
251 newClient.Settings.ALWAYS_DECODE_OBJECTS = false;
252 newClient.Settings.AVATAR_TRACKING = false;
253 newClient.Settings.OBJECT_TRACKING = false;
254 newClient.Settings.SEND_AGENT_THROTTLE = true;
255 newClient.Settings.SEND_PINGS = true;
256 newClient.Settings.STORE_LAND_PATCHES = false;
257 newClient.Settings.USE_ASSET_CACHE = false;
258 newClient.Settings.MULTIPLE_SIMS = true;
259 newClient.Throttle.Asset = 100000;
260 newClient.Throttle.Land = 100000;
261 newClient.Throttle.Task = 100000;
262 newClient.Throttle.Texture = 100000;
263 newClient.Throttle.Wind = 100000;
264 newClient.Throttle.Total = 400000;
265 }
266
267 newClient.Network.LoginProgress += this.Network_LoginProgress;
268 newClient.Network.SimConnected += this.Network_SimConnected;
269 newClient.Network.Disconnected += this.Network_OnDisconnected;
270 newClient.Objects.ObjectUpdate += Objects_NewPrim;
271
272 Client = newClient;
160 } 273 }
161 274
162 //We do our actions here. This is where one would 275 //We do our actions here. This is where one would
163 //add additional steps and/or things the bot should do 276 //add additional steps and/or things the bot should do
164 private void Action() 277 private void Action()
165 { 278 {
166 while (true) 279 while (ConnectionState != ConnectionState.Disconnecting)
280 {
167 lock (Behaviours) 281 lock (Behaviours)
168 Behaviours.ForEach( 282 {
169 b => 283 foreach (IBehaviour behaviour in Behaviours.Values)
170 { 284 {
171 Thread.Sleep(Random.Next(3000, 10000)); 285// Thread.Sleep(Random.Next(3000, 10000));
172 286
173 // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType()); 287 // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType());
174 b.Action(); 288 behaviour.Action();
175 } 289 }
176 ); 290 }
177 }
178 291
179 /// <summary> 292 // XXX: This is a really shitty way of yielding so that behaviours can be added/removed
180 /// Read the Nini config and initialize 293 Thread.Sleep(100);
181 /// </summary> 294 }
182 public void readconfig() 295
183 { 296 lock (Behaviours)
184 wear = startupConfig.GetString("wear", "no"); 297 foreach (IBehaviour b in Behaviours.Values)
298 b.Close();
185 } 299 }
186 300
187 /// <summary> 301 /// <summary>
188 /// Tells LibSecondLife to logout and disconnect. Raises the disconnect events once it finishes. 302 /// Tells LibSecondLife to logout and disconnect. Raises the disconnect events once it finishes.
189 /// </summary> 303 /// </summary>
190 public void shutdown() 304 public void Disconnect()
191 { 305 {
192 ConnectionState = ConnectionState.Disconnecting; 306 ConnectionState = ConnectionState.Disconnecting;
193 307
194 if (m_actionThread != null) 308// if (m_actionThread != null)
195 m_actionThread.Abort(); 309// m_actionThread.Abort();
196 310
197 Client.Network.Logout(); 311 Client.Network.Logout();
198 } 312 }
199 313
314 public void Connect()
315 {
316 Thread connectThread = new Thread(ConnectInternal);
317 connectThread.Name = Name;
318 connectThread.IsBackground = true;
319
320 connectThread.Start();
321 }
322
200 /// <summary> 323 /// <summary>
201 /// This is the bot startup loop. 324 /// This is the bot startup loop.
202 /// </summary> 325 /// </summary>
203 public void startup() 326 private void ConnectInternal()
204 { 327 {
205 Client.Settings.LOGIN_SERVER = LoginUri;
206 Client.Settings.ALWAYS_DECODE_OBJECTS = false;
207 Client.Settings.AVATAR_TRACKING = false;
208 Client.Settings.OBJECT_TRACKING = false;
209 Client.Settings.SEND_AGENT_THROTTLE = true;
210 Client.Settings.SEND_PINGS = true;
211 Client.Settings.STORE_LAND_PATCHES = false;
212 Client.Settings.USE_ASSET_CACHE = false;
213 Client.Settings.MULTIPLE_SIMS = true;
214 Client.Throttle.Asset = 100000;
215 Client.Throttle.Land = 100000;
216 Client.Throttle.Task = 100000;
217 Client.Throttle.Texture = 100000;
218 Client.Throttle.Wind = 100000;
219 Client.Throttle.Total = 400000;
220 Client.Network.LoginProgress += this.Network_LoginProgress;
221 Client.Network.SimConnected += this.Network_SimConnected;
222 Client.Network.Disconnected += this.Network_OnDisconnected;
223 Client.Objects.ObjectUpdate += Objects_NewPrim;
224
225 ConnectionState = ConnectionState.Connecting; 328 ConnectionState = ConnectionState.Connecting;
226 329
227 if (Client.Network.Login(FirstName, LastName, Password, "pCampBot", "Your name")) 330 // Current create a new client on each connect. libomv doesn't seem to process new sim
331 // information (e.g. EstablishAgentCommunication events) if connecting after a disceonnect with the same
332 // client
333 CreateLibOmvClient();
334
335 if (Client.Network.Login(FirstName, LastName, Password, "pCampBot", StartLocation, "Your name"))
228 { 336 {
229 ConnectionState = ConnectionState.Connected; 337 ConnectionState = ConnectionState.Connected;
230 338
@@ -268,6 +376,30 @@ namespace pCampBot
268 } 376 }
269 } 377 }
270 378
379 /// <summary>
380 /// Sit this bot on the ground.
381 /// </summary>
382 public void SitOnGround()
383 {
384 if (ConnectionState == ConnectionState.Connected)
385 Client.Self.SitOnGround();
386 }
387
388 /// <summary>
389 /// Stand this bot
390 /// </summary>
391 public void Stand()
392 {
393 if (ConnectionState == ConnectionState.Connected)
394 {
395 // Unlike sit on ground, here libomv checks whether we have SEND_AGENT_UPDATES enabled.
396 bool prevUpdatesSetting = Client.Settings.SEND_AGENT_UPDATES;
397 Client.Settings.SEND_AGENT_UPDATES = true;
398 Client.Self.Stand();
399 Client.Settings.SEND_AGENT_UPDATES = prevUpdatesSetting;
400 }
401 }
402
271 public void SaveDefaultAppearance() 403 public void SaveDefaultAppearance()
272 { 404 {
273 saveDir = "MyAppearance/" + FirstName + "_" + LastName; 405 saveDir = "MyAppearance/" + FirstName + "_" + LastName;
@@ -468,6 +600,8 @@ namespace pCampBot
468// || args.Reason == NetworkManager.DisconnectType.NetworkTimeout) 600// || args.Reason == NetworkManager.DisconnectType.NetworkTimeout)
469// && OnDisconnected != null) 601// && OnDisconnected != null)
470 602
603
604
471 if ( 605 if (
472 (args.Reason == NetworkManager.DisconnectType.ClientInitiated 606 (args.Reason == NetworkManager.DisconnectType.ClientInitiated
473 || args.Reason == NetworkManager.DisconnectType.ServerInitiated 607 || args.Reason == NetworkManager.DisconnectType.ServerInitiated
@@ -481,8 +615,8 @@ namespace pCampBot
481 615
482 public void Objects_NewPrim(object sender, PrimEventArgs args) 616 public void Objects_NewPrim(object sender, PrimEventArgs args)
483 { 617 {
484// if (Name.EndsWith("4")) 618 if (!RequestObjectTextures)
485// throw new Exception("Aaargh"); 619 return;
486 620
487 Primitive prim = args.Prim; 621 Primitive prim = args.Prim;
488 622
diff --git a/OpenSim/Tools/pCampBot/BotManager.cs b/OpenSim/Tools/pCampBot/BotManager.cs
index d615b3f..3c1b11e 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 "bot", 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 "bot", false, "quit", "quit", "Shutdown bots and exit", HandleShutdown);
125 "quit", 183
126 "Shutdown bots and exit", 184 m_console.Commands.AddCommand(
127 HandleShutdown); 185 "bot", 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 "bot", 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 "bot", 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 "bot", 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 "bot", false, "sit", "sit", "Sit all bots on the ground.",
213 HandleSit);
214
215 m_console.Commands.AddCommand(
216 "bot", false, "stand", "stand", "Stand all bots.",
217 HandleStand);
218
219 m_console.Commands.AddCommand(
220 "bot", false, "set bots", "set bots <key> <value>", "Set a setting for all bots.", HandleSetBots);
221
222 m_console.Commands.AddCommand(
223 "bot", false, "show regions", "show regions", "Show regions known to bots", HandleShowRegions);
224
225 m_console.Commands.AddCommand(
226 "bot", false, "show bots", "show bots", "Shows the status of all bots", HandleShowBotsStatus);
227
228 m_console.Commands.AddCommand(
229 "bot", 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
diff --git a/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs b/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs
index 9c984be..0ed4825 100644
--- a/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs
+++ b/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs
@@ -32,6 +32,11 @@ namespace pCampBot.Interfaces
32 public interface IBehaviour 32 public interface IBehaviour
33 { 33 {
34 /// <summary> 34 /// <summary>
35 /// Abbreviated name of this behaviour.
36 /// </summary>
37 string AbbreviatedName { get; }
38
39 /// <summary>
35 /// Name of this behaviour. 40 /// Name of this behaviour.
36 /// </summary> 41 /// </summary>
37 string Name { get; } 42 string Name { get; }
@@ -46,6 +51,14 @@ namespace pCampBot.Interfaces
46 void Initialize(Bot bot); 51 void Initialize(Bot bot);
47 52
48 /// <summary> 53 /// <summary>
54 /// Close down this behaviour.
55 /// </summary>
56 /// <remarks>
57 /// This is triggered if a behaviour is removed via explicit command and when a bot is disconnected
58 /// </remarks>
59 void Close();
60
61 /// <summary>
49 /// Action to take when this behaviour is invoked. 62 /// Action to take when this behaviour is invoked.
50 /// </summary> 63 /// </summary>
51 /// <param name="bot"></param> 64 /// <param name="bot"></param>
diff --git a/OpenSim/Tools/pCampBot/pCampBot.cs b/OpenSim/Tools/pCampBot/pCampBot.cs
index 9e82577..781bb00 100644
--- a/OpenSim/Tools/pCampBot/pCampBot.cs
+++ b/OpenSim/Tools/pCampBot/pCampBot.cs
@@ -26,6 +26,7 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.IO;
29using System.Reflection; 30using System.Reflection;
30using System.Threading; 31using System.Threading;
31using log4net; 32using log4net;
@@ -50,30 +51,62 @@ namespace pCampBot
50 { 51 {
51 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 52 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
52 53
54 public const string ConfigFileName = "pCampBot.ini";
55
53 [STAThread] 56 [STAThread]
54 public static void Main(string[] args) 57 public static void Main(string[] args)
55 { 58 {
56 XmlConfigurator.Configure(); 59 XmlConfigurator.Configure();
57 60
58 IConfig config = ParseConfig(args); 61 IConfig commandLineConfig = ParseConfig(args);
59 if (config.Get("help") != null || config.Get("loginuri") == null) 62 if (commandLineConfig.Get("help") != null || commandLineConfig.Get("loginuri") == null)
60 { 63 {
61 Help(); 64 Help();
62 } 65 }
63 else if (config.Get("firstname") == null || config.Get("lastname") == null || config.Get("password") == null) 66 else if (
67 commandLineConfig.Get("firstname") == null
68 || commandLineConfig.Get("lastname") == null
69 || commandLineConfig.Get("password") == null)
64 { 70 {
65 Console.WriteLine("ERROR: You must supply a firstname, lastname and password for the bots."); 71 Console.WriteLine("ERROR: You must supply a firstname, lastname and password for the bots.");
66 } 72 }
67 else 73 else
68 { 74 {
69 int botcount = config.GetInt("botcount", 1);
70
71 BotManager bm = new BotManager(); 75 BotManager bm = new BotManager();
72 76
73 //startup specified number of bots. 1 is the default 77 string iniFilePath = Path.GetFullPath(Path.Combine(Util.configDir(), ConfigFileName));
74 Thread startBotThread = new Thread(o => bm.dobotStartup(botcount, config)); 78
75 startBotThread.Name = "Initial start bots thread"; 79 if (File.Exists(iniFilePath))
76 startBotThread.Start(); 80 {
81 m_log.InfoFormat("[PCAMPBOT]: Reading configuration settings from {0}", iniFilePath);
82
83 IConfigSource configSource = new IniConfigSource(iniFilePath);
84
85 IConfig botManagerConfig = configSource.Configs["BotManager"];
86
87 if (botManagerConfig != null)
88 {
89 bm.LoginDelay = botManagerConfig.GetInt("LoginDelay", bm.LoginDelay);
90 }
91
92 IConfig botConfig = configSource.Configs["Bot"];
93
94 if (botConfig != null)
95 {
96 bm.InitBotSendAgentUpdates
97 = botConfig.GetBoolean("SendAgentUpdates", bm.InitBotSendAgentUpdates);
98 bm.InitBotRequestObjectTextures
99 = botConfig.GetBoolean("RequestObjectTextures", bm.InitBotRequestObjectTextures);
100 }
101 }
102
103 int botcount = commandLineConfig.GetInt("botcount", 1);
104 bool startConnected = commandLineConfig.Get("connect") != null;
105
106 bm.CreateBots(botcount, commandLineConfig);
107
108 if (startConnected)
109 bm.ConnectBots(botcount);
77 110
78 while (true) 111 while (true)
79 { 112 {
@@ -94,8 +127,11 @@ namespace pCampBot
94 //Set up our nifty config.. thanks to nini 127 //Set up our nifty config.. thanks to nini
95 ArgvConfigSource cs = new ArgvConfigSource(args); 128 ArgvConfigSource cs = new ArgvConfigSource(args);
96 129
130 cs.AddSwitch("Startup", "connect", "c");
97 cs.AddSwitch("Startup", "botcount", "n"); 131 cs.AddSwitch("Startup", "botcount", "n");
132 cs.AddSwitch("Startup", "from", "f");
98 cs.AddSwitch("Startup", "loginuri", "l"); 133 cs.AddSwitch("Startup", "loginuri", "l");
134 cs.AddSwitch("Startup", "start", "s");
99 cs.AddSwitch("Startup", "firstname"); 135 cs.AddSwitch("Startup", "firstname");
100 cs.AddSwitch("Startup", "lastname"); 136 cs.AddSwitch("Startup", "lastname");
101 cs.AddSwitch("Startup", "password"); 137 cs.AddSwitch("Startup", "password");
@@ -113,22 +149,27 @@ namespace pCampBot
113 // You can either say no, to not load anything, yes, to load one of the default wearables, a folder 149 // You can either say no, to not load anything, yes, to load one of the default wearables, a folder
114 // name, to load an specific folder, or save, to save an avatar with some already existing wearables 150 // name, to load an specific folder, or save, to save an avatar with some already existing wearables
115 // worn to the folder MyAppearance/FirstName_LastName, and the load it. 151 // worn to the folder MyAppearance/FirstName_LastName, and the load it.
152
116 Console.WriteLine( 153 Console.WriteLine(
117 "usage: pCampBot <-loginuri loginuri> [OPTIONS]\n" + 154 "usage: pCampBot <-loginuri loginuri> [OPTIONS]\n"
118 "Spawns a set of bots to test an OpenSim region\n\n" + 155 + "Spawns a set of bots to test an OpenSim region\n\n"
119 " -l, -loginuri loginuri for sim to log into (required)\n" + 156 + " -l, -loginuri loginuri for grid/standalone (required)\n"
120 " -n, -botcount number of bots to start (default: 1)\n" + 157 + " -s, -start start location for bots (optional). Can be \"last\", \"home\" or a specific location with or without co-ords (e.g. \"region1\" or \"region2/50/30/90\"\n"
121 " -firstname first name for the bots\n" + 158 + " -firstname first name for the bots (required)\n"
122 " -lastname lastname for the bots. Each lastname will have _<bot-number> appended, e.g. Ima Bot_0\n" + 159 + " -lastname lastname for the bots (required). Each lastname will have _<bot-number> appended, e.g. Ima Bot_0\n"
123 " -password password for the bots\n" + 160 + " -password password for the bots (required)\n"
124 " -b, behaviours behaviours for bots. Comma separated, e.g. p,g. Default is p\n" + 161 + " -n, -botcount number of bots to start (default: 1) (optional)\n"
125 " current options are:\n" + 162 + " -f, -from starting number for login bot names, e.g. 25 will login Ima Bot_25, Ima Bot_26, etc. (default: 0) (optional)\n"
126 " p (physics)\n" + 163 + " -c, -connect connect all bots at startup (optional)\n"
127 " g (grab)\n" + 164 + " -b, behaviours behaviours for bots. Comma separated, e.g. p,g. Default is p (required)\n"
128 " t (teleport)\n" + 165 + " current options are:\n"
129// " c (cross)" + 166 + " p (physics - bots constantly move and jump around)\n"
130 " -wear set appearance folder to load from (default: no)\n" + 167 + " g (grab - bots randomly click prims whether set clickable or not)\n"
131 " -h, -help show this message"); 168 + " n (none - bots do nothing)\n"
169 + " t (teleport - bots regularly teleport between regions on the grid)\n"
170// " c (cross)\n" +
171 + " -wear folder from which to load appearance data, \"no\" if there is no such folder (default: no) (optional)\n"
172 + " -h, -help show this message.\n");
132 } 173 }
133 } 174 }
134} 175}