diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index 6323a88f8e..4fa4a7cded 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -3540,6 +3540,18 @@ namespace OpenSim.Region.Framework.Scenes
}
}
+ ///
+ /// Remove the given client from the scene.
+ ///
+ ///
+ /// Only clientstack code should call this directly. All other code should call IncomingCloseAgent() instead
+ /// to properly operate the state machine and avoid race conditions with other close requests (such as directly
+ /// from viewers).
+ ///
+ /// ID of agent to close
+ ///
+ /// Close the neighbour child agents associated with this client.
+ ///
public override void RemoveClient(UUID agentID, bool closeChildAgents)
{
AgentCircuitData acd = m_authenticateHandler.GetAgentCircuitData(agentID);
diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs
index 4eef162056..1dac676129 100644
--- a/OpenSim/Region/Framework/Scenes/SceneBase.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs
@@ -218,6 +218,19 @@ namespace OpenSim.Region.Framework.Scenes
#region Add/Remove Agent/Avatar
public abstract ISceneAgent AddNewClient(IClientAPI client, PresenceType type);
+
+ ///
+ /// Remove the given client from the scene.
+ ///
+ ///
+ /// Only clientstack code should call this directly. All other code should call IncomingCloseAgent() instead
+ /// to properly operate the state machine and avoid race conditions with other close requests (such as directly
+ /// from viewers).
+ ///
+ /// ID of agent to close
+ ///
+ /// Close the neighbour child agents associated with this client.
+ ///
public abstract void RemoveClient(UUID agentID, bool closeChildAgents);
public bool TryGetScenePresence(UUID agentID, out object scenePresence)
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
index d584782e74..502f85f02b 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -89,13 +89,6 @@ public sealed class BSCharacter : BSPhysObject
// set _avatarVolume and _mass based on capsule size, _density and Scale
ComputeAvatarVolumeAndMass();
- // The avatar's movement is controlled by this motor that speeds up and slows down
- // the avatar seeking to reach the motor's target speed.
- // This motor runs as a prestep action for the avatar so it will keep the avatar
- // standing as well as moving. Destruction of the avatar will destroy the pre-step action.
- m_moveActor = new BSActorAvatarMove(PhysScene, this, AvatarMoveActorName);
- PhysicalActors.Add(AvatarMoveActorName, m_moveActor);
-
DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5},pos={6}",
LocalID, _size, Scale, Density, _avatarVolume, RawMass, pos);
@@ -106,6 +99,13 @@ public sealed class BSCharacter : BSPhysObject
// New body and shape into PhysBody and PhysShape
PhysScene.Shapes.GetBodyAndShape(true, PhysScene.World, this);
+ // The avatar's movement is controlled by this motor that speeds up and slows down
+ // the avatar seeking to reach the motor's target speed.
+ // This motor runs as a prestep action for the avatar so it will keep the avatar
+ // standing as well as moving. Destruction of the avatar will destroy the pre-step action.
+ m_moveActor = new BSActorAvatarMove(PhysScene, this, AvatarMoveActorName);
+ PhysicalActors.Add(AvatarMoveActorName, m_moveActor);
+
SetPhysicalProperties();
});
return;
diff --git a/OpenSim/Tools/pCampBot/Bot.cs b/OpenSim/Tools/pCampBot/Bot.cs
index 9821180bc6..dac8ccb49b 100644
--- a/OpenSim/Tools/pCampBot/Bot.cs
+++ b/OpenSim/Tools/pCampBot/Bot.cs
@@ -63,11 +63,6 @@ namespace pCampBot
///
public BotManager Manager { get; private set; }
- ///
- /// Bot config, passed from BotManager.
- ///
- private IConfig startupConfig;
-
///
/// Behaviours implemented by this bot.
///
@@ -102,6 +97,8 @@ namespace pCampBot
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;
@@ -137,7 +134,7 @@ 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;
@@ -151,11 +148,9 @@ namespace pCampBot
Name = string.Format("{0} {1}", FirstName, LastName);
Password = password;
LoginUri = loginUri;
+ StartLocation = startLocation;
Manager = bm;
- startupConfig = bm.Config;
- readconfig();
-
Behaviours = behaviours;
}
@@ -163,7 +158,7 @@ namespace pCampBot
//add additional steps and/or things the bot should do
private void Action()
{
- while (true)
+ while (ConnectionState != ConnectionState.Disconnecting)
lock (Behaviours)
Behaviours.ForEach(
b =>
@@ -176,14 +171,6 @@ namespace pCampBot
);
}
- ///
- /// Read the Nini config and initialize
- ///
- public void readconfig()
- {
- wear = startupConfig.GetString("wear", "no");
- }
-
///
/// Tells LibSecondLife to logout and disconnect. Raises the disconnect events once it finishes.
///
@@ -191,8 +178,8 @@ namespace pCampBot
{
ConnectionState = ConnectionState.Disconnecting;
- if (m_actionThread != null)
- m_actionThread.Abort();
+// if (m_actionThread != null)
+// m_actionThread.Abort();
Client.Network.Logout();
}
@@ -207,6 +194,7 @@ namespace pCampBot
Client.Settings.AVATAR_TRACKING = false;
Client.Settings.OBJECT_TRACKING = false;
Client.Settings.SEND_AGENT_THROTTLE = true;
+ Client.Settings.SEND_AGENT_UPDATES = false;
Client.Settings.SEND_PINGS = true;
Client.Settings.STORE_LAND_PATCHES = false;
Client.Settings.USE_ASSET_CACHE = false;
@@ -224,7 +212,7 @@ namespace pCampBot
ConnectionState = ConnectionState.Connecting;
- if (Client.Network.Login(FirstName, LastName, Password, "pCampBot", "Your name"))
+ if (Client.Network.Login(FirstName, LastName, Password, "pCampBot", StartLocation, "Your name"))
{
ConnectionState = ConnectionState.Connected;
@@ -481,9 +469,6 @@ namespace pCampBot
public void Objects_NewPrim(object sender, PrimEventArgs args)
{
-// if (Name.EndsWith("4"))
-// throw new Exception("Aaargh");
-
Primitive prim = args.Prim;
if (prim != null)
diff --git a/OpenSim/Tools/pCampBot/BotManager.cs b/OpenSim/Tools/pCampBot/BotManager.cs
index 74bd36a9a9..397a98e37c 100644
--- a/OpenSim/Tools/pCampBot/BotManager.cs
+++ b/OpenSim/Tools/pCampBot/BotManager.cs
@@ -51,6 +51,11 @@ namespace pCampBot
public const int DefaultLoginDelay = 5000;
+ ///
+ /// True if pCampbot is in the process of shutting down.
+ ///
+ public bool ShuttingDown { get; private set; }
+
///
/// Delay between logins of multiple bots.
///
@@ -62,6 +67,16 @@ namespace pCampBot
///
protected CommandConsole m_console;
+ ///
+ /// Controls whether bots start out sending agent updates on connection.
+ ///
+ public bool InitBotSendAgentUpdates { get; set; }
+
+ ///
+ /// Controls whether bots request textures for the object information they receive
+ ///
+ public bool InitBotRequestObjectTextures { get; set; }
+
///
/// Created bots, whether active or inactive.
///
@@ -72,11 +87,6 @@ namespace pCampBot
///
public Random Rng { get; private set; }
- ///
- /// Overall configuration.
- ///
- public IConfig Config { get; private set; }
-
///
/// Track the assets we have and have not received so we don't endlessly repeat requests.
///
@@ -92,6 +102,9 @@ namespace pCampBot
///
public BotManager()
{
+ InitBotSendAgentUpdates = true;
+ InitBotRequestObjectTextures = true;
+
LoginDelay = DefaultLoginDelay;
Rng = new Random(Environment.TickCount);
@@ -148,31 +161,40 @@ namespace pCampBot
///
/// How many bots to start up
/// The configuration for the bots to use
- public void dobotStartup(int botcount, IConfig cs)
+ public void dobotStartup(int botcount, IConfig startupConfig)
{
- Config = cs;
+ string firstName = startupConfig.GetString("firstname");
+ string lastNameStem = startupConfig.GetString("lastname");
+ string password = startupConfig.GetString("password");
+ string loginUri = startupConfig.GetString("loginuri");
+ string startLocation = startupConfig.GetString("start", "last");
+ int fromBotNumber = startupConfig.GetInt("from", 0);
+ string 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");
+ string startUri = ParseInputStartLocationToUri(startLocation);
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 => behaviourSwitches.Add(b));
MainConsole.Instance.OutputFormat(
- "[BOT MANAGER]: Starting {0} bots connecting to {1}, named {2} {3}_",
+ "[BOT MANAGER]: Starting {0} bots connecting to {1}, location {2}, named {3} {4}_",
botcount,
loginUri,
+ startUri,
firstName,
lastNameStem);
MainConsole.Instance.OutputFormat("[BOT MANAGER]: Delay between logins is {0}ms", LoginDelay);
+ MainConsole.Instance.OutputFormat("[BOT MANAGER]: BotsSendAgentUpdates is {0}", InitBotSendAgentUpdates);
+ MainConsole.Instance.OutputFormat("[BOT MANAGER]: InitBotRequestObjectTextures is {0}", InitBotRequestObjectTextures);
for (int i = 0; i < botcount; i++)
{
- string lastName = string.Format("{0}_{1}", lastNameStem, i);
+ if (ShuttingDown)
+ break;
+
+ string lastName = string.Format("{0}_{1}", lastNameStem, i + fromBotNumber);
// We must give each bot its own list of instantiated behaviours since they store state.
List behaviours = new List();
@@ -193,10 +215,49 @@ namespace pCampBot
if (behaviourSwitches.Contains("t"))
behaviours.Add(new TeleportBehaviour());
- StartBot(this, behaviours, firstName, lastName, password, loginUri);
+ StartBot(this, behaviours, firstName, lastName, password, loginUri, startUri, wearSetting);
}
}
+ ///
+ /// 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);
+ }
+
// ///
// /// Add additional bots (and threads) to our bot pool
// ///
@@ -226,15 +287,19 @@ namespace pCampBot
/// Last name
/// Password
/// Login URI
+ /// Location to start the bot. Can be "last", "home" or a specific sim name.
+ ///
public void StartBot(
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}",
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.OnConnected += handlebotEvent;
pb.OnDisconnected += handlebotEvent;
@@ -306,7 +371,9 @@ namespace pCampBot
private void HandleShutdown(string module, string[] cmd)
{
- m_log.Info("[BOTMANAGER]: Shutting down bots");
+ MainConsole.Instance.Output("Shutting down");
+
+ ShuttingDown = true;
doBotShutdown();
}
diff --git a/OpenSim/Tools/pCampBot/pCampBot.cs b/OpenSim/Tools/pCampBot/pCampBot.cs
index 2707a49f7d..b02f9170f9 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,28 +51,52 @@ 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();
+ 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 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);
+
//startup specified number of bots. 1 is the default
- Thread startBotThread = new Thread(o => bm.dobotStartup(botcount, config));
+ Thread startBotThread = new Thread(o => bm.dobotStartup(botcount, commandLineConfig));
startBotThread.Name = "Initial start bots thread";
startBotThread.Start();
@@ -95,7 +120,9 @@ namespace pCampBot
ArgvConfigSource cs = new ArgvConfigSource(args);
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,23 +140,26 @@ 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 - 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" +
+ "usage: pCampBot <-loginuri loginuri> [OPTIONS]\n"
+ + "Spawns a set of bots to test an OpenSim region\n\n"
+ + " -l, -loginuri loginuri for grid/standalone (required)\n"
+ + " -s, -start optional start location for bots. 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\n"
+ + " -lastname lastname for the bots. Each lastname will have _ appended, e.g. Ima Bot_0\n"
+ + " -password password for the bots\n"
+ + " -n, -botcount optional number of bots to start (default: 1)\n"
+ + " -f, -from optional starting number for login bot names, e.g. 25 will login Ima Bot_25, Ima Bot_26, etc. (default: 0)"
+ + " -b, behaviours behaviours for bots. Comma separated, e.g. p,g. Default is p\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)" +
- " -wear set appearance folder to load from (default: no)\n" +
- " -h, -help show this message");
+ + " -wear optional folder from which to load appearance data, \"no\" if there is no such folder (default: no)\n"
+ + " -h, -help show this message.\n");
}
}
}
diff --git a/bin/pCampbot.ini.example b/bin/pCampbot.ini.example
new file mode 100644
index 0000000000..f44feaee1f
--- /dev/null
+++ b/bin/pCampbot.ini.example
@@ -0,0 +1,15 @@
+; This is the example config file for pCampbot
+; To use it, copy this file to pCampbot.ini and change settings if required
+
+[Bot]
+ ; Control whether bots should regularly send agent updates
+ ; Not doing this will reduce CPU requirements for pCampbot but greatly
+ ; reduce the realism compared to viewers which are constantly sending AgentUpdates UDP packets.
+ ; Defaults to true.
+ SendAgentUpdates = true
+
+ ; Control whether bots will requests textures when receiving object information
+ ; Not doing this will reduce CPU requirements for pCampbot but greatly
+ ; reduce the realism compared to viewers which requests such texture data if not already cached.
+ ; Defaults to true.
+ RequestObjectTextures = true