From 134f86e8d5c414409631b25b8c6f0ee45fbd8631 Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Thu, 3 Nov 2016 21:44:39 +1000 Subject: Initial update to OpenSim 0.8.2.1 source code. --- .../Tools/pCampBot/Behaviours/AbstractBehaviour.cs | 19 +- .../Tools/pCampBot/Behaviours/CrossBehaviour.cs | 6 +- .../Tools/pCampBot/Behaviours/GrabbingBehaviour.cs | 9 +- .../Behaviours/InventoryDownloadBehaviour.cs | 121 +++ OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs | 60 ++ .../Tools/pCampBot/Behaviours/PhysicsBehaviour.cs | 9 + .../Tools/pCampBot/Behaviours/PhysicsBehaviour2.cs | 86 ++ .../Tools/pCampBot/Behaviours/TeleportBehaviour.cs | 9 +- .../Tools/pCampBot/Behaviours/TwitchyBehaviour.cs | 73 ++ OpenSim/Tools/pCampBot/Bot.cs | 348 ++++++-- OpenSim/Tools/pCampBot/BotManager.cs | 903 +++++++++++++++++---- OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs | 21 + OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs | 4 +- OpenSim/Tools/pCampBot/pCampBot.cs | 89 +- 14 files changed, 1501 insertions(+), 256 deletions(-) create mode 100644 OpenSim/Tools/pCampBot/Behaviours/InventoryDownloadBehaviour.cs create mode 100644 OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs create mode 100644 OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour2.cs create mode 100644 OpenSim/Tools/pCampBot/Behaviours/TwitchyBehaviour.cs (limited to 'OpenSim/Tools/pCampBot') 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; using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using pCampBot.Interfaces; namespace pCampBot { - public class AbstractBehaviour : IBehaviour + public abstract class AbstractBehaviour : IBehaviour { + /// + /// Abbreviated name of this behaviour. + /// + public string AbbreviatedName { get; protected set; } + public string Name { get; protected set; } public Bot Bot { get; protected set; } - public virtual void Action() {} + public abstract void Action(); + + public virtual void Interrupt() {} + + protected AutoResetEvent m_interruptEvent = new AutoResetEvent(false); public virtual void Initialize(Bot bot) { Bot = bot; } + + public virtual void Close() + { + Interrupt(); + } } } 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 public const int m_regionCrossingTimeout = 1000 * 60; - public CrossBehaviour() { Name = "Cross"; } + public CrossBehaviour() + { + AbbreviatedName = "c"; + Name = "Cross"; + } public override void Action() { 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; using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using pCampBot.Interfaces; namespace pCampBot @@ -41,7 +42,11 @@ namespace pCampBot /// public class GrabbingBehaviour : AbstractBehaviour { - public GrabbingBehaviour() { Name = "Grabbing"; } + public GrabbingBehaviour() + { + AbbreviatedName = "g"; + Name = "Grabbing"; + } public override void Action() { @@ -56,6 +61,8 @@ namespace pCampBot Bot.Client.Self.Grab(prim.LocalID); Bot.Client.Self.GrabUpdate(prim.ID, Vector3.Zero); Bot.Client.Self.DeGrab(prim.LocalID); + + Thread.Sleep(1000); } } } \ 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Linq; +using pCampBot.Interfaces; + +namespace pCampBot +{ + /// + /// Do nothing + /// + public class InventoryDownloadBehaviour : AbstractBehaviour + { + private bool m_initialized; + private int m_Requests = 2; + private Stopwatch m_StopWatch = new Stopwatch(); + private List m_processed = new List(); + + public InventoryDownloadBehaviour() + { + AbbreviatedName = "inv"; + Name = "Inventory"; + } + + public override void Action() + { + if (!m_initialized) + { + m_initialized = true; + Bot.Client.Settings.HTTP_INVENTORY = true; + Bot.Client.Settings.FETCH_MISSING_INVENTORY = true; + Bot.Client.Inventory.FolderUpdated += Inventory_FolderUpdated; + Console.WriteLine("Lib owner is " + Bot.Client.Inventory.Store.LibraryRootNode.Data.OwnerID); + m_StopWatch.Start(); + Bot.Client.Inventory.RequestFolderContents(Bot.Client.Inventory.Store.RootFolder.UUID, Bot.Client.Self.AgentID, true, true, InventorySortOrder.ByDate); + Bot.Client.Inventory.RequestFolderContents(Bot.Client.Inventory.Store.LibraryRootNode.Data.UUID, Bot.Client.Inventory.Store.LibraryRootNode.Data.OwnerID, true, true, InventorySortOrder.ByDate); + } + + Thread.Sleep(1000); + Console.WriteLine("Total items: " + Bot.Client.Inventory.Store.Items.Count + "; Total requests: " + m_Requests + "; Time: " + m_StopWatch.Elapsed); + + } + + void Inventory_FolderUpdated(object sender, FolderUpdatedEventArgs e) + { + if (e.Success) + { + //Console.WriteLine("Folder " + e.FolderID + " updated"); + bool fetch = false; + lock (m_processed) + { + if (!m_processed.Contains(e.FolderID)) + { + m_processed.Add(e.FolderID); + fetch = true; + } + } + + if (fetch) + { + List m_foldersToFetch = new List(); + foreach (InventoryBase item in Bot.Client.Inventory.Store.GetContents(e.FolderID)) + { + if (item is InventoryFolder) + { + InventoryFolder f = new InventoryFolder(item.UUID); + f.OwnerID = item.OwnerID; + m_foldersToFetch.Add(f); + } + } + if (m_foldersToFetch.Count > 0) + { + m_Requests += 1; + Bot.Client.Inventory.RequestFolderContentsCap(m_foldersToFetch, Bot.Client.Network.CurrentSim.Caps.CapabilityURI("FetchInventoryDescendents2"), true, true, InventorySortOrder.ByDate); + } + } + + if (Bot.Client.Inventory.Store.Items.Count >= 15739) + { + m_StopWatch.Stop(); + Console.WriteLine("Stop! Total items: " + Bot.Client.Inventory.Store.Items.Count + "; Total requests: " + m_Requests + "; Time: " + m_StopWatch.Elapsed); + } + } + + } + + public override void Interrupt() + { + m_interruptEvent.Set(); + } + } +} \ 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..0d43781 --- /dev/null +++ b/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs @@ -0,0 +1,60 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Linq; +using pCampBot.Interfaces; + +namespace pCampBot +{ + /// + /// Do nothing + /// + public class NoneBehaviour : AbstractBehaviour + { + public NoneBehaviour() + { + AbbreviatedName = "n"; + Name = "None"; + } + + public override void Action() + { + Bot.Client.Self.Jump(false); + Bot.Client.Self.Movement.Stop = true; + m_interruptEvent.WaitOne(); + Bot.Client.Self.Movement.Stop = false; + } + + public override void Interrupt() + { + m_interruptEvent.Set(); + } + } +} \ 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 public PhysicsBehaviour() { + AbbreviatedName = "p"; Name = "Physics"; talkarray = readexcuses(); } @@ -77,6 +78,14 @@ namespace pCampBot Bot.Client.Self.Chat(randomf, 0, ChatType.Normal); } + public override void Close() + { + if (Bot.ConnectionState == ConnectionState.Connected) + Bot.Client.Self.Jump(false); + + base.Close(); + } + private string[] readexcuses() { string allexcuses = ""; diff --git a/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour2.cs b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour2.cs new file mode 100644 index 0000000..1ec2046 --- /dev/null +++ b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour2.cs @@ -0,0 +1,86 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using log4net; +using pCampBot.Interfaces; + +namespace pCampBot +{ + /// + /// This behavior is for the systematic study of some performance improvements made + /// for OSCC'13. + /// Walk around, sending AgentUpdate packets all the time. + /// + public class PhysicsBehaviour2 : AbstractBehaviour + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public PhysicsBehaviour2() + { + AbbreviatedName = "ph2"; + Name = "Physics2"; + } + + private const int TIME_WALKING = 5 * 10; // 5 seconds + private int counter = 0; + + public override void Action() + { + + if (counter >= TIME_WALKING) + { + counter = 0; + + Vector3 target = new Vector3(Bot.Random.Next(1, 254), Bot.Random.Next(1, 254), Bot.Client.Self.SimPosition.Z); + MyTurnToward(target); + + Bot.Client.Self.Movement.AtPos = true; + + } + else + counter++; + // In any case, send an update + Bot.Client.Self.Movement.SendUpdate(); + } + + private void MyTurnToward(Vector3 target) + { + Quaternion between = Vector3.RotationBetween(Vector3.UnitX, Vector3.Normalize(target - Bot.Client.Self.SimPosition)); + Quaternion rot = between ; + + Bot.Client.Self.Movement.BodyRotation = rot; + Bot.Client.Self.Movement.HeadRotation = rot; + Bot.Client.Self.Movement.Camera.LookAt(Bot.Client.Self.SimPosition, target); + } + } +} \ 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; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading; using log4net; using OpenMetaverse; using pCampBot.Interfaces; @@ -42,7 +43,11 @@ namespace pCampBot { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public TeleportBehaviour() { Name = "Teleport"; } + public TeleportBehaviour() + { + AbbreviatedName = "t"; + Name = "Teleport"; + } public override void Action() { @@ -70,6 +75,8 @@ namespace pCampBot Bot.Name, sourceRegion.Name, Bot.Client.Self.SimPosition, destRegion.Name, destPosition); Bot.Client.Self.Teleport(destRegion.RegionHandle, destPosition); + + Thread.Sleep(Bot.Random.Next(3000, 10000)); } } } \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Behaviours/TwitchyBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/TwitchyBehaviour.cs new file mode 100644 index 0000000..7b4639d --- /dev/null +++ b/OpenSim/Tools/pCampBot/Behaviours/TwitchyBehaviour.cs @@ -0,0 +1,73 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using log4net; +using pCampBot.Interfaces; + +namespace pCampBot +{ + /// + /// This behavior is for the systematic study of some performance improvements made + /// for OSCC'13. + /// Do nothing, but send AgentUpdate packets all the time that have only slightly + /// different state. The delta of difference will be filtered by OpenSim early on + /// in the packet processing pipeline. These filters did not exist before OSCC'13. + /// + public class TwitchyBehaviour : AbstractBehaviour + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public TwitchyBehaviour() + { + AbbreviatedName = "tw"; + Name = "Twitchy"; + } + + private const float TWITCH = 0.0001f; + private int direction = 1; + + public override void Action() + { + Bot.Client.Self.Movement.BodyRotation = new Quaternion(Bot.Client.Self.Movement.BodyRotation.X + direction * TWITCH, + Bot.Client.Self.Movement.BodyRotation.Y, + Bot.Client.Self.Movement.BodyRotation.Z, + Bot.Client.Self.Movement.BodyRotation.W); + + //m_log.DebugFormat("[TWITCH]: BodyRot {0}", Bot.Client.Self.Movement.BodyRotation); + direction = -direction; + + Bot.Client.Self.Movement.SendUpdate(); + + } + + } +} \ 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; using log4net; using OpenMetaverse; using OpenMetaverse.Assets; +using OpenMetaverse.Packets; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Console; using pCampBot.Interfaces; using Timer = System.Timers.Timer; +using PermissionMask = OpenSim.Framework.PermissionMask; namespace pCampBot { @@ -55,25 +57,47 @@ namespace pCampBot { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + public int PacketDebugLevel + { + get { return m_packetDebugLevel; } + set + { + if (value == m_packetDebugLevel) + return; + + m_packetDebugLevel = value; + + if (Client != null) + { + if (m_packetDebugLevel <= 0) + Client.Network.UnregisterCallback(PacketType.Default, PacketReceivedDebugHandler); + else + Client.Network.RegisterCallback(PacketType.Default, PacketReceivedDebugHandler, false); + } + } + } + private int m_packetDebugLevel; + public delegate void AnEvent(Bot callbot, EventType someevent); // event delegate for bot events /// - /// Bot manager. + /// Controls whether bots request textures for the object information they receive /// - public BotManager Manager { get; private set; } + public bool RequestObjectTextures { get; set; } /// - /// Bot config, passed from BotManager. + /// Bot manager. /// - private IConfig startupConfig; + public BotManager Manager { get; private set; } /// /// Behaviours implemented by this bot. /// /// - /// Lock this list before manipulating it. + /// Indexed by abbreviated name. There can only be one instance of a particular behaviour. + /// Lock this structure before manipulating it. /// - public List Behaviours { get; private set; } + public Dictionary Behaviours { get; private set; } /// /// Objects that the bot has discovered. @@ -96,11 +120,35 @@ namespace pCampBot /// public ConnectionState ConnectionState { get; private set; } + public List Simulators + { + get + { + lock (Client.Network.Simulators) + return new List(Client.Network.Simulators); + } + } + + /// + /// The number of connections that this bot has to different simulators. + /// + /// Includes both root and child connections. + public int SimulatorsCount + { + get + { + lock (Client.Network.Simulators) + return Client.Network.Simulators.Count; + } + } + public string FirstName { get; private set; } public string LastName { get; private set; } public string Name { get; private set; } public string Password { get; private set; } public string LoginUri { get; private set; } + public string StartLocation { get; private set; } + public string saveDir; public string wear; @@ -136,94 +184,180 @@ namespace pCampBot /// public Bot( BotManager bm, List behaviours, - string firstName, string lastName, string password, string loginUri) + string firstName, string lastName, string password, string startLocation, string loginUri) { ConnectionState = ConnectionState.Disconnected; - behaviours.ForEach(b => b.Initialize(this)); - - Client = new GridClient(); - - Random = new Random(Environment.TickCount);// We do stuff randomly here + Random = new Random(bm.Rng.Next()); FirstName = firstName; LastName = lastName; Name = string.Format("{0} {1}", FirstName, LastName); Password = password; LoginUri = loginUri; + StartLocation = startLocation; Manager = bm; - startupConfig = bm.Config; - readconfig(); - Behaviours = behaviours; + Behaviours = new Dictionary(); + foreach (IBehaviour behaviour in behaviours) + AddBehaviour(behaviour); + + // Only calling for use as a template. + CreateLibOmvClient(); + } + + public bool TryGetBehaviour(string abbreviatedName, out IBehaviour behaviour) + { + lock (Behaviours) + return Behaviours.TryGetValue(abbreviatedName, out behaviour); + } + + public bool AddBehaviour(IBehaviour behaviour) + { + Dictionary updatedBehaviours = new Dictionary(Behaviours); + + if (!updatedBehaviours.ContainsKey(behaviour.AbbreviatedName)) + { + behaviour.Initialize(this); + updatedBehaviours.Add(behaviour.AbbreviatedName, behaviour); + Behaviours = updatedBehaviours; + + return true; + } + + return false; + } + + public bool RemoveBehaviour(string abbreviatedName) + { + if (Behaviours.Count <= 0) + return false; + + Dictionary updatedBehaviours = new Dictionary(Behaviours); + IBehaviour behaviour; + + if (!updatedBehaviours.TryGetValue(abbreviatedName, out behaviour)) + return false; + + updatedBehaviours.Remove(abbreviatedName); + Behaviours = updatedBehaviours; + + behaviour.Close(); + + return true; + } + + private void CreateLibOmvClient() + { + GridClient newClient = new GridClient(); + + if (Client != null) + { + // Remove any registered debug handlers + Client.Network.UnregisterCallback(PacketType.Default, PacketReceivedDebugHandler); + + newClient.Settings.LOGIN_SERVER = Client.Settings.LOGIN_SERVER; + newClient.Settings.ALWAYS_DECODE_OBJECTS = Client.Settings.ALWAYS_DECODE_OBJECTS; + newClient.Settings.AVATAR_TRACKING = Client.Settings.AVATAR_TRACKING; + newClient.Settings.OBJECT_TRACKING = Client.Settings.OBJECT_TRACKING; + newClient.Settings.SEND_AGENT_THROTTLE = Client.Settings.SEND_AGENT_THROTTLE; + newClient.Settings.SEND_AGENT_UPDATES = Client.Settings.SEND_AGENT_UPDATES; + newClient.Settings.SEND_PINGS = Client.Settings.SEND_PINGS; + newClient.Settings.STORE_LAND_PATCHES = Client.Settings.STORE_LAND_PATCHES; + newClient.Settings.USE_ASSET_CACHE = Client.Settings.USE_ASSET_CACHE; + newClient.Settings.MULTIPLE_SIMS = Client.Settings.MULTIPLE_SIMS; + newClient.Throttle.Asset = Client.Throttle.Asset; + newClient.Throttle.Land = Client.Throttle.Land; + newClient.Throttle.Task = Client.Throttle.Task; + newClient.Throttle.Texture = Client.Throttle.Texture; + newClient.Throttle.Wind = Client.Throttle.Wind; + newClient.Throttle.Total = Client.Throttle.Total; + } + else + { + newClient.Settings.LOGIN_SERVER = LoginUri; + newClient.Settings.ALWAYS_DECODE_OBJECTS = false; + newClient.Settings.AVATAR_TRACKING = false; + newClient.Settings.OBJECT_TRACKING = false; + newClient.Settings.SEND_AGENT_THROTTLE = true; + newClient.Settings.SEND_PINGS = true; + newClient.Settings.STORE_LAND_PATCHES = false; + newClient.Settings.USE_ASSET_CACHE = false; + newClient.Settings.MULTIPLE_SIMS = true; + newClient.Throttle.Asset = 100000; + newClient.Throttle.Land = 100000; + newClient.Throttle.Task = 100000; + newClient.Throttle.Texture = 100000; + newClient.Throttle.Wind = 100000; + newClient.Throttle.Total = 400000; + } + + newClient.Network.LoginProgress += Network_LoginProgress; + newClient.Network.SimConnected += Network_SimConnected; + newClient.Network.SimDisconnected += Network_SimDisconnected; + newClient.Network.Disconnected += Network_OnDisconnected; + newClient.Objects.ObjectUpdate += Objects_NewPrim; + + if (m_packetDebugLevel > 0) + newClient.Network.RegisterCallback(PacketType.Default, PacketReceivedDebugHandler); + + Client = newClient; } //We do our actions here. This is where one would //add additional steps and/or things the bot should do private void Action() { - while (true) - lock (Behaviours) - Behaviours.ForEach( - b => - { - Thread.Sleep(Random.Next(3000, 10000)); - - // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType()); - b.Action(); - } - ); - } + while (ConnectionState == ConnectionState.Connected) + { + foreach (IBehaviour behaviour in Behaviours.Values) + { +// Thread.Sleep(Random.Next(3000, 10000)); + + // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType()); + behaviour.Action(); + } + } - /// - /// Read the Nini config and initialize - /// - public void readconfig() - { - wear = startupConfig.GetString("wear", "no"); + foreach (IBehaviour b in Behaviours.Values) + b.Close(); } /// /// Tells LibSecondLife to logout and disconnect. Raises the disconnect events once it finishes. /// - public void shutdown() + public void Disconnect() { ConnectionState = ConnectionState.Disconnecting; - - if (m_actionThread != null) - m_actionThread.Abort(); + + foreach (IBehaviour behaviour in Behaviours.Values) + behaviour.Close(); Client.Network.Logout(); } + public void Connect() + { + Thread connectThread = new Thread(ConnectInternal); + connectThread.Name = Name; + connectThread.IsBackground = true; + + connectThread.Start(); + } + /// /// This is the bot startup loop. /// - public void startup() + private void ConnectInternal() { - Client.Settings.LOGIN_SERVER = LoginUri; - Client.Settings.ALWAYS_DECODE_OBJECTS = false; - Client.Settings.AVATAR_TRACKING = false; - Client.Settings.OBJECT_TRACKING = false; - Client.Settings.SEND_AGENT_THROTTLE = true; - Client.Settings.SEND_PINGS = true; - Client.Settings.STORE_LAND_PATCHES = false; - Client.Settings.USE_ASSET_CACHE = false; - Client.Settings.MULTIPLE_SIMS = true; - Client.Throttle.Asset = 100000; - Client.Throttle.Land = 100000; - Client.Throttle.Task = 100000; - Client.Throttle.Texture = 100000; - Client.Throttle.Wind = 100000; - Client.Throttle.Total = 400000; - Client.Network.LoginProgress += this.Network_LoginProgress; - Client.Network.SimConnected += this.Network_SimConnected; - Client.Network.Disconnected += this.Network_OnDisconnected; - Client.Objects.ObjectUpdate += Objects_NewPrim; - ConnectionState = ConnectionState.Connecting; - if (Client.Network.Login(FirstName, LastName, Password, "pCampBot", "Your name")) + // Current create a new client on each connect. libomv doesn't seem to process new sim + // information (e.g. EstablishAgentCommunication events) if connecting after a disceonnect with the same + // client + CreateLibOmvClient(); + + if (Client.Network.Login(FirstName, LastName, Password, "pCampBot", StartLocation, "pCampBot")) { ConnectionState = ConnectionState.Connected; @@ -234,7 +368,6 @@ namespace pCampBot // OnConnected(this, EventType.CONNECTED); if (wear == "save") { - Client.Appearance.SetPreviousAppearance(); SaveDefaultAppearance(); } else if (wear != "no") @@ -267,6 +400,30 @@ namespace pCampBot } } + /// + /// Sit this bot on the ground. + /// + public void SitOnGround() + { + if (ConnectionState == ConnectionState.Connected) + Client.Self.SitOnGround(); + } + + /// + /// Stand this bot + /// + public void Stand() + { + if (ConnectionState == ConnectionState.Connected) + { + // Unlike sit on ground, here libomv checks whether we have SEND_AGENT_UPDATES enabled. + bool prevUpdatesSetting = Client.Settings.SEND_AGENT_UPDATES; + Client.Settings.SEND_AGENT_UPDATES = true; + Client.Self.Stand(); + Client.Settings.SEND_AGENT_UPDATES = prevUpdatesSetting; + } + } + public void SaveDefaultAppearance() { saveDir = "MyAppearance/" + FirstName + "_" + LastName; @@ -362,7 +519,7 @@ namespace pCampBot asset.Encode(); transid = Client.Assets.RequestUpload(asset,true); Client.Inventory.RequestCreateItem(clothfolder.UUID, "MyClothing" + i.ToString(), "MyClothing", AssetType.Clothing, - transid, InventoryType.Wearable, asset.WearableType, PermissionMask.All, delegate(bool success, InventoryItem item) + transid, InventoryType.Wearable, asset.WearableType, (OpenMetaverse.PermissionMask)PermissionMask.All, delegate(bool success, InventoryItem item) { if (success) { @@ -386,7 +543,7 @@ namespace pCampBot asset.Encode(); transid = Client.Assets.RequestUpload(asset,true); Client.Inventory.RequestCreateItem(clothfolder.UUID, "MyBodyPart" + i.ToString(), "MyBodyPart", AssetType.Bodypart, - transid, InventoryType.Wearable, asset.WearableType, PermissionMask.All, delegate(bool success, InventoryItem item) + transid, InventoryType.Wearable, asset.WearableType, (OpenMetaverse.PermissionMask)PermissionMask.All, delegate(bool success, InventoryItem item) { if (success) { @@ -450,7 +607,13 @@ namespace pCampBot public void Network_SimConnected(object sender, SimConnectedEventArgs args) { m_log.DebugFormat( - "[BOT]: Bot {0} connected to {1} at {2}", Name, args.Simulator.Name, args.Simulator.IPEndPoint); + "[BOT]: Bot {0} connected to region {1} at {2}", Name, args.Simulator.Name, args.Simulator.IPEndPoint); + } + + public void Network_SimDisconnected(object sender, SimDisconnectedEventArgs args) + { + m_log.DebugFormat( + "[BOT]: Bot {0} disconnected from region {1} at {2}", Name, args.Simulator.Name, args.Simulator.IPEndPoint); } public void Network_OnDisconnected(object sender, DisconnectedEventArgs args) @@ -458,7 +621,7 @@ namespace pCampBot ConnectionState = ConnectionState.Disconnected; m_log.DebugFormat( - "[BOT]: Bot {0} disconnected reason {1}, message {2}", Name, args.Reason, args.Message); + "[BOT]: Bot {0} disconnected from grid, reason {1}, message {2}", Name, args.Reason, args.Message); // m_log.ErrorFormat("Fired Network_OnDisconnected"); @@ -467,6 +630,8 @@ namespace pCampBot // || args.Reason == NetworkManager.DisconnectType.NetworkTimeout) // && OnDisconnected != null) + + if ( (args.Reason == NetworkManager.DisconnectType.ClientInitiated || args.Reason == NetworkManager.DisconnectType.ServerInitiated @@ -480,8 +645,8 @@ namespace pCampBot public void Objects_NewPrim(object sender, PrimEventArgs args) { -// if (Name.EndsWith("4")) -// throw new Exception("Aaargh"); + if (!RequestObjectTextures) + return; Primitive prim = args.Prim; @@ -494,7 +659,7 @@ namespace pCampBot { if (prim.Textures.DefaultTexture.TextureID != UUID.Zero) { - GetTexture(prim.Textures.DefaultTexture.TextureID); + GetTextureOrMesh(prim.Textures.DefaultTexture.TextureID, true); } for (int i = 0; i < prim.Textures.FaceTextures.Length; i++) @@ -506,32 +671,56 @@ namespace pCampBot UUID textureID = prim.Textures.FaceTextures[i].TextureID; if (textureID != UUID.Zero) - GetTexture(textureID); + GetTextureOrMesh(textureID, true); } } } if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero) - GetTexture(prim.Sculpt.SculptTexture); + { + bool mesh = (prim.Sculpt.Type == SculptType.Mesh); + GetTextureOrMesh(prim.Sculpt.SculptTexture, !mesh); + } } } - private void GetTexture(UUID textureID) + private void GetTextureOrMesh(UUID assetID, bool texture) { lock (Manager.AssetsReceived) { // Don't request assets more than once. - if (Manager.AssetsReceived.ContainsKey(textureID)) + if (Manager.AssetsReceived.ContainsKey(assetID)) return; - Manager.AssetsReceived[textureID] = false; - Client.Assets.RequestImage(textureID, ImageType.Normal, Asset_TextureCallback_Texture); + Manager.AssetsReceived[assetID] = false; + } + + try + { + if (texture) + Client.Assets.RequestImage(assetID, ImageType.Normal, Asset_TextureCallback_Texture); + else + Client.Assets.RequestMesh(assetID, Asset_MeshCallback); + } + catch (Exception e) + { + m_log.Warn(string.Format("Error requesting {0} {1}", texture ? "texture" : "mesh", assetID), e); } } public void Asset_TextureCallback_Texture(TextureRequestState state, AssetTexture assetTexture) { - //TODO: Implement texture saving and applying + if (state == TextureRequestState.Finished) + { + lock (Manager.AssetsReceived) + Manager.AssetsReceived[assetTexture.AssetID] = true; + } + } + + private void Asset_MeshCallback(bool success, AssetMesh assetMesh) + { + lock (Manager.AssetsReceived) + Manager.AssetsReceived[assetMesh.AssetID] = success; } public void Asset_ReceivedCallback(AssetDownload transfer, Asset asset) @@ -544,5 +733,16 @@ namespace pCampBot // SaveAsset((AssetWearable) asset); // } } + + private void PacketReceivedDebugHandler(object o, PacketReceivedEventArgs args) + { + Packet p = args.Packet; + Header h = p.Header; + Simulator s = args.Simulator; + + m_log.DebugFormat( + "[BOT]: Bot {0} received from {1} packet {2} #{3}, rel {4}, res {5}", + Name, s.Name, p.Type, h.Sequence, h.Reliable, h.Resent); + } } } 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; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Console; +using OpenSim.Framework.Monitoring; using pCampBot.Interfaces; namespace pCampBot { + public enum BotManagerBotConnectingState + { + Initializing, + Ready, + Connecting, + Disconnecting + } + /// /// Thread/Bot manager for the application /// @@ -52,6 +61,16 @@ namespace pCampBot public const int DefaultLoginDelay = 5000; /// + /// Is pCampbot ready to connect or currently in the process of connecting or disconnecting bots? + /// + public BotManagerBotConnectingState BotConnectingState { get; private set; } + + /// + /// Used to control locking as we can't lock an enum. + /// + private object BotConnectingStateChangeObject = new object(); + + /// /// Delay between logins of multiple bots. /// /// TODO: This value needs to be configurable by a command line argument. @@ -63,19 +82,24 @@ namespace pCampBot protected CommandConsole m_console; /// - /// Created bots, whether active or inactive. + /// Controls whether bots start out sending agent updates on connection. /// - protected List m_lBot; + public bool InitBotSendAgentUpdates { get; set; } /// - /// Random number generator. + /// Controls whether bots request textures for the object information they receive /// - public Random Rng { get; private set; } + public bool InitBotRequestObjectTextures { get; set; } /// - /// Overall configuration. + /// Created bots, whether active or inactive. /// - public IConfig Config { get; private set; } + protected List m_bots; + + /// + /// Random number generator. + /// + public Random Rng { get; private set; } /// /// Track the assets we have and have not received so we don't endlessly repeat requests. @@ -88,10 +112,68 @@ namespace pCampBot public Dictionary RegionsKnown { get; private set; } /// + /// First name for bots + /// + private string m_firstName; + + /// + /// Last name stem for bots + /// + private string m_lastNameStem; + + /// + /// Password for bots + /// + private string m_password; + + /// + /// Login URI for bots. + /// + private string m_loginUri; + + /// + /// Start location for bots. + /// + private string m_startUri; + + /// + /// Postfix bot number at which bot sequence starts. + /// + private int m_fromBotNumber; + + /// + /// Wear setting for bots. + /// + private string m_wearSetting; + + /// + /// Behaviour switches for bots. + /// + private HashSet m_defaultBehaviourSwitches = new HashSet(); + + /// + /// Collects general information on this server (which reveals this to be a misnamed class). + /// + private ServerStatsCollector m_serverStatsCollector; + + /// /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data /// public BotManager() { + // We set this to avoid issues with bots running out of HTTP connections if many are run from a single machine + // to multiple regions. + Settings.MAX_HTTP_CONNECTIONS = int.MaxValue; + +// System.Threading.ThreadPool.SetMaxThreads(600, 240); +// +// int workerThreads, iocpThreads; +// System.Threading.ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads); +// Console.WriteLine("ThreadPool.GetMaxThreads {0} {1}", workerThreads, iocpThreads); + + InitBotSendAgentUpdates = true; + InitBotRequestObjectTextures = true; + LoginDelay = DefaultLoginDelay; Rng = new Random(Environment.TickCount); @@ -117,30 +199,84 @@ namespace pCampBot } } - m_console.Commands.AddCommand("bot", false, "shutdown", - "shutdown", - "Shutdown bots and exit", HandleShutdown); - - m_console.Commands.AddCommand("bot", false, "quit", - "quit", - "Shutdown bots and exit", - HandleShutdown); - - m_console.Commands.AddCommand("bot", false, "show regions", - "show regions", - "Show regions known to bots", - HandleShowRegions); - - m_console.Commands.AddCommand("bot", false, "show bots", - "show bots", - "Shows the status of all bots", - HandleShowStatus); - -// m_console.Commands.AddCommand("bot", false, "add bots", -// "add bots ", -// "Add more bots", HandleAddBots); - - m_lBot = new List(); + m_console.Commands.AddCommand( + "Bots", false, "shutdown", "shutdown", "Shutdown bots and exit", HandleShutdown); + + m_console.Commands.AddCommand( + "Bots", false, "quit", "quit", "Shutdown bots and exit", HandleShutdown); + + m_console.Commands.AddCommand( + "Bots", false, "connect", "connect []", "Connect bots", + "If an is given, then the first disconnected bots by postfix number are connected.\n" + + "If no is given, then all currently disconnected bots are connected.", + HandleConnect); + + m_console.Commands.AddCommand( + "Bots", false, "disconnect", "disconnect []", "Disconnect bots", + "Disconnecting bots will interupt any bot connection process, including connection on startup.\n" + + "If an is given, then the last connected bots by postfix number are disconnected.\n" + + "If no is given, then all currently connected bots are disconnected.", + HandleDisconnect); + + m_console.Commands.AddCommand( + "Bots", false, "add behaviour", "add behaviour []", + "Add a behaviour to a bot", + "If no bot number is specified then behaviour is added to all bots.\n" + + "Can be performed on connected or disconnected bots.", + HandleAddBehaviour); + + m_console.Commands.AddCommand( + "Bots", false, "remove behaviour", "remove behaviour []", + "Remove a behaviour from a bot", + "If no bot number is specified then behaviour is added to all bots.\n" + + "Can be performed on connected or disconnected bots.", + HandleRemoveBehaviour); + + m_console.Commands.AddCommand( + "Bots", false, "sit", "sit", "Sit all bots on the ground.", + HandleSit); + + m_console.Commands.AddCommand( + "Bots", false, "stand", "stand", "Stand all bots.", + HandleStand); + + m_console.Commands.AddCommand( + "Bots", false, "set bots", "set bots ", "Set a setting for all bots.", HandleSetBots); + + m_console.Commands.AddCommand( + "Bots", false, "show regions", "show regions", "Show regions known to bots", HandleShowRegions); + + m_console.Commands.AddCommand( + "Bots", false, "show bots", "show bots", "Shows the status of all bots.", HandleShowBotsStatus); + + m_console.Commands.AddCommand( + "Bots", false, "show bot", "show bot ", + "Shows the detailed status and settings of a particular bot.", HandleShowBotStatus); + + m_console.Commands.AddCommand( + "Debug", + false, + "debug lludp packet", + "debug lludp packet ", + "Turn on received packet logging.", + "If level > 0 then all received packets that are not duplicates are logged.\n" + + "If level <= 0 then no received packets are logged.", + HandleDebugLludpPacketCommand); + + m_console.Commands.AddCommand( + "Bots", false, "show status", "show status", "Shows pCampbot status.", HandleShowStatus); + + m_bots = new List(); + + Watchdog.Enabled = true; + StatsManager.RegisterConsoleCommands(m_console); + + m_serverStatsCollector = new ServerStatsCollector(); + m_serverStatsCollector.Initialise(null); + m_serverStatsCollector.Enabled = true; + m_serverStatsCollector.Start(); + + BotConnectingState = BotManagerBotConnectingState.Ready; } /// @@ -148,74 +284,195 @@ namespace pCampBot /// /// How many bots to start up /// The configuration for the bots to use - public void dobotStartup(int botcount, IConfig cs) + public void CreateBots(int botcount, IConfig startupConfig) { - Config = cs; + m_firstName = startupConfig.GetString("firstname"); + m_lastNameStem = startupConfig.GetString("lastname"); + m_password = startupConfig.GetString("password"); + m_loginUri = startupConfig.GetString("loginuri"); + m_fromBotNumber = startupConfig.GetInt("from", 0); + m_wearSetting = startupConfig.GetString("wear", "no"); - string firstName = cs.GetString("firstname"); - string lastNameStem = cs.GetString("lastname"); - string password = cs.GetString("password"); - string loginUri = cs.GetString("loginuri"); + m_startUri = ParseInputStartLocationToUri(startupConfig.GetString("start", "last")); - HashSet behaviourSwitches = new HashSet(); Array.ForEach( - cs.GetString("behaviours", "p").Split(new char[] { ',' }), b => behaviourSwitches.Add(b)); + startupConfig.GetString("behaviours", "p").Split(new char[] { ',' }), b => m_defaultBehaviourSwitches.Add(b)); - MainConsole.Instance.OutputFormat( - "[BOT MANAGER]: Starting {0} bots connecting to {1}, named {2} {3}_", - botcount, - loginUri, - firstName, - lastNameStem); + for (int i = 0; i < botcount; i++) + { + lock (m_bots) + { + string lastName = string.Format("{0}_{1}", m_lastNameStem, i + m_fromBotNumber); + + CreateBot( + this, + CreateBehavioursFromAbbreviatedNames(m_defaultBehaviourSwitches), + m_firstName, lastName, m_password, m_loginUri, m_startUri, m_wearSetting); + } + } + } - MainConsole.Instance.OutputFormat("[BOT MANAGER]: Delay between logins is {0}ms", LoginDelay); + private List CreateBehavioursFromAbbreviatedNames(HashSet abbreviatedNames) + { + // We must give each bot its own list of instantiated behaviours since they store state. + List behaviours = new List(); - for (int i = 0; i < botcount; i++) + // Hard-coded for now + foreach (string abName in abbreviatedNames) { - string lastName = string.Format("{0}_{1}", lastNameStem, i); - - // We must give each bot its own list of instantiated behaviours since they store state. - List behaviours = new List(); - - // Hard-coded for now - if (behaviourSwitches.Contains("p")) - behaviours.Add(new PhysicsBehaviour()); - - if (behaviourSwitches.Contains("g")) - behaviours.Add(new GrabbingBehaviour()); - - if (behaviourSwitches.Contains("t")) - behaviours.Add(new TeleportBehaviour()); - - if (behaviourSwitches.Contains("c")) - behaviours.Add(new CrossBehaviour()); - - StartBot(this, behaviours, firstName, lastName, password, loginUri); + IBehaviour newBehaviour = null; + + if (abName == "c") + newBehaviour = new CrossBehaviour(); + + if (abName == "g") + newBehaviour = new GrabbingBehaviour(); + + if (abName == "n") + newBehaviour = new NoneBehaviour(); + + if (abName == "p") + newBehaviour = new PhysicsBehaviour(); + + if (abName == "t") + newBehaviour = new TeleportBehaviour(); + + if (abName == "tw") + newBehaviour = new TwitchyBehaviour(); + + if (abName == "ph2") + newBehaviour = new PhysicsBehaviour2(); + + if (abName == "inv") + newBehaviour = new InventoryDownloadBehaviour(); + + if (newBehaviour != null) + { + behaviours.Add(newBehaviour); + } + else + { + MainConsole.Instance.OutputFormat("No behaviour with abbreviated name {0} found", abName); + } } + + return behaviours; } -// /// -// /// Add additional bots (and threads) to our bot pool -// /// -// /// How Many of them to add -// public void addbots(int botcount) -// { -// int len = m_td.Length; -// Thread[] m_td2 = new Thread[len + botcount]; -// for (int i = 0; i < len; i++) -// { -// m_td2[i] = m_td[i]; -// } -// m_td = m_td2; -// int newlen = len + botcount; -// for (int i = len; i < newlen; i++) -// { -// startupBot(Config); -// } -// } + public void ConnectBots(int botcount) + { + lock (BotConnectingStateChangeObject) + { + if (BotConnectingState != BotManagerBotConnectingState.Ready) + { + MainConsole.Instance.OutputFormat( + "Bot connecting status is {0}. Please wait for previous process to complete.", BotConnectingState); + return; + } + + BotConnectingState = BotManagerBotConnectingState.Connecting; + } + + Thread connectBotThread = new Thread(o => ConnectBotsInternal(botcount)); + + connectBotThread.Name = "Bots connection thread"; + connectBotThread.Start(); + } + + private void ConnectBotsInternal(int botCount) + { + m_log.InfoFormat( + "[BOT MANAGER]: Starting {0} bots connecting to {1}, location {2}, named {3} {4}_", + botCount, + m_loginUri, + m_startUri, + m_firstName, + m_lastNameStem); + + m_log.DebugFormat("[BOT MANAGER]: Delay between logins is {0}ms", LoginDelay); + m_log.DebugFormat("[BOT MANAGER]: BotsSendAgentUpdates is {0}", InitBotSendAgentUpdates); + m_log.DebugFormat("[BOT MANAGER]: InitBotRequestObjectTextures is {0}", InitBotRequestObjectTextures); + + List botsToConnect = new List(); + + lock (m_bots) + { + foreach (Bot bot in m_bots) + { + if (bot.ConnectionState == ConnectionState.Disconnected) + botsToConnect.Add(bot); + + if (botsToConnect.Count >= botCount) + break; + } + } + + foreach (Bot bot in botsToConnect) + { + lock (BotConnectingStateChangeObject) + { + if (BotConnectingState != BotManagerBotConnectingState.Connecting) + { + MainConsole.Instance.Output( + "[BOT MANAGER]: Aborting bot connection due to user-initiated disconnection"); + return; + } + } + + bot.Connect(); + + // Stagger logins + Thread.Sleep(LoginDelay); + } + + lock (BotConnectingStateChangeObject) + { + if (BotConnectingState == BotManagerBotConnectingState.Connecting) + BotConnectingState = BotManagerBotConnectingState.Ready; + } + } + + /// + /// Parses the command line start location to a start string/uri that the login mechanism will recognize. + /// + /// + /// The input start location to URI. + /// + /// + /// Start location. + /// + private string ParseInputStartLocationToUri(string startLocation) + { + if (startLocation == "home" || startLocation == "last") + return startLocation; + + string regionName; + + // Just a region name or only one (!) extra component. Like a viewer, we will stick 128/128/0 on the end + Vector3 startPos = new Vector3(128, 128, 0); + + string[] startLocationComponents = startLocation.Split('/'); + + regionName = startLocationComponents[0]; + + if (startLocationComponents.Length >= 2) + { + float.TryParse(startLocationComponents[1], out startPos.X); + + if (startLocationComponents.Length >= 3) + { + float.TryParse(startLocationComponents[2], out startPos.Y); + + if (startLocationComponents.Length >= 4) + float.TryParse(startLocationComponents[3], out startPos.Z); + } + } + + return string.Format("uri:{0}&{1}&{2}&{3}", regionName, startPos.X, startPos.Y, startPos.Z); + } /// - /// This starts up the bot and stores the thread for the bot in the thread array + /// This creates a bot but does not start it. /// /// /// Behaviours for this bot to perform. @@ -223,30 +480,25 @@ namespace pCampBot /// Last name /// Password /// Login URI - public void StartBot( + /// Location to start the bot. Can be "last", "home" or a specific sim name. + /// + public void CreateBot( BotManager bm, List behaviours, - string firstName, string lastName, string password, string loginUri) + string firstName, string lastName, string password, string loginUri, string startLocation, string wearSetting) { MainConsole.Instance.OutputFormat( - "[BOT MANAGER]: Starting bot {0} {1}, behaviours are {2}", + "[BOT MANAGER]: Creating bot {0} {1}, behaviours are {2}", firstName, lastName, string.Join(",", behaviours.ConvertAll(b => b.Name).ToArray())); - Bot pb = new Bot(bm, behaviours, firstName, lastName, password, loginUri); + Bot pb = new Bot(bm, behaviours, firstName, lastName, password, startLocation, loginUri); + pb.wear = wearSetting; + pb.Client.Settings.SEND_AGENT_UPDATES = InitBotSendAgentUpdates; + pb.RequestObjectTextures = InitBotRequestObjectTextures; pb.OnConnected += handlebotEvent; pb.OnDisconnected += handlebotEvent; - lock (m_lBot) - m_lBot.Add(pb); - - Thread pbThread = new Thread(pb.startup); - pbThread.Name = pb.Name; - pbThread.IsBackground = true; - - pbThread.Start(); - - // Stagger logins - Thread.Sleep(LoginDelay); + m_bots.Add(pb); } /// @@ -259,52 +511,322 @@ namespace pCampBot switch (eventt) { case EventType.CONNECTED: + { m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Connected"); break; + } + case EventType.DISCONNECTED: + { m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Disconnected"); + break; + } + } + } - lock (m_lBot) - { - if (m_lBot.TrueForAll(b => b.ConnectionState == ConnectionState.Disconnected)) - Environment.Exit(0); + /// + /// Standard CreateConsole routine + /// + /// + protected CommandConsole CreateConsole() + { + return new LocalConsole("pCampbot"); + } - break; + private void HandleConnect(string module, string[] cmd) + { + lock (m_bots) + { + int botsToConnect; + int disconnectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Disconnected); + + if (cmd.Length == 1) + { + botsToConnect = disconnectedBots; + } + else + { + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[1], out botsToConnect)) + return; + + botsToConnect = Math.Min(botsToConnect, disconnectedBots); + } + + MainConsole.Instance.OutputFormat("Connecting {0} bots", botsToConnect); + + ConnectBots(botsToConnect); + } + } + + private void HandleAddBehaviour(string module, string[] cmd) + { + if (cmd.Length < 3 || cmd.Length > 4) + { + MainConsole.Instance.OutputFormat("Usage: add behaviour []"); + return; + } + + string rawBehaviours = cmd[2]; + + List botsToEffect = new List(); + + if (cmd.Length == 3) + { + lock (m_bots) + botsToEffect.AddRange(m_bots); + } + else + { + int botNumber; + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber)) + return; + + Bot bot = GetBotFromNumber(botNumber); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber); + return; + } + + botsToEffect.Add(bot); + } + + + HashSet rawAbbreviatedSwitchesToAdd = new HashSet(); + Array.ForEach(rawBehaviours.Split(new char[] { ',' }), b => rawAbbreviatedSwitchesToAdd.Add(b)); + + foreach (Bot bot in botsToEffect) + { + List behavioursAdded = new List(); + + foreach (IBehaviour behaviour in CreateBehavioursFromAbbreviatedNames(rawAbbreviatedSwitchesToAdd)) + { + if (bot.AddBehaviour(behaviour)) + behavioursAdded.Add(behaviour); + } + + MainConsole.Instance.OutputFormat( + "Added behaviours {0} to bot {1}", + string.Join(", ", behavioursAdded.ConvertAll(b => b.Name).ToArray()), bot.Name); + } + } + + private void HandleRemoveBehaviour(string module, string[] cmd) + { + if (cmd.Length < 3 || cmd.Length > 4) + { + MainConsole.Instance.OutputFormat("Usage: remove behaviour []"); + return; + } + + string rawBehaviours = cmd[2]; + + List botsToEffect = new List(); + + if (cmd.Length == 3) + { + lock (m_bots) + botsToEffect.AddRange(m_bots); + } + else + { + int botNumber; + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber)) + return; + + Bot bot = GetBotFromNumber(botNumber); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber); + return; + } + + botsToEffect.Add(bot); + } + + HashSet abbreviatedBehavioursToRemove = new HashSet(); + Array.ForEach(rawBehaviours.Split(new char[] { ',' }), b => abbreviatedBehavioursToRemove.Add(b)); + + foreach (Bot bot in botsToEffect) + { + List behavioursRemoved = new List(); + + foreach (string b in abbreviatedBehavioursToRemove) + { + IBehaviour behaviour; + + if (bot.TryGetBehaviour(b, out behaviour)) + { + bot.RemoveBehaviour(b); + behavioursRemoved.Add(behaviour); } + } + + MainConsole.Instance.OutputFormat( + "Removed behaviours {0} from bot {1}", + string.Join(", ", behavioursRemoved.ConvertAll(b => b.Name).ToArray()), bot.Name); } } - /// - /// Shut down all bots - /// - /// - /// We launch each shutdown on its own thread so that a slow shutting down bot doesn't hold up all the others. - /// - public void doBotShutdown() + private void HandleDisconnect(string module, string[] cmd) + { + List connectedBots; + int botsToDisconnectCount; + + lock (m_bots) + connectedBots = m_bots.FindAll(b => b.ConnectionState == ConnectionState.Connected); + + if (cmd.Length == 1) + { + botsToDisconnectCount = connectedBots.Count; + } + else + { + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[1], out botsToDisconnectCount)) + return; + + botsToDisconnectCount = Math.Min(botsToDisconnectCount, connectedBots.Count); + } + + lock (BotConnectingStateChangeObject) + BotConnectingState = BotManagerBotConnectingState.Disconnecting; + + Thread disconnectBotThread = new Thread(o => DisconnectBotsInternal(connectedBots, botsToDisconnectCount)); + + disconnectBotThread.Name = "Bots disconnection thread"; + disconnectBotThread.Start(); + } + + private void DisconnectBotsInternal(List connectedBots, int disconnectCount) { - lock (m_lBot) + MainConsole.Instance.OutputFormat("Disconnecting {0} bots", disconnectCount); + + int disconnectedBots = 0; + + for (int i = connectedBots.Count - 1; i >= 0; i--) { - foreach (Bot bot in m_lBot) + if (disconnectedBots >= disconnectCount) + break; + + Bot thisBot = connectedBots[i]; + + if (thisBot.ConnectionState == ConnectionState.Connected) { - Bot thisBot = bot; - Util.FireAndForget(o => thisBot.shutdown()); + ThreadPool.QueueUserWorkItem(o => thisBot.Disconnect()); + disconnectedBots++; } } + + lock (BotConnectingStateChangeObject) + BotConnectingState = BotManagerBotConnectingState.Ready; } - /// - /// Standard CreateConsole routine - /// - /// - protected CommandConsole CreateConsole() + private void HandleSit(string module, string[] cmd) { - return new LocalConsole("pCampbot"); + lock (m_bots) + { + foreach (Bot bot in m_bots) + { + if (bot.ConnectionState == ConnectionState.Connected) + { + MainConsole.Instance.OutputFormat("Sitting bot {0} on ground.", bot.Name); + bot.SitOnGround(); + } + } + } + } + + private void HandleStand(string module, string[] cmd) + { + lock (m_bots) + { + foreach (Bot bot in m_bots) + { + if (bot.ConnectionState == ConnectionState.Connected) + { + MainConsole.Instance.OutputFormat("Standing bot {0} from ground.", bot.Name); + bot.Stand(); + } + } + } } private void HandleShutdown(string module, string[] cmd) { - m_log.Info("[BOTMANAGER]: Shutting down bots"); - doBotShutdown(); + lock (m_bots) + { + int connectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Connected); + + if (connectedBots > 0) + { + MainConsole.Instance.OutputFormat("Please disconnect {0} connected bots first", connectedBots); + return; + } + } + + MainConsole.Instance.Output("Shutting down"); + + m_serverStatsCollector.Close(); + + Environment.Exit(0); + } + + private void HandleSetBots(string module, string[] cmd) + { + string key = cmd[2]; + string rawValue = cmd[3]; + + if (key == "SEND_AGENT_UPDATES") + { + bool newSendAgentUpdatesSetting; + + if (!ConsoleUtil.TryParseConsoleBool(MainConsole.Instance, rawValue, out newSendAgentUpdatesSetting)) + return; + + MainConsole.Instance.OutputFormat( + "Setting SEND_AGENT_UPDATES to {0} for all bots", newSendAgentUpdatesSetting); + + lock (m_bots) + m_bots.ForEach(b => b.Client.Settings.SEND_AGENT_UPDATES = newSendAgentUpdatesSetting); + } + else + { + MainConsole.Instance.Output("Error: Only setting currently available is SEND_AGENT_UPDATES"); + } + } + + private void HandleDebugLludpPacketCommand(string module, string[] args) + { + if (args.Length != 6) + { + MainConsole.Instance.OutputFormat("Usage: debug lludp packet "); + return; + } + + int level; + + if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[3], out level)) + return; + + string botFirstName = args[4]; + string botLastName = args[5]; + + Bot bot; + + lock (m_bots) + bot = m_bots.FirstOrDefault(b => b.FirstName == botFirstName && b.LastName == botLastName); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("No bot named {0} {1}", botFirstName, botLastName); + return; + } + + bot.PacketDebugLevel = level; + + MainConsole.Instance.OutputFormat("Set debug level of {0} to {1}", bot.Name, bot.PacketDebugLevel); } private void HandleShowRegions(string module, string[] cmd) @@ -324,41 +846,120 @@ namespace pCampBot private void HandleShowStatus(string module, string[] cmd) { - string outputFormat = "{0,-30} {1, -30} {2,-14}"; - MainConsole.Instance.OutputFormat(outputFormat, "Name", "Region", "Status"); + ConsoleDisplayList cdl = new ConsoleDisplayList(); + cdl.AddRow("Bot connecting state", BotConnectingState); - lock (m_lBot) + MainConsole.Instance.Output(cdl.ToString()); + } + + private void HandleShowBotsStatus(string module, string[] cmd) + { + ConsoleDisplayTable cdt = new ConsoleDisplayTable(); + cdt.AddColumn("Name", 24); + cdt.AddColumn("Region", 24); + cdt.AddColumn("Status", 13); + cdt.AddColumn("Conns", 5); + cdt.AddColumn("Behaviours", 20); + + Dictionary totals = new Dictionary(); + foreach (object o in Enum.GetValues(typeof(ConnectionState))) + totals[(ConnectionState)o] = 0; + + lock (m_bots) { - foreach (Bot pb in m_lBot) + foreach (Bot bot in m_bots) { - Simulator currentSim = pb.Client.Network.CurrentSim; - - MainConsole.Instance.OutputFormat( - outputFormat, - pb.Name, currentSim != null ? currentSim.Name : "(none)", pb.ConnectionState); + Simulator currentSim = bot.Client.Network.CurrentSim; + totals[bot.ConnectionState]++; + + cdt.AddRow( + bot.Name, + currentSim != null ? currentSim.Name : "(none)", + bot.ConnectionState, + bot.SimulatorsCount, + string.Join(",", bot.Behaviours.Keys.ToArray())); } } + + MainConsole.Instance.Output(cdt.ToString()); + + ConsoleDisplayList cdl = new ConsoleDisplayList(); + + foreach (KeyValuePair kvp in totals) + cdl.AddRow(kvp.Key, kvp.Value); + + MainConsole.Instance.Output(cdl.ToString()); } - /* - private void HandleQuit(string module, string[] cmd) + private void HandleShowBotStatus(string module, string[] cmd) { - 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"); - Environment.Exit(0); + if (cmd.Length != 3) + { + MainConsole.Instance.Output("Usage: show bot "); + return; + } + + int botNumber; + + if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, cmd[2], out botNumber)) + return; + + Bot bot = GetBotFromNumber(botNumber); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber); + return; + } + + ConsoleDisplayList cdl = new ConsoleDisplayList(); + cdl.AddRow("Name", bot.Name); + cdl.AddRow("Status", bot.ConnectionState); + + Simulator currentSim = bot.Client.Network.CurrentSim; + cdl.AddRow("Region", currentSim != null ? currentSim.Name : "(none)"); + + List connectedSimulators = bot.Simulators; + List simulatorNames = connectedSimulators.ConvertAll(cs => cs.Name); + cdl.AddRow("Connections", string.Join(", ", simulatorNames.ToArray())); + + MainConsole.Instance.Output(cdl.ToString()); + + MainConsole.Instance.Output("Settings"); + + ConsoleDisplayList statusCdl = new ConsoleDisplayList(); + + statusCdl.AddRow( + "Behaviours", + string.Join(", ", bot.Behaviours.Values.ToList().ConvertAll(b => b.Name).ToArray())); + + GridClient botClient = bot.Client; + statusCdl.AddRow("SEND_AGENT_UPDATES", botClient.Settings.SEND_AGENT_UPDATES); + + MainConsole.Instance.Output(statusCdl.ToString()); + } + + /// + /// Get a specific bot from its number. + /// + /// null if no bot was found + /// + private Bot GetBotFromNumber(int botNumber) + { + string name = GenerateBotNameFromNumber(botNumber); + + Bot bot; + + lock (m_bots) + bot = m_bots.Find(b => b.Name == name); + + return bot; + } + + private string GenerateBotNameFromNumber(int botNumber) + { + return string.Format("{0} {1}_{2}", m_firstName, m_lastNameStem, botNumber); } - */ -// -// private void HandleAddBots(string module, string[] cmd) -// { -// int newbots = 0; -// -// if (cmd.Length > 2) -// { -// Int32.TryParse(cmd[2], out newbots); -// } -// if (newbots > 0) -// addbots(newbots); -// } internal void Grid_GridRegion(object o, GridRegionEventArgs args) { @@ -379,4 +980,4 @@ namespace pCampBot } } } -} +} \ 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 public interface IBehaviour { /// + /// Abbreviated name of this behaviour. + /// + string AbbreviatedName { get; } + + /// /// Name of this behaviour. /// string Name { get; } @@ -46,6 +51,22 @@ namespace pCampBot.Interfaces void Initialize(Bot bot); /// + /// Interrupt the behaviour. + /// + /// + /// This should cause the current Action call() to terminate if this is active. + /// + void Interrupt(); + + /// + /// Close down this behaviour. + /// + /// + /// This is triggered if a behaviour is removed via explicit command and when a bot is disconnected + /// + void Close(); + + /// /// Action to take when this behaviour is invoked. /// /// 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; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.5.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("0.8.3.*")] + 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 @@ */ using System; +using System.IO; using System.Reflection; using System.Threading; using log4net; @@ -50,30 +51,62 @@ namespace pCampBot { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + public const string ConfigFileName = "pCampBot.ini"; + [STAThread] public static void Main(string[] args) { XmlConfigurator.Configure(); - IConfig config = ParseConfig(args); - if (config.Get("help") != null || config.Get("loginuri") == null) + IConfig commandLineConfig = ParseConfig(args); + if (commandLineConfig.Get("help") != null || commandLineConfig.Get("loginuri") == null) { Help(); } - else if (config.Get("firstname") == null || config.Get("lastname") == null || config.Get("password") == null) + else if ( + commandLineConfig.Get("firstname") == null + || commandLineConfig.Get("lastname") == null + || commandLineConfig.Get("password") == null) { Console.WriteLine("ERROR: You must supply a firstname, lastname and password for the bots."); } else { - int botcount = config.GetInt("botcount", 1); - BotManager bm = new BotManager(); - //startup specified number of bots. 1 is the default - Thread startBotThread = new Thread(o => bm.dobotStartup(botcount, config)); - startBotThread.Name = "Initial start bots thread"; - startBotThread.Start(); + string iniFilePath = Path.GetFullPath(Path.Combine(Util.configDir(), ConfigFileName)); + + if (File.Exists(iniFilePath)) + { + m_log.InfoFormat("[PCAMPBOT]: Reading configuration settings from {0}", iniFilePath); + + IConfigSource configSource = new IniConfigSource(iniFilePath); + + IConfig botManagerConfig = configSource.Configs["BotManager"]; + + if (botManagerConfig != null) + { + bm.LoginDelay = botManagerConfig.GetInt("LoginDelay", bm.LoginDelay); + } + + IConfig botConfig = configSource.Configs["Bot"]; + + if (botConfig != null) + { + bm.InitBotSendAgentUpdates + = botConfig.GetBoolean("SendAgentUpdates", bm.InitBotSendAgentUpdates); + bm.InitBotRequestObjectTextures + = botConfig.GetBoolean("RequestObjectTextures", bm.InitBotRequestObjectTextures); + } + } + + int botcount = commandLineConfig.GetInt("botcount", 1); + bool startConnected = commandLineConfig.Get("connect") != null; + + bm.CreateBots(botcount, commandLineConfig); + + if (startConnected) + bm.ConnectBots(botcount); while (true) { @@ -94,8 +127,11 @@ namespace pCampBot //Set up our nifty config.. thanks to nini ArgvConfigSource cs = new ArgvConfigSource(args); + cs.AddSwitch("Startup", "connect", "c"); cs.AddSwitch("Startup", "botcount", "n"); + cs.AddSwitch("Startup", "from", "f"); cs.AddSwitch("Startup", "loginuri", "l"); + cs.AddSwitch("Startup", "start", "s"); cs.AddSwitch("Startup", "firstname"); cs.AddSwitch("Startup", "lastname"); cs.AddSwitch("Startup", "password"); @@ -113,22 +149,27 @@ namespace pCampBot // You can either say no, to not load anything, yes, to load one of the default wearables, a folder // name, to load an specific folder, or save, to save an avatar with some already existing wearables // worn to the folder MyAppearance/FirstName_LastName, and the load it. + Console.WriteLine( - "usage: pCampBot <-loginuri loginuri> [OPTIONS]\n" + - "Spawns a set of bots to test an OpenSim region\n\n" + - " -l, -loginuri loginuri for sim to log into (required)\n" + - " -n, -botcount number of bots to start (default: 1)\n" + - " -firstname first name for the bots\n" + - " -lastname lastname for the bots. Each lastname will have _ appended, e.g. Ima Bot_0\n" + - " -password password for the bots\n" + - " -b, behaviours behaviours for bots. Comma separated, e.g. p,g. Default is p\n" + - " current options are:\n" + - " p (physics)\n" + - " g (grab)\n" + - " t (teleport)\n" + -// " c (cross)" + - " -wear set appearance folder to load from (default: no)\n" + - " -h, -help show this message"); + "Usage: pCampBot -loginuri -firstname -lastname -password [OPTIONS]\n" + + "Spawns a set of bots to test an OpenSim region\n\n" + + " -l, -loginuri loginuri for grid/standalone (required)\n" + + " -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" + + " -firstname first name for the bots (required)\n" + + " -lastname lastname for the bots (required). Each lastname will have _ appended, e.g. Ima Bot_0\n" + + " -password password for the bots (required)\n" + + " -n, -botcount number of bots to start (default: 1) (optional)\n" + + " -f, -from starting number for login bot names, e.g. 25 will login Ima Bot_25, Ima Bot_26, etc. (default: 0) (optional)\n" + + " -c, -connect connect all bots at startup (optional)\n" + + " -b, behaviours behaviours for bots. Comma separated, e.g. p,g (default: p) (optional)\n" + + " current options are:\n" + + " p (physics - bots constantly move and jump around)\n" + + " g (grab - bots randomly click prims whether set clickable or not)\n" + + " n (none - bots do nothing)\n" + + " t (teleport - bots regularly teleport between regions on the grid)\n" +// " c (cross)\n" + + + " -wear folder from which to load appearance data, \"no\" if there is no such folder (default: no) (optional)\n" + + " -h, -help show this message.\n"); } } } -- cgit v1.1