aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Tools/pCampBot
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs19
-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/InventoryDownloadBehaviour.cs121
-rw-r--r--OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs (renamed from OpenSim/Region/Physics/POSPlugin/POSPlugin.cs)40
-rw-r--r--OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs9
-rw-r--r--OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour2.cs (renamed from OpenSim/Region/Physics/OdePlugin/OdePlugin.cs)74
-rw-r--r--OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs9
-rw-r--r--OpenSim/Tools/pCampBot/Behaviours/TwitchyBehaviour.cs (renamed from OpenSim/Tests/Clients/InstantMessage/IMClient.cs)66
-rw-r--r--OpenSim/Tools/pCampBot/Bot.cs348
-rw-r--r--OpenSim/Tools/pCampBot/BotManager.cs903
-rw-r--r--OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs21
-rw-r--r--OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs4
-rw-r--r--OpenSim/Tools/pCampBot/pCampBot.cs89
14 files changed, 1367 insertions, 351 deletions
diff --git a/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs
index 9a9371d..c1ba36b 100644
--- a/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs
+++ b/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs
@@ -29,21 +29,36 @@ 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
35{ 36{
36 public class AbstractBehaviour : IBehaviour 37 public abstract class AbstractBehaviour : IBehaviour
37 { 38 {
39 /// <summary>
40 /// Abbreviated name of this behaviour.
41 /// </summary>
42 public string AbbreviatedName { get; protected set; }
43
38 public string Name { get; protected set; } 44 public string Name { get; protected set; }
39 45
40 public Bot Bot { get; protected set; } 46 public Bot Bot { get; protected set; }
41 47
42 public virtual void Action() {} 48 public abstract void Action();
49
50 public virtual void Interrupt() {}
51
52 protected AutoResetEvent m_interruptEvent = new AutoResetEvent(false);
43 53
44 public virtual void Initialize(Bot bot) 54 public virtual void Initialize(Bot bot)
45 { 55 {
46 Bot = bot; 56 Bot = bot;
47 } 57 }
58
59 public virtual void Close()
60 {
61 Interrupt();
62 }
48 } 63 }
49} 64}
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/InventoryDownloadBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/InventoryDownloadBehaviour.cs
new file mode 100644
index 0000000..521415c
--- /dev/null
+++ b/OpenSim/Tools/pCampBot/Behaviours/InventoryDownloadBehaviour.cs
@@ -0,0 +1,121 @@
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.Diagnostics;
32using System.Threading;
33using System.Linq;
34using pCampBot.Interfaces;
35
36namespace pCampBot
37{
38 /// <summary>
39 /// Do nothing
40 /// </summary>
41 public class InventoryDownloadBehaviour : AbstractBehaviour
42 {
43 private bool m_initialized;
44 private int m_Requests = 2;
45 private Stopwatch m_StopWatch = new Stopwatch();
46 private List<UUID> m_processed = new List<UUID>();
47
48 public InventoryDownloadBehaviour()
49 {
50 AbbreviatedName = "inv";
51 Name = "Inventory";
52 }
53
54 public override void Action()
55 {
56 if (!m_initialized)
57 {
58 m_initialized = true;
59 Bot.Client.Settings.HTTP_INVENTORY = true;
60 Bot.Client.Settings.FETCH_MISSING_INVENTORY = true;
61 Bot.Client.Inventory.FolderUpdated += Inventory_FolderUpdated;
62 Console.WriteLine("Lib owner is " + Bot.Client.Inventory.Store.LibraryRootNode.Data.OwnerID);
63 m_StopWatch.Start();
64 Bot.Client.Inventory.RequestFolderContents(Bot.Client.Inventory.Store.RootFolder.UUID, Bot.Client.Self.AgentID, true, true, InventorySortOrder.ByDate);
65 Bot.Client.Inventory.RequestFolderContents(Bot.Client.Inventory.Store.LibraryRootNode.Data.UUID, Bot.Client.Inventory.Store.LibraryRootNode.Data.OwnerID, true, true, InventorySortOrder.ByDate);
66 }
67
68 Thread.Sleep(1000);
69 Console.WriteLine("Total items: " + Bot.Client.Inventory.Store.Items.Count + "; Total requests: " + m_Requests + "; Time: " + m_StopWatch.Elapsed);
70
71 }
72
73 void Inventory_FolderUpdated(object sender, FolderUpdatedEventArgs e)
74 {
75 if (e.Success)
76 {
77 //Console.WriteLine("Folder " + e.FolderID + " updated");
78 bool fetch = false;
79 lock (m_processed)
80 {
81 if (!m_processed.Contains(e.FolderID))
82 {
83 m_processed.Add(e.FolderID);
84 fetch = true;
85 }
86 }
87
88 if (fetch)
89 {
90 List<InventoryFolder> m_foldersToFetch = new List<InventoryFolder>();
91 foreach (InventoryBase item in Bot.Client.Inventory.Store.GetContents(e.FolderID))
92 {
93 if (item is InventoryFolder)
94 {
95 InventoryFolder f = new InventoryFolder(item.UUID);
96 f.OwnerID = item.OwnerID;
97 m_foldersToFetch.Add(f);
98 }
99 }
100 if (m_foldersToFetch.Count > 0)
101 {
102 m_Requests += 1;
103 Bot.Client.Inventory.RequestFolderContentsCap(m_foldersToFetch, Bot.Client.Network.CurrentSim.Caps.CapabilityURI("FetchInventoryDescendents2"), true, true, InventorySortOrder.ByDate);
104 }
105 }
106
107 if (Bot.Client.Inventory.Store.Items.Count >= 15739)
108 {
109 m_StopWatch.Stop();
110 Console.WriteLine("Stop! Total items: " + Bot.Client.Inventory.Store.Items.Count + "; Total requests: " + m_Requests + "; Time: " + m_StopWatch.Elapsed);
111 }
112 }
113
114 }
115
116 public override void Interrupt()
117 {
118 m_interruptEvent.Set();
119 }
120 }
121} \ No newline at end of file
diff --git a/OpenSim/Region/Physics/POSPlugin/POSPlugin.cs b/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs
index e6b42e6..0d43781 100644
--- a/OpenSim/Region/Physics/POSPlugin/POSPlugin.cs
+++ b/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs
@@ -25,40 +25,36 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using OpenMetaverse;
28using System; 29using System;
29using System.Collections.Generic; 30using System.Collections.Generic;
30using Nini.Config; 31using System.Linq;
31using OpenSim.Framework; 32using pCampBot.Interfaces;
32using OpenSim.Region.Physics.Manager;
33 33
34namespace OpenSim.Region.Physics.POSPlugin 34namespace pCampBot
35{ 35{
36 /// <summary> 36 /// <summary>
37 /// for now will be a very POS physics engine 37 /// Do nothing
38 /// </summary> 38 /// </summary>
39 public class POSPlugin : IPhysicsPlugin 39 public class NoneBehaviour : AbstractBehaviour
40 { 40 {
41 public POSPlugin() 41 public NoneBehaviour()
42 { 42 {
43 } 43 AbbreviatedName = "n";
44 44 Name = "None";
45 public bool Init()
46 {
47 return true;
48 }
49
50 public PhysicsScene GetScene(string sceneIdentifier)
51 {
52 return new POSScene(sceneIdentifier);
53 } 45 }
54 46
55 public string GetName() 47 public override void Action()
56 { 48 {
57 return ("POS"); 49 Bot.Client.Self.Jump(false);
50 Bot.Client.Self.Movement.Stop = true;
51 m_interruptEvent.WaitOne();
52 Bot.Client.Self.Movement.Stop = false;
58 } 53 }
59 54
60 public void Dispose() 55 public override void Interrupt()
61 { 56 {
57 m_interruptEvent.Set();
62 } 58 }
63 } 59 }
64} 60} \ No newline at end of file
diff --git a/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs
index daa7485..98ab931 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,14 @@ 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 base.Close();
87 }
88
80 private string[] readexcuses() 89 private string[] readexcuses()
81 { 90 {
82 string allexcuses = ""; 91 string allexcuses = "";
diff --git a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour2.cs
index 478dd95..1ec2046 100644
--- a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs
+++ b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour2.cs
@@ -25,66 +25,62 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using OpenMetaverse;
28using System; 29using System;
29using System.Collections.Generic; 30using System.Collections.Generic;
31using System.Linq;
30using System.Reflection; 32using System.Reflection;
31using System.Runtime.InteropServices;
32using System.Threading; 33using System.Threading;
33using System.IO;
34using System.Diagnostics;
35using log4net; 34using log4net;
36using Nini.Config; 35using pCampBot.Interfaces;
37using Ode.NET;
38using OpenSim.Framework;
39using OpenSim.Region.Physics.Manager;
40using OpenMetaverse;
41 36
42namespace OpenSim.Region.Physics.OdePlugin 37namespace pCampBot
43{ 38{
44 /// <summary> 39 /// <summary>
45 /// ODE plugin 40 /// This behavior is for the systematic study of some performance improvements made
41 /// for OSCC'13.
42 /// Walk around, sending AgentUpdate packets all the time.
46 /// </summary> 43 /// </summary>
47 public class OdePlugin : IPhysicsPlugin 44 public class PhysicsBehaviour2 : AbstractBehaviour
48 { 45 {
49 private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 46// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
50
51 private OdeScene m_scene;
52 47
53 public bool Init() 48 public PhysicsBehaviour2()
54 { 49 {
55 return true; 50 AbbreviatedName = "ph2";
51 Name = "Physics2";
56 } 52 }
57 53
58 public PhysicsScene GetScene(String sceneIdentifier) 54 private const int TIME_WALKING = 5 * 10; // 5 seconds
55 private int counter = 0;
56
57 public override void Action()
59 { 58 {
60 if (m_scene == null) 59
60 if (counter >= TIME_WALKING)
61 { 61 {
62 // We do this so that OpenSimulator on Windows loads the correct native ODE library depending on whether 62 counter = 0;
63 // it's running as a 32-bit process or a 64-bit one. By invoking LoadLibary here, later DLLImports
64 // will find it already loaded later on.
65 //
66 // This isn't necessary for other platforms (e.g. Mac OSX and Linux) since the DLL used can be
67 // controlled in Ode.NET.dll.config
68 if (Util.IsWindows())
69 Util.LoadArchSpecificWindowsDll("ode.dll");
70 63
71 // Initializing ODE only when a scene is created allows alternative ODE plugins to co-habit (according to 64 Vector3 target = new Vector3(Bot.Random.Next(1, 254), Bot.Random.Next(1, 254), Bot.Client.Self.SimPosition.Z);
72 // http://opensimulator.org/mantis/view.php?id=2750). 65 MyTurnToward(target);
73 d.InitODE();
74
75 m_scene = new OdeScene(sceneIdentifier);
76 }
77 66
78 return m_scene; 67 Bot.Client.Self.Movement.AtPos = true;
79 }
80 68
81 public string GetName() 69 }
82 { 70 else
83 return ("OpenDynamicsEngine"); 71 counter++;
72 // In any case, send an update
73 Bot.Client.Self.Movement.SendUpdate();
84 } 74 }
85 75
86 public void Dispose() 76 private void MyTurnToward(Vector3 target)
87 { 77 {
78 Quaternion between = Vector3.RotationBetween(Vector3.UnitX, Vector3.Normalize(target - Bot.Client.Self.SimPosition));
79 Quaternion rot = between ;
80
81 Bot.Client.Self.Movement.BodyRotation = rot;
82 Bot.Client.Self.Movement.HeadRotation = rot;
83 Bot.Client.Self.Movement.Camera.LookAt(Bot.Client.Self.SimPosition, target);
88 } 84 }
89 } 85 }
90} \ No newline at end of file 86} \ No newline at end of file
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/Tests/Clients/InstantMessage/IMClient.cs b/OpenSim/Tools/pCampBot/Behaviours/TwitchyBehaviour.cs
index e7304a2..7b4639d 100644
--- a/OpenSim/Tests/Clients/InstantMessage/IMClient.cs
+++ b/OpenSim/Tools/pCampBot/Behaviours/TwitchyBehaviour.cs
@@ -25,51 +25,49 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using OpenMetaverse;
28using System; 29using System;
29using System.Collections.Generic; 30using System.Collections.Generic;
30using System.Text; 31using System.Linq;
31using System.Reflection; 32using System.Reflection;
32
33using OpenMetaverse;
34using log4net; 33using log4net;
35using log4net.Appender; 34using pCampBot.Interfaces;
36using log4net.Layout;
37 35
38using OpenSim.Framework; 36namespace pCampBot
39using OpenSim.Services.Interfaces;
40using OpenSim.Services.Connectors.InstantMessage;
41
42namespace OpenSim.Tests.Clients.InstantMessage
43{ 37{
44 public class IMClient 38 /// <summary>
39 /// This behavior is for the systematic study of some performance improvements made
40 /// for OSCC'13.
41 /// Do nothing, but send AgentUpdate packets all the time that have only slightly
42 /// different state. The delta of difference will be filtered by OpenSim early on
43 /// in the packet processing pipeline. These filters did not exist before OSCC'13.
44 /// </summary>
45 public class TwitchyBehaviour : AbstractBehaviour
45 { 46 {
46 private static readonly ILog m_log = 47// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
47 LogManager.GetLogger(
48 MethodBase.GetCurrentMethod().DeclaringType);
49
50 public static void Main(string[] args)
51 {
52 ConsoleAppender consoleAppender = new ConsoleAppender();
53 consoleAppender.Layout =
54 new PatternLayout("%date [%thread] %-5level %logger [%property{NDC}] - %message%newline");
55 log4net.Config.BasicConfigurator.Configure(consoleAppender);
56 48
57 string serverURI = "http://127.0.0.1:8002"; 49 public TwitchyBehaviour()
58 GridInstantMessage im = new GridInstantMessage(); 50 {
59 im.fromAgentID = new Guid(); 51 AbbreviatedName = "tw";
60 im.toAgentID = new Guid(); 52 Name = "Twitchy";
61 im.message = "Hello"; 53 }
62 im.imSessionID = new Guid(); 54
55 private const float TWITCH = 0.0001f;
56 private int direction = 1;
57
58 public override void Action()
59 {
60 Bot.Client.Self.Movement.BodyRotation = new Quaternion(Bot.Client.Self.Movement.BodyRotation.X + direction * TWITCH,
61 Bot.Client.Self.Movement.BodyRotation.Y,
62 Bot.Client.Self.Movement.BodyRotation.Z,
63 Bot.Client.Self.Movement.BodyRotation.W);
63 64
64 bool success = InstantMessageServiceConnector.SendInstantMessage(serverURI, im); 65 //m_log.DebugFormat("[TWITCH]: BodyRot {0}", Bot.Client.Self.Movement.BodyRotation);
66 direction = -direction;
65 67
66 if (success) 68 Bot.Client.Self.Movement.SendUpdate();
67 m_log.InfoFormat("[IM CLIENT]: Successfully IMed {0}", serverURI);
68 else
69 m_log.InfoFormat("[IM CLIENT]: failed to IM {0}", serverURI);
70 69
71 System.Console.WriteLine("\n");
72 } 70 }
73 71
74 } 72 }
75} 73} \ No newline at end of file
diff --git a/OpenSim/Tools/pCampBot/Bot.cs b/OpenSim/Tools/pCampBot/Bot.cs
index daaa3c0..4f28733 100644
--- a/OpenSim/Tools/pCampBot/Bot.cs
+++ b/OpenSim/Tools/pCampBot/Bot.cs
@@ -35,11 +35,13 @@ using System.Timers;
35using log4net; 35using log4net;
36using OpenMetaverse; 36using OpenMetaverse;
37using OpenMetaverse.Assets; 37using OpenMetaverse.Assets;
38using OpenMetaverse.Packets;
38using Nini.Config; 39using Nini.Config;
39using OpenSim.Framework; 40using OpenSim.Framework;
40using OpenSim.Framework.Console; 41using OpenSim.Framework.Console;
41using pCampBot.Interfaces; 42using pCampBot.Interfaces;
42using Timer = System.Timers.Timer; 43using Timer = System.Timers.Timer;
44using PermissionMask = OpenSim.Framework.PermissionMask;
43 45
44namespace pCampBot 46namespace pCampBot
45{ 47{
@@ -55,25 +57,47 @@ namespace pCampBot
55 { 57 {
56 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 58 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
57 59
60 public int PacketDebugLevel
61 {
62 get { return m_packetDebugLevel; }
63 set
64 {
65 if (value == m_packetDebugLevel)
66 return;
67
68 m_packetDebugLevel = value;
69
70 if (Client != null)
71 {
72 if (m_packetDebugLevel <= 0)
73 Client.Network.UnregisterCallback(PacketType.Default, PacketReceivedDebugHandler);
74 else
75 Client.Network.RegisterCallback(PacketType.Default, PacketReceivedDebugHandler, false);
76 }
77 }
78 }
79 private int m_packetDebugLevel;
80
58 public delegate void AnEvent(Bot callbot, EventType someevent); // event delegate for bot events 81 public delegate void AnEvent(Bot callbot, EventType someevent); // event delegate for bot events
59 82
60 /// <summary> 83 /// <summary>
61 /// Bot manager. 84 /// Controls whether bots request textures for the object information they receive
62 /// </summary> 85 /// </summary>
63 public BotManager Manager { get; private set; } 86 public bool RequestObjectTextures { get; set; }
64 87
65 /// <summary> 88 /// <summary>
66 /// Bot config, passed from BotManager. 89 /// Bot manager.
67 /// </summary> 90 /// </summary>
68 private IConfig startupConfig; 91 public BotManager Manager { get; private set; }
69 92
70 /// <summary> 93 /// <summary>
71 /// Behaviours implemented by this bot. 94 /// Behaviours implemented by this bot.
72 /// </summary> 95 /// </summary>
73 /// <remarks> 96 /// <remarks>
74 /// Lock this list before manipulating it. 97 /// Indexed by abbreviated name. There can only be one instance of a particular behaviour.
98 /// Lock this structure before manipulating it.
75 /// </remarks> 99 /// </remarks>
76 public List<IBehaviour> Behaviours { get; private set; } 100 public Dictionary<string, IBehaviour> Behaviours { get; private set; }
77 101
78 /// <summary> 102 /// <summary>
79 /// Objects that the bot has discovered. 103 /// Objects that the bot has discovered.
@@ -96,11 +120,35 @@ namespace pCampBot
96 /// </summary> 120 /// </summary>
97 public ConnectionState ConnectionState { get; private set; } 121 public ConnectionState ConnectionState { get; private set; }
98 122
123 public List<Simulator> Simulators
124 {
125 get
126 {
127 lock (Client.Network.Simulators)
128 return new List<Simulator>(Client.Network.Simulators);
129 }
130 }
131
132 /// <summary>
133 /// The number of connections that this bot has to different simulators.
134 /// </summary>
135 /// <value>Includes both root and child connections.</value>
136 public int SimulatorsCount
137 {
138 get
139 {
140 lock (Client.Network.Simulators)
141 return Client.Network.Simulators.Count;
142 }
143 }
144
99 public string FirstName { get; private set; } 145 public string FirstName { get; private set; }
100 public string LastName { get; private set; } 146 public string LastName { get; private set; }
101 public string Name { get; private set; } 147 public string Name { get; private set; }
102 public string Password { get; private set; } 148 public string Password { get; private set; }
103 public string LoginUri { get; private set; } 149 public string LoginUri { get; private set; }
150 public string StartLocation { get; private set; }
151
104 public string saveDir; 152 public string saveDir;
105 public string wear; 153 public string wear;
106 154
@@ -136,94 +184,180 @@ namespace pCampBot
136 /// <param name="behaviours"></param> 184 /// <param name="behaviours"></param>
137 public Bot( 185 public Bot(
138 BotManager bm, List<IBehaviour> behaviours, 186 BotManager bm, List<IBehaviour> behaviours,
139 string firstName, string lastName, string password, string loginUri) 187 string firstName, string lastName, string password, string startLocation, string loginUri)
140 { 188 {
141 ConnectionState = ConnectionState.Disconnected; 189 ConnectionState = ConnectionState.Disconnected;
142 190
143 behaviours.ForEach(b => b.Initialize(this)); 191 Random = new Random(bm.Rng.Next());
144
145 Client = new GridClient();
146
147 Random = new Random(Environment.TickCount);// We do stuff randomly here
148 FirstName = firstName; 192 FirstName = firstName;
149 LastName = lastName; 193 LastName = lastName;
150 Name = string.Format("{0} {1}", FirstName, LastName); 194 Name = string.Format("{0} {1}", FirstName, LastName);
151 Password = password; 195 Password = password;
152 LoginUri = loginUri; 196 LoginUri = loginUri;
197 StartLocation = startLocation;
153 198
154 Manager = bm; 199 Manager = bm;
155 startupConfig = bm.Config;
156 readconfig();
157 200
158 Behaviours = behaviours; 201 Behaviours = new Dictionary<string, IBehaviour>();
202 foreach (IBehaviour behaviour in behaviours)
203 AddBehaviour(behaviour);
204
205 // Only calling for use as a template.
206 CreateLibOmvClient();
207 }
208
209 public bool TryGetBehaviour(string abbreviatedName, out IBehaviour behaviour)
210 {
211 lock (Behaviours)
212 return Behaviours.TryGetValue(abbreviatedName, out behaviour);
213 }
214
215 public bool AddBehaviour(IBehaviour behaviour)
216 {
217 Dictionary<string, IBehaviour> updatedBehaviours = new Dictionary<string, IBehaviour>(Behaviours);
218
219 if (!updatedBehaviours.ContainsKey(behaviour.AbbreviatedName))
220 {
221 behaviour.Initialize(this);
222 updatedBehaviours.Add(behaviour.AbbreviatedName, behaviour);
223 Behaviours = updatedBehaviours;
224
225 return true;
226 }
227
228 return false;
229 }
230
231 public bool RemoveBehaviour(string abbreviatedName)
232 {
233 if (Behaviours.Count <= 0)
234 return false;
235
236 Dictionary<string, IBehaviour> updatedBehaviours = new Dictionary<string, IBehaviour>(Behaviours);
237 IBehaviour behaviour;
238
239 if (!updatedBehaviours.TryGetValue(abbreviatedName, out behaviour))
240 return false;
241
242 updatedBehaviours.Remove(abbreviatedName);
243 Behaviours = updatedBehaviours;
244
245 behaviour.Close();
246
247 return true;
248 }
249
250 private void CreateLibOmvClient()
251 {
252 GridClient newClient = new GridClient();
253
254 if (Client != null)
255 {
256 // Remove any registered debug handlers
257 Client.Network.UnregisterCallback(PacketType.Default, PacketReceivedDebugHandler);
258
259 newClient.Settings.LOGIN_SERVER = Client.Settings.LOGIN_SERVER;
260 newClient.Settings.ALWAYS_DECODE_OBJECTS = Client.Settings.ALWAYS_DECODE_OBJECTS;
261 newClient.Settings.AVATAR_TRACKING = Client.Settings.AVATAR_TRACKING;
262 newClient.Settings.OBJECT_TRACKING = Client.Settings.OBJECT_TRACKING;
263 newClient.Settings.SEND_AGENT_THROTTLE = Client.Settings.SEND_AGENT_THROTTLE;
264 newClient.Settings.SEND_AGENT_UPDATES = Client.Settings.SEND_AGENT_UPDATES;
265 newClient.Settings.SEND_PINGS = Client.Settings.SEND_PINGS;
266 newClient.Settings.STORE_LAND_PATCHES = Client.Settings.STORE_LAND_PATCHES;
267 newClient.Settings.USE_ASSET_CACHE = Client.Settings.USE_ASSET_CACHE;
268 newClient.Settings.MULTIPLE_SIMS = Client.Settings.MULTIPLE_SIMS;
269 newClient.Throttle.Asset = Client.Throttle.Asset;
270 newClient.Throttle.Land = Client.Throttle.Land;
271 newClient.Throttle.Task = Client.Throttle.Task;
272 newClient.Throttle.Texture = Client.Throttle.Texture;
273 newClient.Throttle.Wind = Client.Throttle.Wind;
274 newClient.Throttle.Total = Client.Throttle.Total;
275 }
276 else
277 {
278 newClient.Settings.LOGIN_SERVER = LoginUri;
279 newClient.Settings.ALWAYS_DECODE_OBJECTS = false;
280 newClient.Settings.AVATAR_TRACKING = false;
281 newClient.Settings.OBJECT_TRACKING = false;
282 newClient.Settings.SEND_AGENT_THROTTLE = true;
283 newClient.Settings.SEND_PINGS = true;
284 newClient.Settings.STORE_LAND_PATCHES = false;
285 newClient.Settings.USE_ASSET_CACHE = false;
286 newClient.Settings.MULTIPLE_SIMS = true;
287 newClient.Throttle.Asset = 100000;
288 newClient.Throttle.Land = 100000;
289 newClient.Throttle.Task = 100000;
290 newClient.Throttle.Texture = 100000;
291 newClient.Throttle.Wind = 100000;
292 newClient.Throttle.Total = 400000;
293 }
294
295 newClient.Network.LoginProgress += Network_LoginProgress;
296 newClient.Network.SimConnected += Network_SimConnected;
297 newClient.Network.SimDisconnected += Network_SimDisconnected;
298 newClient.Network.Disconnected += Network_OnDisconnected;
299 newClient.Objects.ObjectUpdate += Objects_NewPrim;
300
301 if (m_packetDebugLevel > 0)
302 newClient.Network.RegisterCallback(PacketType.Default, PacketReceivedDebugHandler);
303
304 Client = newClient;
159 } 305 }
160 306
161 //We do our actions here. This is where one would 307 //We do our actions here. This is where one would
162 //add additional steps and/or things the bot should do 308 //add additional steps and/or things the bot should do
163 private void Action() 309 private void Action()
164 { 310 {
165 while (true) 311 while (ConnectionState == ConnectionState.Connected)
166 lock (Behaviours) 312 {
167 Behaviours.ForEach( 313 foreach (IBehaviour behaviour in Behaviours.Values)
168 b => 314 {
169 { 315// Thread.Sleep(Random.Next(3000, 10000));
170 Thread.Sleep(Random.Next(3000, 10000)); 316
171 317 // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType());
172 // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType()); 318 behaviour.Action();
173 b.Action(); 319 }
174 } 320 }
175 );
176 }
177 321
178 /// <summary> 322 foreach (IBehaviour b in Behaviours.Values)
179 /// Read the Nini config and initialize 323 b.Close();
180 /// </summary>
181 public void readconfig()
182 {
183 wear = startupConfig.GetString("wear", "no");
184 } 324 }
185 325
186 /// <summary> 326 /// <summary>
187 /// Tells LibSecondLife to logout and disconnect. Raises the disconnect events once it finishes. 327 /// Tells LibSecondLife to logout and disconnect. Raises the disconnect events once it finishes.
188 /// </summary> 328 /// </summary>
189 public void shutdown() 329 public void Disconnect()
190 { 330 {
191 ConnectionState = ConnectionState.Disconnecting; 331 ConnectionState = ConnectionState.Disconnecting;
192 332
193 if (m_actionThread != null) 333 foreach (IBehaviour behaviour in Behaviours.Values)
194 m_actionThread.Abort(); 334 behaviour.Close();
195 335
196 Client.Network.Logout(); 336 Client.Network.Logout();
197 } 337 }
198 338
339 public void Connect()
340 {
341 Thread connectThread = new Thread(ConnectInternal);
342 connectThread.Name = Name;
343 connectThread.IsBackground = true;
344
345 connectThread.Start();
346 }
347
199 /// <summary> 348 /// <summary>
200 /// This is the bot startup loop. 349 /// This is the bot startup loop.
201 /// </summary> 350 /// </summary>
202 public void startup() 351 private void ConnectInternal()
203 { 352 {
204 Client.Settings.LOGIN_SERVER = LoginUri;
205 Client.Settings.ALWAYS_DECODE_OBJECTS = false;
206 Client.Settings.AVATAR_TRACKING = false;
207 Client.Settings.OBJECT_TRACKING = false;
208 Client.Settings.SEND_AGENT_THROTTLE = true;
209 Client.Settings.SEND_PINGS = true;
210 Client.Settings.STORE_LAND_PATCHES = false;
211 Client.Settings.USE_ASSET_CACHE = false;
212 Client.Settings.MULTIPLE_SIMS = true;
213 Client.Throttle.Asset = 100000;
214 Client.Throttle.Land = 100000;
215 Client.Throttle.Task = 100000;
216 Client.Throttle.Texture = 100000;
217 Client.Throttle.Wind = 100000;
218 Client.Throttle.Total = 400000;
219 Client.Network.LoginProgress += this.Network_LoginProgress;
220 Client.Network.SimConnected += this.Network_SimConnected;
221 Client.Network.Disconnected += this.Network_OnDisconnected;
222 Client.Objects.ObjectUpdate += Objects_NewPrim;
223
224 ConnectionState = ConnectionState.Connecting; 353 ConnectionState = ConnectionState.Connecting;
225 354
226 if (Client.Network.Login(FirstName, LastName, Password, "pCampBot", "Your name")) 355 // Current create a new client on each connect. libomv doesn't seem to process new sim
356 // information (e.g. EstablishAgentCommunication events) if connecting after a disceonnect with the same
357 // client
358 CreateLibOmvClient();
359
360 if (Client.Network.Login(FirstName, LastName, Password, "pCampBot", StartLocation, "pCampBot"))
227 { 361 {
228 ConnectionState = ConnectionState.Connected; 362 ConnectionState = ConnectionState.Connected;
229 363
@@ -234,7 +368,6 @@ namespace pCampBot
234// OnConnected(this, EventType.CONNECTED); 368// OnConnected(this, EventType.CONNECTED);
235 if (wear == "save") 369 if (wear == "save")
236 { 370 {
237 Client.Appearance.SetPreviousAppearance();
238 SaveDefaultAppearance(); 371 SaveDefaultAppearance();
239 } 372 }
240 else if (wear != "no") 373 else if (wear != "no")
@@ -267,6 +400,30 @@ namespace pCampBot
267 } 400 }
268 } 401 }
269 402
403 /// <summary>
404 /// Sit this bot on the ground.
405 /// </summary>
406 public void SitOnGround()
407 {
408 if (ConnectionState == ConnectionState.Connected)
409 Client.Self.SitOnGround();
410 }
411
412 /// <summary>
413 /// Stand this bot
414 /// </summary>
415 public void Stand()
416 {
417 if (ConnectionState == ConnectionState.Connected)
418 {
419 // Unlike sit on ground, here libomv checks whether we have SEND_AGENT_UPDATES enabled.
420 bool prevUpdatesSetting = Client.Settings.SEND_AGENT_UPDATES;
421 Client.Settings.SEND_AGENT_UPDATES = true;
422 Client.Self.Stand();
423 Client.Settings.SEND_AGENT_UPDATES = prevUpdatesSetting;
424 }
425 }
426
270 public void SaveDefaultAppearance() 427 public void SaveDefaultAppearance()
271 { 428 {
272 saveDir = "MyAppearance/" + FirstName + "_" + LastName; 429 saveDir = "MyAppearance/" + FirstName + "_" + LastName;
@@ -362,7 +519,7 @@ namespace pCampBot
362 asset.Encode(); 519 asset.Encode();
363 transid = Client.Assets.RequestUpload(asset,true); 520 transid = Client.Assets.RequestUpload(asset,true);
364 Client.Inventory.RequestCreateItem(clothfolder.UUID, "MyClothing" + i.ToString(), "MyClothing", AssetType.Clothing, 521 Client.Inventory.RequestCreateItem(clothfolder.UUID, "MyClothing" + i.ToString(), "MyClothing", AssetType.Clothing,
365 transid, InventoryType.Wearable, asset.WearableType, PermissionMask.All, delegate(bool success, InventoryItem item) 522 transid, InventoryType.Wearable, asset.WearableType, (OpenMetaverse.PermissionMask)PermissionMask.All, delegate(bool success, InventoryItem item)
366 { 523 {
367 if (success) 524 if (success)
368 { 525 {
@@ -386,7 +543,7 @@ namespace pCampBot
386 asset.Encode(); 543 asset.Encode();
387 transid = Client.Assets.RequestUpload(asset,true); 544 transid = Client.Assets.RequestUpload(asset,true);
388 Client.Inventory.RequestCreateItem(clothfolder.UUID, "MyBodyPart" + i.ToString(), "MyBodyPart", AssetType.Bodypart, 545 Client.Inventory.RequestCreateItem(clothfolder.UUID, "MyBodyPart" + i.ToString(), "MyBodyPart", AssetType.Bodypart,
389 transid, InventoryType.Wearable, asset.WearableType, PermissionMask.All, delegate(bool success, InventoryItem item) 546 transid, InventoryType.Wearable, asset.WearableType, (OpenMetaverse.PermissionMask)PermissionMask.All, delegate(bool success, InventoryItem item)
390 { 547 {
391 if (success) 548 if (success)
392 { 549 {
@@ -450,7 +607,13 @@ namespace pCampBot
450 public void Network_SimConnected(object sender, SimConnectedEventArgs args) 607 public void Network_SimConnected(object sender, SimConnectedEventArgs args)
451 { 608 {
452 m_log.DebugFormat( 609 m_log.DebugFormat(
453 "[BOT]: Bot {0} connected to {1} at {2}", Name, args.Simulator.Name, args.Simulator.IPEndPoint); 610 "[BOT]: Bot {0} connected to region {1} at {2}", Name, args.Simulator.Name, args.Simulator.IPEndPoint);
611 }
612
613 public void Network_SimDisconnected(object sender, SimDisconnectedEventArgs args)
614 {
615 m_log.DebugFormat(
616 "[BOT]: Bot {0} disconnected from region {1} at {2}", Name, args.Simulator.Name, args.Simulator.IPEndPoint);
454 } 617 }
455 618
456 public void Network_OnDisconnected(object sender, DisconnectedEventArgs args) 619 public void Network_OnDisconnected(object sender, DisconnectedEventArgs args)
@@ -458,7 +621,7 @@ namespace pCampBot
458 ConnectionState = ConnectionState.Disconnected; 621 ConnectionState = ConnectionState.Disconnected;
459 622
460 m_log.DebugFormat( 623 m_log.DebugFormat(
461 "[BOT]: Bot {0} disconnected reason {1}, message {2}", Name, args.Reason, args.Message); 624 "[BOT]: Bot {0} disconnected from grid, reason {1}, message {2}", Name, args.Reason, args.Message);
462 625
463// m_log.ErrorFormat("Fired Network_OnDisconnected"); 626// m_log.ErrorFormat("Fired Network_OnDisconnected");
464 627
@@ -467,6 +630,8 @@ namespace pCampBot
467// || args.Reason == NetworkManager.DisconnectType.NetworkTimeout) 630// || args.Reason == NetworkManager.DisconnectType.NetworkTimeout)
468// && OnDisconnected != null) 631// && OnDisconnected != null)
469 632
633
634
470 if ( 635 if (
471 (args.Reason == NetworkManager.DisconnectType.ClientInitiated 636 (args.Reason == NetworkManager.DisconnectType.ClientInitiated
472 || args.Reason == NetworkManager.DisconnectType.ServerInitiated 637 || args.Reason == NetworkManager.DisconnectType.ServerInitiated
@@ -480,8 +645,8 @@ namespace pCampBot
480 645
481 public void Objects_NewPrim(object sender, PrimEventArgs args) 646 public void Objects_NewPrim(object sender, PrimEventArgs args)
482 { 647 {
483// if (Name.EndsWith("4")) 648 if (!RequestObjectTextures)
484// throw new Exception("Aaargh"); 649 return;
485 650
486 Primitive prim = args.Prim; 651 Primitive prim = args.Prim;
487 652
@@ -494,7 +659,7 @@ namespace pCampBot
494 { 659 {
495 if (prim.Textures.DefaultTexture.TextureID != UUID.Zero) 660 if (prim.Textures.DefaultTexture.TextureID != UUID.Zero)
496 { 661 {
497 GetTexture(prim.Textures.DefaultTexture.TextureID); 662 GetTextureOrMesh(prim.Textures.DefaultTexture.TextureID, true);
498 } 663 }
499 664
500 for (int i = 0; i < prim.Textures.FaceTextures.Length; i++) 665 for (int i = 0; i < prim.Textures.FaceTextures.Length; i++)
@@ -506,32 +671,56 @@ namespace pCampBot
506 UUID textureID = prim.Textures.FaceTextures[i].TextureID; 671 UUID textureID = prim.Textures.FaceTextures[i].TextureID;
507 672
508 if (textureID != UUID.Zero) 673 if (textureID != UUID.Zero)
509 GetTexture(textureID); 674 GetTextureOrMesh(textureID, true);
510 } 675 }
511 } 676 }
512 } 677 }
513 678
514 if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero) 679 if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero)
515 GetTexture(prim.Sculpt.SculptTexture); 680 {
681 bool mesh = (prim.Sculpt.Type == SculptType.Mesh);
682 GetTextureOrMesh(prim.Sculpt.SculptTexture, !mesh);
683 }
516 } 684 }
517 } 685 }
518 686
519 private void GetTexture(UUID textureID) 687 private void GetTextureOrMesh(UUID assetID, bool texture)
520 { 688 {
521 lock (Manager.AssetsReceived) 689 lock (Manager.AssetsReceived)
522 { 690 {
523 // Don't request assets more than once. 691 // Don't request assets more than once.
524 if (Manager.AssetsReceived.ContainsKey(textureID)) 692 if (Manager.AssetsReceived.ContainsKey(assetID))
525 return; 693 return;
526 694
527 Manager.AssetsReceived[textureID] = false; 695 Manager.AssetsReceived[assetID] = false;
528 Client.Assets.RequestImage(textureID, ImageType.Normal, Asset_TextureCallback_Texture); 696 }
697
698 try
699 {
700 if (texture)
701 Client.Assets.RequestImage(assetID, ImageType.Normal, Asset_TextureCallback_Texture);
702 else
703 Client.Assets.RequestMesh(assetID, Asset_MeshCallback);
704 }
705 catch (Exception e)
706 {
707 m_log.Warn(string.Format("Error requesting {0} {1}", texture ? "texture" : "mesh", assetID), e);
529 } 708 }
530 } 709 }
531 710
532 public void Asset_TextureCallback_Texture(TextureRequestState state, AssetTexture assetTexture) 711 public void Asset_TextureCallback_Texture(TextureRequestState state, AssetTexture assetTexture)
533 { 712 {
534 //TODO: Implement texture saving and applying 713 if (state == TextureRequestState.Finished)
714 {
715 lock (Manager.AssetsReceived)
716 Manager.AssetsReceived[assetTexture.AssetID] = true;
717 }
718 }
719
720 private void Asset_MeshCallback(bool success, AssetMesh assetMesh)
721 {
722 lock (Manager.AssetsReceived)
723 Manager.AssetsReceived[assetMesh.AssetID] = success;
535 } 724 }
536 725
537 public void Asset_ReceivedCallback(AssetDownload transfer, Asset asset) 726 public void Asset_ReceivedCallback(AssetDownload transfer, Asset asset)
@@ -544,5 +733,16 @@ namespace pCampBot
544// SaveAsset((AssetWearable) asset); 733// SaveAsset((AssetWearable) asset);
545// } 734// }
546 } 735 }
736
737 private void PacketReceivedDebugHandler(object o, PacketReceivedEventArgs args)
738 {
739 Packet p = args.Packet;
740 Header h = p.Header;
741 Simulator s = args.Simulator;
742
743 m_log.DebugFormat(
744 "[BOT]: Bot {0} received from {1} packet {2} #{3}, rel {4}, res {5}",
745 Name, s.Name, p.Type, h.Sequence, h.Reliable, h.Resent);
746 }
547 } 747 }
548} 748}
diff --git a/OpenSim/Tools/pCampBot/BotManager.cs b/OpenSim/Tools/pCampBot/BotManager.cs
index d615b3f..0af9592 100644
--- a/OpenSim/Tools/pCampBot/BotManager.cs
+++ b/OpenSim/Tools/pCampBot/BotManager.cs
@@ -38,10 +38,19 @@ using log4net.Repository;
38using Nini.Config; 38using Nini.Config;
39using OpenSim.Framework; 39using OpenSim.Framework;
40using OpenSim.Framework.Console; 40using OpenSim.Framework.Console;
41using OpenSim.Framework.Monitoring;
41using pCampBot.Interfaces; 42using pCampBot.Interfaces;
42 43
43namespace pCampBot 44namespace pCampBot
44{ 45{
46 public enum BotManagerBotConnectingState
47 {
48 Initializing,
49 Ready,
50 Connecting,
51 Disconnecting
52 }
53
45 /// <summary> 54 /// <summary>
46 /// Thread/Bot manager for the application 55 /// Thread/Bot manager for the application
47 /// </summary> 56 /// </summary>
@@ -52,6 +61,16 @@ namespace pCampBot
52 public const int DefaultLoginDelay = 5000; 61 public const int DefaultLoginDelay = 5000;
53 62
54 /// <summary> 63 /// <summary>
64 /// Is pCampbot ready to connect or currently in the process of connecting or disconnecting bots?
65 /// </summary>
66 public BotManagerBotConnectingState BotConnectingState { get; private set; }
67
68 /// <summary>
69 /// Used to control locking as we can't lock an enum.
70 /// </summary>
71 private object BotConnectingStateChangeObject = new object();
72
73 /// <summary>
55 /// Delay between logins of multiple bots. 74 /// Delay between logins of multiple bots.
56 /// </summary> 75 /// </summary>
57 /// <remarks>TODO: This value needs to be configurable by a command line argument.</remarks> 76 /// <remarks>TODO: This value needs to be configurable by a command line argument.</remarks>
@@ -63,19 +82,24 @@ namespace pCampBot
63 protected CommandConsole m_console; 82 protected CommandConsole m_console;
64 83
65 /// <summary> 84 /// <summary>
66 /// Created bots, whether active or inactive. 85 /// Controls whether bots start out sending agent updates on connection.
67 /// </summary> 86 /// </summary>
68 protected List<Bot> m_lBot; 87 public bool InitBotSendAgentUpdates { get; set; }
69 88
70 /// <summary> 89 /// <summary>
71 /// Random number generator. 90 /// Controls whether bots request textures for the object information they receive
72 /// </summary> 91 /// </summary>
73 public Random Rng { get; private set; } 92 public bool InitBotRequestObjectTextures { get; set; }
74 93
75 /// <summary> 94 /// <summary>
76 /// Overall configuration. 95 /// Created bots, whether active or inactive.
77 /// </summary> 96 /// </summary>
78 public IConfig Config { get; private set; } 97 protected List<Bot> m_bots;
98
99 /// <summary>
100 /// Random number generator.
101 /// </summary>
102 public Random Rng { get; private set; }
79 103
80 /// <summary> 104 /// <summary>
81 /// Track the assets we have and have not received so we don't endlessly repeat requests. 105 /// Track the assets we have and have not received so we don't endlessly repeat requests.
@@ -88,10 +112,68 @@ namespace pCampBot
88 public Dictionary<ulong, GridRegion> RegionsKnown { get; private set; } 112 public Dictionary<ulong, GridRegion> RegionsKnown { get; private set; }
89 113
90 /// <summary> 114 /// <summary>
115 /// First name for bots
116 /// </summary>
117 private string m_firstName;
118
119 /// <summary>
120 /// Last name stem for bots
121 /// </summary>
122 private string m_lastNameStem;
123
124 /// <summary>
125 /// Password for bots
126 /// </summary>
127 private string m_password;
128
129 /// <summary>
130 /// Login URI for bots.
131 /// </summary>
132 private string m_loginUri;
133
134 /// <summary>
135 /// Start location for bots.
136 /// </summary>
137 private string m_startUri;
138
139 /// <summary>
140 /// Postfix bot number at which bot sequence starts.
141 /// </summary>
142 private int m_fromBotNumber;
143
144 /// <summary>
145 /// Wear setting for bots.
146 /// </summary>
147 private string m_wearSetting;
148
149 /// <summary>
150 /// Behaviour switches for bots.
151 /// </summary>
152 private HashSet<string> m_defaultBehaviourSwitches = new HashSet<string>();
153
154 /// <summary>
155 /// Collects general information on this server (which reveals this to be a misnamed class).
156 /// </summary>
157 private ServerStatsCollector m_serverStatsCollector;
158
159 /// <summary>
91 /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data 160 /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data
92 /// </summary> 161 /// </summary>
93 public BotManager() 162 public BotManager()
94 { 163 {
164 // We set this to avoid issues with bots running out of HTTP connections if many are run from a single machine
165 // to multiple regions.
166 Settings.MAX_HTTP_CONNECTIONS = int.MaxValue;
167
168// System.Threading.ThreadPool.SetMaxThreads(600, 240);
169//
170// int workerThreads, iocpThreads;
171// System.Threading.ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads);
172// Console.WriteLine("ThreadPool.GetMaxThreads {0} {1}", workerThreads, iocpThreads);
173
174 InitBotSendAgentUpdates = true;
175 InitBotRequestObjectTextures = true;
176
95 LoginDelay = DefaultLoginDelay; 177 LoginDelay = DefaultLoginDelay;
96 178
97 Rng = new Random(Environment.TickCount); 179 Rng = new Random(Environment.TickCount);
@@ -117,30 +199,84 @@ namespace pCampBot
117 } 199 }
118 } 200 }
119 201
120 m_console.Commands.AddCommand("bot", false, "shutdown", 202 m_console.Commands.AddCommand(
121 "shutdown", 203 "Bots", false, "shutdown", "shutdown", "Shutdown bots and exit", HandleShutdown);
122 "Shutdown bots and exit", HandleShutdown); 204
123 205 m_console.Commands.AddCommand(
124 m_console.Commands.AddCommand("bot", false, "quit", 206 "Bots", false, "quit", "quit", "Shutdown bots and exit", HandleShutdown);
125 "quit", 207
126 "Shutdown bots and exit", 208 m_console.Commands.AddCommand(
127 HandleShutdown); 209 "Bots", false, "connect", "connect [<n>]", "Connect bots",
128 210 "If an <n> is given, then the first <n> disconnected bots by postfix number are connected.\n"
129 m_console.Commands.AddCommand("bot", false, "show regions", 211 + "If no <n> is given, then all currently disconnected bots are connected.",
130 "show regions", 212 HandleConnect);
131 "Show regions known to bots", 213
132 HandleShowRegions); 214 m_console.Commands.AddCommand(
133 215 "Bots", false, "disconnect", "disconnect [<n>]", "Disconnect bots",
134 m_console.Commands.AddCommand("bot", false, "show bots", 216 "Disconnecting bots will interupt any bot connection process, including connection on startup.\n"
135 "show bots", 217 + "If an <n> is given, then the last <n> connected bots by postfix number are disconnected.\n"
136 "Shows the status of all bots", 218 + "If no <n> is given, then all currently connected bots are disconnected.",
137 HandleShowStatus); 219 HandleDisconnect);
138 220
139// m_console.Commands.AddCommand("bot", false, "add bots", 221 m_console.Commands.AddCommand(
140// "add bots <number>", 222 "Bots", false, "add behaviour", "add behaviour <abbreviated-name> [<bot-number>]",
141// "Add more bots", HandleAddBots); 223 "Add a behaviour to a bot",
142 224 "If no bot number is specified then behaviour is added to all bots.\n"
143 m_lBot = new List<Bot>(); 225 + "Can be performed on connected or disconnected bots.",
226 HandleAddBehaviour);
227
228 m_console.Commands.AddCommand(
229 "Bots", false, "remove behaviour", "remove behaviour <abbreviated-name> [<bot-number>]",
230 "Remove a behaviour from a bot",
231 "If no bot number is specified then behaviour is added to all bots.\n"
232 + "Can be performed on connected or disconnected bots.",
233 HandleRemoveBehaviour);
234
235 m_console.Commands.AddCommand(
236 "Bots", false, "sit", "sit", "Sit all bots on the ground.",
237 HandleSit);
238
239 m_console.Commands.AddCommand(
240 "Bots", false, "stand", "stand", "Stand all bots.",
241 HandleStand);
242
243 m_console.Commands.AddCommand(
244 "Bots", false, "set bots", "set bots <key> <value>", "Set a setting for all bots.", HandleSetBots);
245
246 m_console.Commands.AddCommand(
247 "Bots", false, "show regions", "show regions", "Show regions known to bots", HandleShowRegions);
248
249 m_console.Commands.AddCommand(
250 "Bots", false, "show bots", "show bots", "Shows the status of all bots.", HandleShowBotsStatus);
251
252 m_console.Commands.AddCommand(
253 "Bots", false, "show bot", "show bot <bot-number>",
254 "Shows the detailed status and settings of a particular bot.", HandleShowBotStatus);
255
256 m_console.Commands.AddCommand(
257 "Debug",
258 false,
259 "debug lludp packet",
260 "debug lludp packet <level> <avatar-first-name> <avatar-last-name>",
261 "Turn on received packet logging.",
262 "If level > 0 then all received packets that are not duplicates are logged.\n"
263 + "If level <= 0 then no received packets are logged.",
264 HandleDebugLludpPacketCommand);
265
266 m_console.Commands.AddCommand(
267 "Bots", false, "show status", "show status", "Shows pCampbot status.", HandleShowStatus);
268
269 m_bots = new List<Bot>();
270
271 Watchdog.Enabled = true;
272 StatsManager.RegisterConsoleCommands(m_console);
273
274 m_serverStatsCollector = new ServerStatsCollector();
275 m_serverStatsCollector.Initialise(null);
276 m_serverStatsCollector.Enabled = true;
277 m_serverStatsCollector.Start();
278
279 BotConnectingState = BotManagerBotConnectingState.Ready;
144 } 280 }
145 281
146 /// <summary> 282 /// <summary>
@@ -148,74 +284,195 @@ namespace pCampBot
148 /// </summary> 284 /// </summary>
149 /// <param name="botcount">How many bots to start up</param> 285 /// <param name="botcount">How many bots to start up</param>
150 /// <param name="cs">The configuration for the bots to use</param> 286 /// <param name="cs">The configuration for the bots to use</param>
151 public void dobotStartup(int botcount, IConfig cs) 287 public void CreateBots(int botcount, IConfig startupConfig)
152 { 288 {
153 Config = cs; 289 m_firstName = startupConfig.GetString("firstname");
290 m_lastNameStem = startupConfig.GetString("lastname");
291 m_password = startupConfig.GetString("password");
292 m_loginUri = startupConfig.GetString("loginuri");
293 m_fromBotNumber = startupConfig.GetInt("from", 0);
294 m_wearSetting = startupConfig.GetString("wear", "no");
154 295
155 string firstName = cs.GetString("firstname"); 296 m_startUri = ParseInputStartLocationToUri(startupConfig.GetString("start", "last"));
156 string lastNameStem = cs.GetString("lastname");
157 string password = cs.GetString("password");
158 string loginUri = cs.GetString("loginuri");
159 297
160 HashSet<string> behaviourSwitches = new HashSet<string>();
161 Array.ForEach<string>( 298 Array.ForEach<string>(
162 cs.GetString("behaviours", "p").Split(new char[] { ',' }), b => behaviourSwitches.Add(b)); 299 startupConfig.GetString("behaviours", "p").Split(new char[] { ',' }), b => m_defaultBehaviourSwitches.Add(b));
163 300
164 MainConsole.Instance.OutputFormat( 301 for (int i = 0; i < botcount; i++)
165 "[BOT MANAGER]: Starting {0} bots connecting to {1}, named {2} {3}_<n>", 302 {
166 botcount, 303 lock (m_bots)
167 loginUri, 304 {
168 firstName, 305 string lastName = string.Format("{0}_{1}", m_lastNameStem, i + m_fromBotNumber);
169 lastNameStem); 306
307 CreateBot(
308 this,
309 CreateBehavioursFromAbbreviatedNames(m_defaultBehaviourSwitches),
310 m_firstName, lastName, m_password, m_loginUri, m_startUri, m_wearSetting);
311 }
312 }
313 }
170 314
171 MainConsole.Instance.OutputFormat("[BOT MANAGER]: Delay between logins is {0}ms", LoginDelay); 315 private List<IBehaviour> CreateBehavioursFromAbbreviatedNames(HashSet<string> abbreviatedNames)
316 {
317 // We must give each bot its own list of instantiated behaviours since they store state.
318 List<IBehaviour> behaviours = new List<IBehaviour>();
172 319
173 for (int i = 0; i < botcount; i++) 320 // Hard-coded for now
321 foreach (string abName in abbreviatedNames)
174 { 322 {
175 string lastName = string.Format("{0}_{1}", lastNameStem, i); 323 IBehaviour newBehaviour = null;
176 324
177 // We must give each bot its own list of instantiated behaviours since they store state. 325 if (abName == "c")
178 List<IBehaviour> behaviours = new List<IBehaviour>(); 326 newBehaviour = new CrossBehaviour();
179 327
180 // Hard-coded for now 328 if (abName == "g")
181 if (behaviourSwitches.Contains("p")) 329 newBehaviour = new GrabbingBehaviour();
182 behaviours.Add(new PhysicsBehaviour()); 330
183 331 if (abName == "n")
184 if (behaviourSwitches.Contains("g")) 332 newBehaviour = new NoneBehaviour();
185 behaviours.Add(new GrabbingBehaviour()); 333
186 334 if (abName == "p")
187 if (behaviourSwitches.Contains("t")) 335 newBehaviour = new PhysicsBehaviour();
188 behaviours.Add(new TeleportBehaviour()); 336
189 337 if (abName == "t")
190 if (behaviourSwitches.Contains("c")) 338 newBehaviour = new TeleportBehaviour();
191 behaviours.Add(new CrossBehaviour()); 339
192 340 if (abName == "tw")
193 StartBot(this, behaviours, firstName, lastName, password, loginUri); 341 newBehaviour = new TwitchyBehaviour();
342
343 if (abName == "ph2")
344 newBehaviour = new PhysicsBehaviour2();
345
346 if (abName == "inv")
347 newBehaviour = new InventoryDownloadBehaviour();
348
349 if (newBehaviour != null)
350 {
351 behaviours.Add(newBehaviour);
352 }
353 else
354 {
355 MainConsole.Instance.OutputFormat("No behaviour with abbreviated name {0} found", abName);
356 }
194 } 357 }
358
359 return behaviours;
195 } 360 }
196 361
197// /// <summary> 362 public void ConnectBots(int botcount)
198// /// Add additional bots (and threads) to our bot pool 363 {
199// /// </summary> 364 lock (BotConnectingStateChangeObject)
200// /// <param name="botcount">How Many of them to add</param> 365 {
201// public void addbots(int botcount) 366 if (BotConnectingState != BotManagerBotConnectingState.Ready)
202// { 367 {
203// int len = m_td.Length; 368 MainConsole.Instance.OutputFormat(
204// Thread[] m_td2 = new Thread[len + botcount]; 369 "Bot connecting status is {0}. Please wait for previous process to complete.", BotConnectingState);
205// for (int i = 0; i < len; i++) 370 return;
206// { 371 }
207// m_td2[i] = m_td[i]; 372
208// } 373 BotConnectingState = BotManagerBotConnectingState.Connecting;
209// m_td = m_td2; 374 }
210// int newlen = len + botcount; 375
211// for (int i = len; i < newlen; i++) 376 Thread connectBotThread = new Thread(o => ConnectBotsInternal(botcount));
212// { 377
213// startupBot(Config); 378 connectBotThread.Name = "Bots connection thread";
214// } 379 connectBotThread.Start();
215// } 380 }
381
382 private void ConnectBotsInternal(int botCount)
383 {
384 m_log.InfoFormat(
385 "[BOT MANAGER]: Starting {0} bots connecting to {1}, location {2}, named {3} {4}_<n>",
386 botCount,
387 m_loginUri,
388 m_startUri,
389 m_firstName,
390 m_lastNameStem);
391
392 m_log.DebugFormat("[BOT MANAGER]: Delay between logins is {0}ms", LoginDelay);
393 m_log.DebugFormat("[BOT MANAGER]: BotsSendAgentUpdates is {0}", InitBotSendAgentUpdates);
394 m_log.DebugFormat("[BOT MANAGER]: InitBotRequestObjectTextures is {0}", InitBotRequestObjectTextures);
395
396 List<Bot> botsToConnect = new List<Bot>();
397
398 lock (m_bots)
399 {
400 foreach (Bot bot in m_bots)
401 {
402 if (bot.ConnectionState == ConnectionState.Disconnected)
403 botsToConnect.Add(bot);
404
405 if (botsToConnect.Count >= botCount)
406 break;
407 }
408 }
409
410 foreach (Bot bot in botsToConnect)
411 {
412 lock (BotConnectingStateChangeObject)
413 {
414 if (BotConnectingState != BotManagerBotConnectingState.Connecting)
415 {
416 MainConsole.Instance.Output(
417 "[BOT MANAGER]: Aborting bot connection due to user-initiated disconnection");
418 return;
419 }
420 }
421
422 bot.Connect();
423
424 // Stagger logins
425 Thread.Sleep(LoginDelay);
426 }
427
428 lock (BotConnectingStateChangeObject)
429 {
430 if (BotConnectingState == BotManagerBotConnectingState.Connecting)
431 BotConnectingState = BotManagerBotConnectingState.Ready;
432 }
433 }
434
435 /// <summary>
436 /// Parses the command line start location to a start string/uri that the login mechanism will recognize.
437 /// </summary>
438 /// <returns>
439 /// The input start location to URI.
440 /// </returns>
441 /// <param name='startLocation'>
442 /// Start location.
443 /// </param>
444 private string ParseInputStartLocationToUri(string startLocation)
445 {
446 if (startLocation == "home" || startLocation == "last")
447 return startLocation;
448
449 string regionName;
450
451 // Just a region name or only one (!) extra component. Like a viewer, we will stick 128/128/0 on the end
452 Vector3 startPos = new Vector3(128, 128, 0);
453
454 string[] startLocationComponents = startLocation.Split('/');
455
456 regionName = startLocationComponents[0];
457
458 if (startLocationComponents.Length >= 2)
459 {
460 float.TryParse(startLocationComponents[1], out startPos.X);
461
462 if (startLocationComponents.Length >= 3)
463 {
464 float.TryParse(startLocationComponents[2], out startPos.Y);
465
466 if (startLocationComponents.Length >= 4)
467 float.TryParse(startLocationComponents[3], out startPos.Z);
468 }
469 }
470
471 return string.Format("uri:{0}&{1}&{2}&{3}", regionName, startPos.X, startPos.Y, startPos.Z);
472 }
216 473
217 /// <summary> 474 /// <summary>
218 /// This starts up the bot and stores the thread for the bot in the thread array 475 /// This creates a bot but does not start it.
219 /// </summary> 476 /// </summary>
220 /// <param name="bm"></param> 477 /// <param name="bm"></param>
221 /// <param name="behaviours">Behaviours for this bot to perform.</param> 478 /// <param name="behaviours">Behaviours for this bot to perform.</param>
@@ -223,30 +480,25 @@ namespace pCampBot
223 /// <param name="lastName">Last name</param> 480 /// <param name="lastName">Last name</param>
224 /// <param name="password">Password</param> 481 /// <param name="password">Password</param>
225 /// <param name="loginUri">Login URI</param> 482 /// <param name="loginUri">Login URI</param>
226 public void StartBot( 483 /// <param name="startLocation">Location to start the bot. Can be "last", "home" or a specific sim name.</param>
484 /// <param name="wearSetting"></param>
485 public void CreateBot(
227 BotManager bm, List<IBehaviour> behaviours, 486 BotManager bm, List<IBehaviour> behaviours,
228 string firstName, string lastName, string password, string loginUri) 487 string firstName, string lastName, string password, string loginUri, string startLocation, string wearSetting)
229 { 488 {
230 MainConsole.Instance.OutputFormat( 489 MainConsole.Instance.OutputFormat(
231 "[BOT MANAGER]: Starting bot {0} {1}, behaviours are {2}", 490 "[BOT MANAGER]: Creating bot {0} {1}, behaviours are {2}",
232 firstName, lastName, string.Join(",", behaviours.ConvertAll<string>(b => b.Name).ToArray())); 491 firstName, lastName, string.Join(",", behaviours.ConvertAll<string>(b => b.Name).ToArray()));
233 492
234 Bot pb = new Bot(bm, behaviours, firstName, lastName, password, loginUri); 493 Bot pb = new Bot(bm, behaviours, firstName, lastName, password, startLocation, loginUri);
494 pb.wear = wearSetting;
495 pb.Client.Settings.SEND_AGENT_UPDATES = InitBotSendAgentUpdates;
496 pb.RequestObjectTextures = InitBotRequestObjectTextures;
235 497
236 pb.OnConnected += handlebotEvent; 498 pb.OnConnected += handlebotEvent;
237 pb.OnDisconnected += handlebotEvent; 499 pb.OnDisconnected += handlebotEvent;
238 500
239 lock (m_lBot) 501 m_bots.Add(pb);
240 m_lBot.Add(pb);
241
242 Thread pbThread = new Thread(pb.startup);
243 pbThread.Name = pb.Name;
244 pbThread.IsBackground = true;
245
246 pbThread.Start();
247
248 // Stagger logins
249 Thread.Sleep(LoginDelay);
250 } 502 }
251 503
252 /// <summary> 504 /// <summary>
@@ -259,52 +511,322 @@ namespace pCampBot
259 switch (eventt) 511 switch (eventt)
260 { 512 {
261 case EventType.CONNECTED: 513 case EventType.CONNECTED:
514 {
262 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Connected"); 515 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Connected");
263 break; 516 break;
517 }
518
264 case EventType.DISCONNECTED: 519 case EventType.DISCONNECTED:
520 {
265 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Disconnected"); 521 m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Disconnected");
522 break;
523 }
524 }
525 }
266 526
267 lock (m_lBot) 527 /// <summary>
268 { 528 /// Standard CreateConsole routine
269 if (m_lBot.TrueForAll(b => b.ConnectionState == ConnectionState.Disconnected)) 529 /// </summary>
270 Environment.Exit(0); 530 /// <returns></returns>
531 protected CommandConsole CreateConsole()
532 {
533 return new LocalConsole("pCampbot");
534 }
271 535
272 break; 536 private void HandleConnect(string module, string[] cmd)
537 {
538 lock (m_bots)
539 {
540 int botsToConnect;
541 int disconnectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Disconnected);
542
543 if (cmd.Length == 1)
544 {
545 botsToConnect = disconnectedBots;
546 }
547 else
548 {
549 if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[1], out botsToConnect))
550 return;
551
552 botsToConnect = Math.Min(botsToConnect, disconnectedBots);
553 }
554
555 MainConsole.Instance.OutputFormat("Connecting {0} bots", botsToConnect);
556
557 ConnectBots(botsToConnect);
558 }
559 }
560
561 private void HandleAddBehaviour(string module, string[] cmd)
562 {
563 if (cmd.Length < 3 || cmd.Length > 4)
564 {
565 MainConsole.Instance.OutputFormat("Usage: add behaviour <abbreviated-behaviour> [<bot-number>]");
566 return;
567 }
568
569 string rawBehaviours = cmd[2];
570
571 List<Bot> botsToEffect = new List<Bot>();
572
573 if (cmd.Length == 3)
574 {
575 lock (m_bots)
576 botsToEffect.AddRange(m_bots);
577 }
578 else
579 {
580 int botNumber;
581 if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber))
582 return;
583
584 Bot bot = GetBotFromNumber(botNumber);
585
586 if (bot == null)
587 {
588 MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber);
589 return;
590 }
591
592 botsToEffect.Add(bot);
593 }
594
595
596 HashSet<string> rawAbbreviatedSwitchesToAdd = new HashSet<string>();
597 Array.ForEach<string>(rawBehaviours.Split(new char[] { ',' }), b => rawAbbreviatedSwitchesToAdd.Add(b));
598
599 foreach (Bot bot in botsToEffect)
600 {
601 List<IBehaviour> behavioursAdded = new List<IBehaviour>();
602
603 foreach (IBehaviour behaviour in CreateBehavioursFromAbbreviatedNames(rawAbbreviatedSwitchesToAdd))
604 {
605 if (bot.AddBehaviour(behaviour))
606 behavioursAdded.Add(behaviour);
607 }
608
609 MainConsole.Instance.OutputFormat(
610 "Added behaviours {0} to bot {1}",
611 string.Join(", ", behavioursAdded.ConvertAll<string>(b => b.Name).ToArray()), bot.Name);
612 }
613 }
614
615 private void HandleRemoveBehaviour(string module, string[] cmd)
616 {
617 if (cmd.Length < 3 || cmd.Length > 4)
618 {
619 MainConsole.Instance.OutputFormat("Usage: remove behaviour <abbreviated-behaviour> [<bot-number>]");
620 return;
621 }
622
623 string rawBehaviours = cmd[2];
624
625 List<Bot> botsToEffect = new List<Bot>();
626
627 if (cmd.Length == 3)
628 {
629 lock (m_bots)
630 botsToEffect.AddRange(m_bots);
631 }
632 else
633 {
634 int botNumber;
635 if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber))
636 return;
637
638 Bot bot = GetBotFromNumber(botNumber);
639
640 if (bot == null)
641 {
642 MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber);
643 return;
644 }
645
646 botsToEffect.Add(bot);
647 }
648
649 HashSet<string> abbreviatedBehavioursToRemove = new HashSet<string>();
650 Array.ForEach<string>(rawBehaviours.Split(new char[] { ',' }), b => abbreviatedBehavioursToRemove.Add(b));
651
652 foreach (Bot bot in botsToEffect)
653 {
654 List<IBehaviour> behavioursRemoved = new List<IBehaviour>();
655
656 foreach (string b in abbreviatedBehavioursToRemove)
657 {
658 IBehaviour behaviour;
659
660 if (bot.TryGetBehaviour(b, out behaviour))
661 {
662 bot.RemoveBehaviour(b);
663 behavioursRemoved.Add(behaviour);
273 } 664 }
665 }
666
667 MainConsole.Instance.OutputFormat(
668 "Removed behaviours {0} from bot {1}",
669 string.Join(", ", behavioursRemoved.ConvertAll<string>(b => b.Name).ToArray()), bot.Name);
274 } 670 }
275 } 671 }
276 672
277 /// <summary> 673 private void HandleDisconnect(string module, string[] cmd)
278 /// Shut down all bots 674 {
279 /// </summary> 675 List<Bot> connectedBots;
280 /// <remarks> 676 int botsToDisconnectCount;
281 /// We launch each shutdown on its own thread so that a slow shutting down bot doesn't hold up all the others. 677
282 /// </remarks> 678 lock (m_bots)
283 public void doBotShutdown() 679 connectedBots = m_bots.FindAll(b => b.ConnectionState == ConnectionState.Connected);
680
681 if (cmd.Length == 1)
682 {
683 botsToDisconnectCount = connectedBots.Count;
684 }
685 else
686 {
687 if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[1], out botsToDisconnectCount))
688 return;
689
690 botsToDisconnectCount = Math.Min(botsToDisconnectCount, connectedBots.Count);
691 }
692
693 lock (BotConnectingStateChangeObject)
694 BotConnectingState = BotManagerBotConnectingState.Disconnecting;
695
696 Thread disconnectBotThread = new Thread(o => DisconnectBotsInternal(connectedBots, botsToDisconnectCount));
697
698 disconnectBotThread.Name = "Bots disconnection thread";
699 disconnectBotThread.Start();
700 }
701
702 private void DisconnectBotsInternal(List<Bot> connectedBots, int disconnectCount)
284 { 703 {
285 lock (m_lBot) 704 MainConsole.Instance.OutputFormat("Disconnecting {0} bots", disconnectCount);
705
706 int disconnectedBots = 0;
707
708 for (int i = connectedBots.Count - 1; i >= 0; i--)
286 { 709 {
287 foreach (Bot bot in m_lBot) 710 if (disconnectedBots >= disconnectCount)
711 break;
712
713 Bot thisBot = connectedBots[i];
714
715 if (thisBot.ConnectionState == ConnectionState.Connected)
288 { 716 {
289 Bot thisBot = bot; 717 ThreadPool.QueueUserWorkItem(o => thisBot.Disconnect());
290 Util.FireAndForget(o => thisBot.shutdown()); 718 disconnectedBots++;
291 } 719 }
292 } 720 }
721
722 lock (BotConnectingStateChangeObject)
723 BotConnectingState = BotManagerBotConnectingState.Ready;
293 } 724 }
294 725
295 /// <summary> 726 private void HandleSit(string module, string[] cmd)
296 /// Standard CreateConsole routine
297 /// </summary>
298 /// <returns></returns>
299 protected CommandConsole CreateConsole()
300 { 727 {
301 return new LocalConsole("pCampbot"); 728 lock (m_bots)
729 {
730 foreach (Bot bot in m_bots)
731 {
732 if (bot.ConnectionState == ConnectionState.Connected)
733 {
734 MainConsole.Instance.OutputFormat("Sitting bot {0} on ground.", bot.Name);
735 bot.SitOnGround();
736 }
737 }
738 }
739 }
740
741 private void HandleStand(string module, string[] cmd)
742 {
743 lock (m_bots)
744 {
745 foreach (Bot bot in m_bots)
746 {
747 if (bot.ConnectionState == ConnectionState.Connected)
748 {
749 MainConsole.Instance.OutputFormat("Standing bot {0} from ground.", bot.Name);
750 bot.Stand();
751 }
752 }
753 }
302 } 754 }
303 755
304 private void HandleShutdown(string module, string[] cmd) 756 private void HandleShutdown(string module, string[] cmd)
305 { 757 {
306 m_log.Info("[BOTMANAGER]: Shutting down bots"); 758 lock (m_bots)
307 doBotShutdown(); 759 {
760 int connectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Connected);
761
762 if (connectedBots > 0)
763 {
764 MainConsole.Instance.OutputFormat("Please disconnect {0} connected bots first", connectedBots);
765 return;
766 }
767 }
768
769 MainConsole.Instance.Output("Shutting down");
770
771 m_serverStatsCollector.Close();
772
773 Environment.Exit(0);
774 }
775
776 private void HandleSetBots(string module, string[] cmd)
777 {
778 string key = cmd[2];
779 string rawValue = cmd[3];
780
781 if (key == "SEND_AGENT_UPDATES")
782 {
783 bool newSendAgentUpdatesSetting;
784
785 if (!ConsoleUtil.TryParseConsoleBool(MainConsole.Instance, rawValue, out newSendAgentUpdatesSetting))
786 return;
787
788 MainConsole.Instance.OutputFormat(
789 "Setting SEND_AGENT_UPDATES to {0} for all bots", newSendAgentUpdatesSetting);
790
791 lock (m_bots)
792 m_bots.ForEach(b => b.Client.Settings.SEND_AGENT_UPDATES = newSendAgentUpdatesSetting);
793 }
794 else
795 {
796 MainConsole.Instance.Output("Error: Only setting currently available is SEND_AGENT_UPDATES");
797 }
798 }
799
800 private void HandleDebugLludpPacketCommand(string module, string[] args)
801 {
802 if (args.Length != 6)
803 {
804 MainConsole.Instance.OutputFormat("Usage: debug lludp packet <level> <bot-first-name> <bot-last-name>");
805 return;
806 }
807
808 int level;
809
810 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[3], out level))
811 return;
812
813 string botFirstName = args[4];
814 string botLastName = args[5];
815
816 Bot bot;
817
818 lock (m_bots)
819 bot = m_bots.FirstOrDefault(b => b.FirstName == botFirstName && b.LastName == botLastName);
820
821 if (bot == null)
822 {
823 MainConsole.Instance.OutputFormat("No bot named {0} {1}", botFirstName, botLastName);
824 return;
825 }
826
827 bot.PacketDebugLevel = level;
828
829 MainConsole.Instance.OutputFormat("Set debug level of {0} to {1}", bot.Name, bot.PacketDebugLevel);
308 } 830 }
309 831
310 private void HandleShowRegions(string module, string[] cmd) 832 private void HandleShowRegions(string module, string[] cmd)
@@ -324,41 +846,120 @@ namespace pCampBot
324 846
325 private void HandleShowStatus(string module, string[] cmd) 847 private void HandleShowStatus(string module, string[] cmd)
326 { 848 {
327 string outputFormat = "{0,-30} {1, -30} {2,-14}"; 849 ConsoleDisplayList cdl = new ConsoleDisplayList();
328 MainConsole.Instance.OutputFormat(outputFormat, "Name", "Region", "Status"); 850 cdl.AddRow("Bot connecting state", BotConnectingState);
329 851
330 lock (m_lBot) 852 MainConsole.Instance.Output(cdl.ToString());
853 }
854
855 private void HandleShowBotsStatus(string module, string[] cmd)
856 {
857 ConsoleDisplayTable cdt = new ConsoleDisplayTable();
858 cdt.AddColumn("Name", 24);
859 cdt.AddColumn("Region", 24);
860 cdt.AddColumn("Status", 13);
861 cdt.AddColumn("Conns", 5);
862 cdt.AddColumn("Behaviours", 20);
863
864 Dictionary<ConnectionState, int> totals = new Dictionary<ConnectionState, int>();
865 foreach (object o in Enum.GetValues(typeof(ConnectionState)))
866 totals[(ConnectionState)o] = 0;
867
868 lock (m_bots)
331 { 869 {
332 foreach (Bot pb in m_lBot) 870 foreach (Bot bot in m_bots)
333 { 871 {
334 Simulator currentSim = pb.Client.Network.CurrentSim; 872 Simulator currentSim = bot.Client.Network.CurrentSim;
335 873 totals[bot.ConnectionState]++;
336 MainConsole.Instance.OutputFormat( 874
337 outputFormat, 875 cdt.AddRow(
338 pb.Name, currentSim != null ? currentSim.Name : "(none)", pb.ConnectionState); 876 bot.Name,
877 currentSim != null ? currentSim.Name : "(none)",
878 bot.ConnectionState,
879 bot.SimulatorsCount,
880 string.Join(",", bot.Behaviours.Keys.ToArray()));
339 } 881 }
340 } 882 }
883
884 MainConsole.Instance.Output(cdt.ToString());
885
886 ConsoleDisplayList cdl = new ConsoleDisplayList();
887
888 foreach (KeyValuePair<ConnectionState, int> kvp in totals)
889 cdl.AddRow(kvp.Key, kvp.Value);
890
891 MainConsole.Instance.Output(cdl.ToString());
341 } 892 }
342 893
343 /* 894 private void HandleShowBotStatus(string module, string[] cmd)
344 private void HandleQuit(string module, string[] cmd)
345 { 895 {
346 m_console.Warn("DANGER", "This should only be used to quit the program if you've already used the shutdown command and the program hasn't quit"); 896 if (cmd.Length != 3)
347 Environment.Exit(0); 897 {
898 MainConsole.Instance.Output("Usage: show bot <n>");
899 return;
900 }
901
902 int botNumber;
903
904 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, cmd[2], out botNumber))
905 return;
906
907 Bot bot = GetBotFromNumber(botNumber);
908
909 if (bot == null)
910 {
911 MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber);
912 return;
913 }
914
915 ConsoleDisplayList cdl = new ConsoleDisplayList();
916 cdl.AddRow("Name", bot.Name);
917 cdl.AddRow("Status", bot.ConnectionState);
918
919 Simulator currentSim = bot.Client.Network.CurrentSim;
920 cdl.AddRow("Region", currentSim != null ? currentSim.Name : "(none)");
921
922 List<Simulator> connectedSimulators = bot.Simulators;
923 List<string> simulatorNames = connectedSimulators.ConvertAll<string>(cs => cs.Name);
924 cdl.AddRow("Connections", string.Join(", ", simulatorNames.ToArray()));
925
926 MainConsole.Instance.Output(cdl.ToString());
927
928 MainConsole.Instance.Output("Settings");
929
930 ConsoleDisplayList statusCdl = new ConsoleDisplayList();
931
932 statusCdl.AddRow(
933 "Behaviours",
934 string.Join(", ", bot.Behaviours.Values.ToList().ConvertAll<string>(b => b.Name).ToArray()));
935
936 GridClient botClient = bot.Client;
937 statusCdl.AddRow("SEND_AGENT_UPDATES", botClient.Settings.SEND_AGENT_UPDATES);
938
939 MainConsole.Instance.Output(statusCdl.ToString());
940 }
941
942 /// <summary>
943 /// Get a specific bot from its number.
944 /// </summary>
945 /// <returns>null if no bot was found</returns>
946 /// <param name='botNumber'></param>
947 private Bot GetBotFromNumber(int botNumber)
948 {
949 string name = GenerateBotNameFromNumber(botNumber);
950
951 Bot bot;
952
953 lock (m_bots)
954 bot = m_bots.Find(b => b.Name == name);
955
956 return bot;
957 }
958
959 private string GenerateBotNameFromNumber(int botNumber)
960 {
961 return string.Format("{0} {1}_{2}", m_firstName, m_lastNameStem, botNumber);
348 } 962 }
349 */
350//
351// private void HandleAddBots(string module, string[] cmd)
352// {
353// int newbots = 0;
354//
355// if (cmd.Length > 2)
356// {
357// Int32.TryParse(cmd[2], out newbots);
358// }
359// if (newbots > 0)
360// addbots(newbots);
361// }
362 963
363 internal void Grid_GridRegion(object o, GridRegionEventArgs args) 964 internal void Grid_GridRegion(object o, GridRegionEventArgs args)
364 { 965 {
@@ -379,4 +980,4 @@ namespace pCampBot
379 } 980 }
380 } 981 }
381 } 982 }
382} 983} \ No newline at end of file
diff --git a/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs b/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs
index 9c984be..660c630 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,22 @@ namespace pCampBot.Interfaces
46 void Initialize(Bot bot); 51 void Initialize(Bot bot);
47 52
48 /// <summary> 53 /// <summary>
54 /// Interrupt the behaviour.
55 /// </summary>
56 /// <remarks>
57 /// This should cause the current Action call() to terminate if this is active.
58 /// </remarks>
59 void Interrupt();
60
61 /// <summary>
62 /// Close down this behaviour.
63 /// </summary>
64 /// <remarks>
65 /// This is triggered if a behaviour is removed via explicit command and when a bot is disconnected
66 /// </remarks>
67 void Close();
68
69 /// <summary>
49 /// Action to take when this behaviour is invoked. 70 /// Action to take when this behaviour is invoked.
50 /// </summary> 71 /// </summary>
51 /// <param name="bot"></param> 72 /// <param name="bot"></param>
diff --git a/OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs b/OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs
index 20598f1..f551135 100644
--- a/OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs
+++ b/OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.8.3.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33
diff --git a/OpenSim/Tools/pCampBot/pCampBot.cs b/OpenSim/Tools/pCampBot/pCampBot.cs
index 9e82577..1fb0e03 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> -firstname <first-name> -lastname <last-name> -password <password> [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 (default: last) (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: p) (optional)\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}