Compare commits

...

61 Commits

Author SHA1 Message Date
Christopher 3e8ca0b273 use List 2020-08-06 22:20:39 +02:00
Christopher 0ff367ef6a replace float to double 2020-08-06 04:47:51 +02:00
Christopher 027d8749a1 check X Y Z for max value 2020-08-06 02:08:09 +02:00
Jak Daniels cd72e7b701 remove CR from all .cs files 2016-04-07 18:21:58 +01:00
Jak Daniels cf4aeac0e5 Change the primflags to Temporary and Phantom 2016-03-25 20:58:23 +00:00
Jak Daniels 3ce758d96a Normalise line endings (again) 2015-10-17 19:47:21 +01:00
Jak Daniels 4b0854fcc1 Set the repo up to have normalised line endings 2015-10-17 19:20:31 +01:00
Jak Daniels 4a157786e9 Update changelog 2015-10-17 13:04:01 +01:00
Jak Daniels 81b353c53c Fix security so it correctly checks the OwnerUUID of an object which contains the script issuing a command 2015-10-17 12:56:50 +01:00
Jak Daniels 1bdb12c558 Added basic security. Module can now be limited ton accept commands from ESTATE_OWNER, ESTATE_MANAGER or list of UUIDs 2015-10-17 11:51:10 +01:00
Jak Daniels e749c7a6db Oops, forgot to keep updating this ;) 2015-10-16 16:02:52 +01:00
Jak Daniels 2683bedd70 Fix typos in docs for BirdsModuleStart setting 2015-10-16 15:49:36 +01:00
Jak Daniels f6c2a9ea83 Update documentation and example .ini to explain the BirdsModuleStartup setting 2015-10-16 15:41:42 +01:00
Jak Daniels d356b62176 Module is started by default, and can run without a config, but will not be enabled. Module can be specifically disabled in a region with BirdsModuleStartup = True/False 2015-10-16 15:00:55 +01:00
Jak Daniels 3dcb5d0b1c Unregister LoadPrims event on region unload 2015-10-02 19:32:04 +01:00
Jak Daniels 307191eb40 Starting to use the scripting extensions 2015-09-23 19:57:27 +01:00
Jak Daniels 4ed2de0a43 Fix CR/LF inconsistencies 2015-09-23 19:56:25 +01:00
Jak Daniels 9dc0a0d18b Further doc change 2015-06-01 13:21:51 +01:00
Jak Daniels d37d9da17a More doc changes 2015-06-01 13:19:10 +01:00
Jak Daniels 4df98ff39d Formatting error in README.md 2015-06-01 13:17:54 +01:00
Jak Daniels 1b6b67b509 Update documentation to explain config using existing Regions.ini 2015-06-01 13:16:15 +01:00
Jak Daniels bee8823d9d Fix Windows/Unix line endings and increse logging when named prim cannot be found. 2015-06-01 11:53:46 +01:00
Jak Daniels d556a68cec Changed the way the module looks for configs to include Regions.ini 2015-05-28 21:46:06 +01:00
Jak Daniels 12a498ff34 Update IAR file with 2 seagull types. 2015-02-28 14:27:49 +00:00
Jak Daniels 640d471a50 Remove whitespace 2015-02-28 14:27:24 +00:00
Jak Daniels fc7bb966a3 Fix typo in README.md 2015-01-06 12:51:14 +00:00
Jak Daniels 7ccf122ddc Stats now returns sitting avatar(s) UUID list too. Updated documentation. 2015-01-06 12:49:08 +00:00
Jak Daniels 231fbeee63 Added to the stats command, now the prim uuid list also returns the UUID of any avatar sitting on it 2015-01-06 12:11:39 +00:00
Jak Daniels 3bd0e30d10 Update documentation 2015-01-04 14:28:44 +00:00
Jak Daniels cf4acdcbf0 Complete work on stats command to show all settings, and list all bird prim names and uuids. 2015-01-04 14:27:05 +00:00
Jak Daniels b8266a3191 Fix typo in README.md 2015-01-03 21:37:01 +00:00
Jak Daniels 3f3cefc2ae Added new commands to alter flock dynamics in realtime. Updated README.md 2015-01-03 21:34:16 +00:00
Jak Daniels 220d5de92b Remove TODO.txt 2015-01-03 16:11:58 +00:00
Jak Daniels f387b5e0c3 Revert previous change to temporary prims as it means birds have to repopulate every
3 minutes. Instead hook the OnPrimsLoaded event (after scene object restore) and delete
any persisted OpenSimBirdsN prims left over from last shutdown.
2015-01-03 16:09:25 +00:00
Jak Daniels 173e046aba Change Bird prims to temporary as well as phantom to stop them persisting in the database 2015-01-03 12:52:46 +00:00
Jak Daniels 51cb61fb29 Allow prim names to have uppercase letters from inworld chat commands 2015-01-03 10:30:52 +00:00
Jak Daniels 2011c6f094 Added ability to accept commands from scripts on BirdsChatChannel 2015-01-02 21:17:51 +00:00
Jak Daniels 4187440a4c Added BirdsMaxFlockSize ini setting to keep max number of birds sane.
Updated documentation.
2015-01-02 15:45:17 +00:00
Jak Daniels 42e7f6af6f Updated documentation regarding network bandwidth. 2015-01-02 15:28:10 +00:00
Jak Daniels 45fd724e56 Only launch a prims script(s) when adding the prim, not on every update.
Rename Bird prims to OpenSimBirdsN where N is 0-birdcount.
2015-01-02 15:11:06 +00:00
Jak Daniels c487d9b945 Fix spurious console output when birds positions updated 2015-01-02 14:23:32 +00:00
Jak Daniels e0541bda5d Update documentation 2015-01-02 13:34:32 +00:00
Jak Daniels 8061edbed0 Added more verbose info on console when commands are issued.
Fixed in-world commands not working unless console region matched in-world region.
Remove birds from scene on region shutdown.
2015-01-02 13:32:46 +00:00
Jak Daniels 9dc171e731 Updated documentation with new commands 2014-12-31 21:17:48 +00:00
Jak Daniels df1e5c37de Add an OpenSimBirds inventory folder with a test bird that can be rezzed to the scene 2014-12-31 21:13:40 +00:00
Jak Daniels b879157683 Update documentation 2014-12-31 21:13:16 +00:00
Jak Daniels 80dfa5a0d6 Fix removeing object from scene and using existing objects 2014-12-31 21:12:20 +00:00
Jak Daniels ca41de0286 Numerous changes to make module more versatile 2014-12-31 18:50:48 +00:00
Jak Daniels 2541527cd7 Fix regression where m_scene not yet defined when adding console commands 2014-12-24 16:50:47 +00:00
Jak Daniels d18d37faf6 Many changes to support per region configuration and project name change 2014-12-24 16:29:19 +00:00
Jak Daniels d75428a078 Rename project to OpenSimBirds 2014-12-24 14:52:11 +00:00
Jak Daniels 3770b0e011 Remove unnecessary files and set file attributes consistently 2014-12-23 14:16:38 +00:00
Jak Daniels 012445a808 Small refactor of directory/project names 2014-12-23 14:07:00 +00:00
Jak Daniels a0e48018ac Minor changes to README.md 2014-07-19 14:45:21 +01:00
Jak Daniels 13a6116bea Minor changes to README.md 2014-07-19 14:41:31 +01:00
Jak Daniels add4d51f40 Minor changes to README.md 2014-07-19 14:40:36 +01:00
Jak Daniels 50b5c96406 Minor changes to README.md 2014-07-19 14:39:45 +01:00
Jak Daniels 14d9f6d4ee Minor changes to README.md 2014-07-19 14:35:31 +01:00
Jak Daniels 0bb8b92e99 Updated README.md, CHANGES.txt and CONTRIBUTORS.txt 2014-07-19 14:33:38 +01:00
Jak Daniels 184fa20e2c Major refactor and added support for varregions (first cut) 2014-07-17 17:43:21 +01:00
Jak Daniels d7c5242aa9 Renamed README to README.md 2014-07-10 17:29:19 +01:00
22 changed files with 1157 additions and 688 deletions

21
.gitattributes vendored Normal file
View File

@ -0,0 +1,21 @@
# Auto detect text files and perform LF normalization
* text=auto
*.cs text diff=csharp eol=lf
*.java text diff=java
*.html text diff=html
*.css text
*.js text
*.sql text
*.csproj text merge=union
*.sln text merge=union eol=crlf
*.docx diff=astextplain
*.DOCX diff=astextplain
# absolute paths are ok, as are globs
/**/postinst* text eol=lf
# paths that don't start with / are treated relative to the .gitattributes folder
relative/path/*.txt text eol=lf

View File

@ -12,3 +12,28 @@
13/07/2011
added more documentation
10/07/2014
forked from https://github.com/jonc/osboids
17/07/2014
refactored the code
added some more config items to set the region border size (no go zone) and the max height
added support to the module for varregions
24/12/2014
renamed the module to OpenSimBirds, changed all references to [Bb]oids to [Bb]irds in code
change the configuration to use own ini file and to setup per region configurations
changed Util class to BirdsUtil to avoid clash with OpenSim.Util
Um.... I think I missed some change log here... many things were changed. Use the git log??
16/10/2015
added a setting to determine whether the module is active on startup. This defaults to true,
meaning that even if there is no config at all the module still loads and waits for commands
from the console or in-world.
17/10/2015
added basic security. It's now possible to specify a list of allowed UUIDs (or ESTATE_MANAGER or
ESTATE_OWNER) of avatars that can send commands to the module from chat, or of object owners
if the commands are from a script.

View File

@ -1,4 +1,5 @@
The following people have contributed to the development of the osboids module
The following people have contributed to the development of the OpenSimBirds module
* Jon Cundill - initial implementation
* Jak Daniels - refactor for opensim 0.8.x and varregions and per region configuration

Binary file not shown.

Binary file not shown.

View File

@ -1,12 +0,0 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ReferencePath>/Users/jon/osim/opensim/bin/</ReferencePath>
<LastOpenVersion>9.0.21022</LastOpenVersion>
<ProjectView>ProjectFiles</ProjectView>
<ProjectTrust>0</ProjectTrust>
</PropertyGroup>
<PropertyGroup Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " />
<PropertyGroup Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
</Project>

View File

@ -1,61 +0,0 @@
<?xml version="1.0" ?>
<project name="Flocking" default="build">
<target name="build">
<echo message="Build Directory is ${project::get-base-directory()}/${build.dir}" />
<mkdir dir="${project::get-base-directory()}/${build.dir}" />
<copy todir="${project::get-base-directory()}/${build.dir}" flatten="true">
<fileset basedir="${project::get-base-directory()}">
</fileset>
</copy>
<copy todir="${project::get-base-directory()}/${build.dir}">
<fileset basedir=".">
</fileset>
</copy>
<csc target="library" debug="${build.debug}" platform="${build.platform}" unsafe="False" warnaserror="False" define="TRACE;DEBUG" nostdlib="False" main="" output="${project::get-base-directory()}/${build.dir}/${project::get-name()}.dll" noconfig="true">
<resources prefix="Flocking" dynamicprefix="true" >
<include name="Resources/Flocking.addin.xml" />
</resources>
<sources failonempty="true">
<include name="Boid.cs" />
<include name="FlockingModel.cs" />
<include name="FlockingModule.cs" />
<include name="FlockingView.cs" />
<include name="FlowMap.cs" />
<include name="Util.cs" />
</sources>
<references basedir="${project::get-base-directory()}">
<lib>
<include name="${project::get-base-directory()}" />
<include name="${project::get-base-directory()}/../../../bin" />
</lib>
<include name="../../../bin/log4net.dll"/>
<include name="../../../bin/Nini.dll"/>
<include name="../../../bin/OpenMetaverse.dll"/>
<include name="../../../bin/OpenMetaverseTypes.dll"/>
<include name="../../../bin/OpenSim.Framework.dll"/>
<include name="../../../bin/OpenSim.Framework.Communications.dll"/>
<include name="../../../bin/OpenSim.Framework.Console.dll"/>
<include name="../../../bin/OpenSim.Region.Framework.dll"/>
<include name="System.dll" />
<include name="System.Drawing.dll" />
<include name="System.Xml.dll" />
</references>
</csc>
<echo message="Copying from [${project::get-base-directory()}/${build.dir}/] to [${project::get-base-directory()}/../../../bin/" />
<mkdir dir="${project::get-base-directory()}/../../../bin/"/>
<copy todir="${project::get-base-directory()}/../../../bin/">
<fileset basedir="${project::get-base-directory()}/${build.dir}/" >
<include name="*.dll"/>
<include name="*.exe"/>
<include name="*.mdb" if='${build.debug}'/>
<include name="*.pdb" if='${build.debug}'/>
</fileset>
</copy>
</target>
<target name="clean">
<delete dir="${bin.dir}" failonerror="false" />
<delete dir="${obj.dir}" failonerror="false" />
</target>
<target name="doc" description="Creates documentation.">
</target>
</project>

View File

@ -1,319 +0,0 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* 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 System;
using System.Timers;
using System.Collections.Generic;
using OpenMetaverse;
using System.IO;
using Nini.Config;
using System.Threading;
using log4net;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Framework;
using OpenSim.Framework.Console;
namespace Flocking
{
public class FlockingModule : INonSharedRegionModule
{
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
static object m_sync = new object();
private Scene m_scene;
private FlockingModel m_model;
private FlockingView m_view;
private bool m_enabled = false;
private bool m_ready = false;
private uint m_frame = 0;
private int m_frameUpdateRate = 1;
private int m_chatChannel = 118;
private string m_boidPrim;
private int m_flockSize = 100;
private float m_maxSpeed;
private float m_maxForce;
private float m_neighbourDistance;
private float m_desiredSeparation;
private float m_tolerance;
private UUID m_owner;
#region IRegionModule Members
public void Initialise (IConfigSource source)
{
//check if we are in the ini files
//if so get some physical constants out of them and pass into the model
IConfig config = source.Configs ["Boids"];
if (config != null) {
m_chatChannel = config.GetInt ("chat-channel", 118);
m_boidPrim = config.GetString ("boid-prim", "boidPrim");
m_flockSize = config.GetInt ("flock-size", 100);
m_maxSpeed = config.GetFloat("max-speed", 3f);
m_maxForce = config.GetFloat("max-force", 0.25f);
m_neighbourDistance = config.GetFloat("neighbour-dist", 25f);
m_desiredSeparation = config.GetFloat("desired-separation", 20f);
m_tolerance = config.GetFloat("tolerance", 5f);
// we're in the config - so turn on this module
m_enabled = true;
}
}
public void AddRegion (Scene scene)
{
m_log.Info ("ADDING FLOCKING");
m_scene = scene;
if (m_enabled) {
//register commands
RegisterCommands ();
//register handlers
m_scene.EventManager.OnFrame += FlockUpdate;
m_scene.EventManager.OnChatFromClient += SimChatSent; //listen for commands sent from the client
// init module
m_model = new FlockingModel (m_maxSpeed, m_maxForce, m_neighbourDistance, m_desiredSeparation, m_tolerance);
m_view = new FlockingView (m_scene);
m_view.BoidPrim = m_boidPrim;
}
}
public void RegionLoaded (Scene scene)
{
if (m_enabled) {
//make a flow map for this scene
FlowMap flowMap = new FlowMap(scene );
flowMap.Initialise();
// Generate initial flock values
m_model.Initialise (m_flockSize, flowMap);
// who is the owner for the flock in this region
m_owner = m_scene.RegionInfo.EstateSettings.EstateOwner;
m_view.PostInitialize (m_owner);
// Mark Module Ready for duty
m_ready = true;
}
}
public void RemoveRegion (Scene scene)
{
if (m_enabled) {
m_scene.EventManager.OnFrame -= FlockUpdate;
m_scene.EventManager.OnChatFromClient -= SimChatSent;
}
}
public string Name {
get { return "FlockingModule"; }
}
public bool IsSharedModule {
get { return false; }
}
#endregion
#region EventHandlers
public void FlockUpdate ()
{
if (((m_frame++ % m_frameUpdateRate) != 0) || !m_ready || !m_enabled) {
return;
}
//m_log.InfoFormat("update my boids");
// work out where everyone has moved to
// and tell the scene to render the new positions
lock( m_sync ) {
List<Boid > boids = m_model.UpdateFlockPos ();
m_view.Render (boids);
}
}
protected void SimChatSent (Object x, OSChatMessage msg)
{
if (m_scene.ConsoleScene () != m_scene || msg.Channel != m_chatChannel)
return; // not for us
// try and parse a valid cmd from this msg
string cmd = msg.Message.ToLower ();
//stick ui in the args so we know to respond in world
//bit of a hack - but lets us use CommandDelegate inWorld
string[] args = (cmd + " <ui>").Split (" ".ToCharArray ());
if (cmd.StartsWith ("stop")) {
HandleStopCmd ("flock", args);
} else if (cmd.StartsWith ("start")) {
HandleStartCmd ("flock", args);
} else if (cmd.StartsWith ("size")) {
HandleSetSizeCmd ("flock", args);
} else if (cmd.StartsWith ("stats")) {
HandleShowStatsCmd ("flock", args);
} else if (cmd.StartsWith ("prim")) {
HandleSetPrimCmd ("flock", args);
} else if (cmd.StartsWith ("framerate")) {
HandleSetFrameRateCmd ("flock", args);
}
}
#endregion
#region Command Handling
private void AddCommand (string cmd, string args, string help, CommandDelegate fn)
{
string argStr = "";
if (args.Trim ().Length > 0) {
argStr = " <" + args + "> ";
}
m_scene.AddCommand (this, "flock-" + cmd, "flock-" + cmd + argStr, help, fn);
}
private void RegisterCommands ()
{
AddCommand ("stop", "", "Stop all Flocking", HandleStopCmd);
AddCommand ("start", "", "Start Flocking", HandleStartCmd);
AddCommand ("size", "num", "Adjust the size of the flock ", HandleSetSizeCmd);
AddCommand ("stats", "", "show flocking stats", HandleShowStatsCmd);
AddCommand ("prim", "name", "set the prim used for each boid to that passed in", HandleSetPrimCmd);
AddCommand ("framerate", "num", "[debugging] only update boids every <num> frames", HandleSetFrameRateCmd);
}
private bool ShouldHandleCmd ()
{
return m_scene.ConsoleScene () == m_scene;
}
private bool IsInWorldCmd (ref string [] args)
{
bool retVal = false;
if (args.Length > 0 && args [args.Length - 1].Equals ("<ui>")) {
retVal = true;
}
return retVal;
}
private void ShowResponse (string response, bool inWorld)
{
if (inWorld) {
IClientAPI ownerAPI = null;
if (m_scene.TryGetClient (m_owner, out ownerAPI)) {
ownerAPI.SendBlueBoxMessage (m_owner, "osboids", response);
}
} else {
MainConsole.Instance.Output (response);
}
}
public void HandleStopCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
m_log.Info ("stop the flocking capability");
m_enabled = false;
m_view.Clear ();
}
}
void HandleSetFrameRateCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
int frameRate = Convert.ToInt32( args[1] );
m_frameUpdateRate = frameRate;
}
}
public void HandleStartCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
m_log.Info ("start the flocking capability");
m_enabled = true;
FlockUpdate ();
}
}
public void HandleSetSizeCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
lock( m_sync ) {
int newSize = Convert.ToInt32(args[1]);
m_model.Size = newSize;
m_view.Clear();
}
}
}
public void HandleShowStatsCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
bool inWorld = IsInWorldCmd (ref args);
ShowResponse ("Num Boids = " + m_model.Size, inWorld);
}
}
public void HandleSetPrimCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
string primName = args[1];
lock(m_sync) {
m_view.BoidPrim = primName;
m_view.Clear();
}
}
}
#endregion
#region IRegionModuleBase Members
public void Close ()
{
}
public Type ReplaceableInterface {
get { return null; }
}
#endregion
}
}

View File

@ -1,13 +0,0 @@
<Addin id="Flocking" version="0.1">
<Runtime>
<Import assembly="Flocking.dll"/>
</Runtime>
<Dependencies>
<Addin id="OpenSim" version="0.5" />
</Dependencies>
<Extension path = "/OpenSim/RegionModules">
<RegionModule id="Flocking" type="Flocking.FlockingModule" />
</Extension>
</Addin>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* Copyright (c) Contributors, https://github.com/jonc/osbirds
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
@ -31,7 +31,7 @@ using OpenMetaverse;
namespace Flocking
{
public class Boid
public class Bird
{
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
private string m_id;
@ -43,27 +43,34 @@ namespace Flocking
private FlockingModel m_model;
private FlowMap m_flowMap;
private int m_regionX;
private int m_regionY;
private int m_regionZ;
private float m_regionBorder;
/// <summary>
/// Initializes a new instance of the <see cref="Flocking.Boid"/> class.
/// Initializes a new instance of the <see cref="Flocking.Bird"/> class.
/// </summary>
/// <param name='l'>
/// L. the initial position of this boid
/// L. the initial position of this bird
/// </param>
/// <param name='ms'>
/// Ms. max speed this boid can attain
/// Ms. max speed this bird can attain
/// </param>
/// <param name='mf'>
/// Mf. max force / acceleration this boid can extert
/// Mf. max force / acceleration this bird can extert
/// </param>
public Boid (string id, FlockingModel model, FlowMap flowMap)
public Bird (string id, FlockingModel model, FlowMap flowMap)
{
m_id = id;
m_acc = Vector3.Zero;
m_vel = new Vector3 (m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1));
m_model = model;
m_flowMap = flowMap;
m_regionX = m_flowMap.LengthX;
m_regionY = m_flowMap.LengthY;
m_regionZ = m_flowMap.LengthZ;
m_regionBorder = m_flowMap.Border;
}
public Vector3 Location {
@ -80,15 +87,15 @@ namespace Flocking
}
/// <summary>
/// Moves our boid in the scene relative to the rest of the flock.
/// Moves our bird in the scene relative to the rest of the flock.
/// </summary>
/// <param name='boids'>
/// Boids. all the other chaps in the scene
/// <param name='birds'>
/// Birds. all the other chaps in the scene
/// </param>
public void MoveInSceneRelativeToFlock (List<Boid> boids)
public void MoveInSceneRelativeToFlock (List<Bird> birds)
{
// we would like to stay with our mates
Flock (boids);
Flock (birds);
// our first priority is to not hurt ourselves
AvoidObstacles ();
@ -112,13 +119,13 @@ namespace Flocking
/// our desire to move towards the flock centre
///
/// </summary>
void Flock (List<Boid> boids)
void Flock (List<Bird> birds)
{
// calc the force vectors on this boid
Vector3 sep = Separate (boids); // Separation
Vector3 ali = Align (boids); // Alignment
Vector3 coh = Cohesion (boids); // Cohesion
// calc the force vectors on this bird
Vector3 sep = Separate (birds); // Separation
Vector3 ali = Align (birds); // Alignment
Vector3 coh = Cohesion (birds); // Cohesion
// Arbitrarily weight these forces
//TODO: expose these consts
@ -128,7 +135,7 @@ namespace Flocking
//coh.mult(1.0);
coh *= 1.0f;
// Add the force vectors to the current acceleration of the boid
// Add the force vectors to the current acceleration of the bird
//acc.add(sep);
m_acc += sep;
//acc.add(ali);
@ -152,7 +159,7 @@ namespace Flocking
m_vel += m_acc;
// Limit speed
//m_vel.limit(maxspeed);
m_vel = Util.Limit (m_vel, m_model.MaxSpeed);
m_vel = BirdsUtil.Limit (m_vel, m_model.MaxSpeed);
m_loc += m_vel;
// Reset accelertion to 0 each cycle
m_acc *= 0.0f;
@ -202,7 +209,7 @@ namespace Flocking
//steer = target.sub(desired,m_vel);
steer = Vector3.Subtract (desired, m_vel);
//steer.limit(maxforce); // Limit to maximum steering force
steer = Util.Limit (steer, m_model.MaxForce);
steer = BirdsUtil.Limit (steer, m_model.MaxForce);
} else {
steer = Vector3.Zero;
}
@ -249,28 +256,28 @@ namespace Flocking
// try increaing our acceleration
// or try decreasing our acceleration
// or turn around - coz where we came from was OK
if (m_loc.X < 5 || m_loc.X > 250)
if (m_loc.X < m_regionBorder || m_loc.X > m_regionX - m_regionBorder)
m_vel.X = -m_vel.X;
if (m_loc.Y < 5 || m_loc.Y > 250)
if (m_loc.Y < m_regionBorder || m_loc.Y > m_regionY - m_regionBorder)
m_vel.Y = -m_vel.Y;
if (m_loc.Z < 21 || m_loc.Z > 271 )
if (m_loc.Z < 21 || m_loc.Z > m_regionZ )
m_vel.Z = -m_vel.Z;
}
/// <summary>
/// Separate ourselves from the specified boids.
/// Separate ourselves from the specified birds.
/// keeps us a respectable distance from our closest neighbours whilst still
/// being part of our local flock
/// </summary>
/// <param name='boids'>
/// Boids. all the boids in the scene
/// <param name='birds'>
/// Birds. all the birds in the scene
/// </param>
Vector3 Separate (List<Boid> boids)
Vector3 Separate (List<Bird> birds)
{
Vector3 steer = new Vector3 (0, 0, 0);
int count = 0;
// For every boid in the system, check if it's too close
foreach (Boid other in boids) {
// For every bird in the system, check if it's too close
foreach (Bird other in birds) {
float d = Vector3.Distance (m_loc, other.Location);
// If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
if ((d > 0) && (d < m_model.DesiredSeparation)) {
@ -294,24 +301,24 @@ namespace Flocking
steer *= m_model.MaxSpeed;
steer -= m_vel;
//steer.limit(maxforce);
steer = Util.Limit (steer, m_model.MaxForce);
steer = BirdsUtil.Limit (steer, m_model.MaxForce);
}
return steer;
}
/// <summary>
/// Align our boid within the flock.
/// For every nearby boid in the system, calculate the average velocity
/// Align our bird within the flock.
/// For every nearby bird in the system, calculate the average velocity
/// and move us towards that - this keeps us moving with the flock.
/// </summary>
/// <param name='boids'>
/// Boids. all the boids in the scene - we only really care about those in the neighbourdist
/// <param name='birds'>
/// Birds. all the birds in the scene - we only really care about those in the neighbourdist
/// </param>
Vector3 Align (List<Boid> boids)
Vector3 Align (List<Bird> birds)
{
Vector3 steer = new Vector3 (0, 0, 0);
int count = 0;
foreach (Boid other in boids) {
foreach (Bird other in birds) {
float d = Vector3.Distance (m_loc, other.Location);
if ((d > 0) && (d < m_model.NeighbourDistance)) {
steer += other.Velocity;
@ -329,7 +336,7 @@ namespace Flocking
steer *= m_model.MaxSpeed;
steer -= m_vel;
//steer.limit(maxforce);
steer = Util.Limit (steer, m_model.MaxForce);
steer = BirdsUtil.Limit (steer, m_model.MaxForce);
}
return steer;
@ -337,18 +344,18 @@ namespace Flocking
/// <summary>
/// MAintain the cohesion of our local flock
/// For the average location (i.e. center) of all nearby boids, calculate our steering vector towards that location
/// For the average location (i.e. center) of all nearby birds, calculate our steering vector towards that location
/// </summary>
/// <param name='boids'>
/// Boids. the boids in the scene
/// <param name='birds'>
/// Birds. the birds in the scene
/// </param>
Vector3 Cohesion (List<Boid> boids)
Vector3 Cohesion (List<Bird> birds)
{
Vector3 sum = Vector3.Zero; // Start with empty vector to accumulate all locations
int count = 0;
foreach (Boid other in boids) {
foreach (Bird other in birds) {
float d = Vector3.Distance (m_loc, other.Location);
if ((d > 0) && (d < m_model.NeighbourDistance)) {
sum += other.Location; // Add location

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* https://github.com/JakDaniels/OpenSimBirds
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
@ -29,7 +30,7 @@ using OpenMetaverse;
namespace Flocking
{
public class Util
public class BirdsUtil
{
public static Vector3 Limit (Vector3 initial, float maxLen)
{

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* https://github.com/JakDaniels/OpenSimBirds
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
@ -32,40 +33,46 @@ namespace Flocking
{
public class FlockingModel
{
private List<Boid> m_flock = new List<Boid>();
private List<Bird> m_flock = new List<Bird>();
private FlowMap m_flowMap;
private float m_maxSpeed;
private float m_maxForce;
private float m_neighbourDistance;
private float m_desiredSeparation;
private float m_tolerance;
private float m_border;
private string m_name;
private Random m_rnd = new Random(Environment.TickCount);
public int Size {
get {return m_flock.Count;}
set {
if( value < m_flock.Count ) {
m_flock.RemoveRange( 0, m_flock.Count - value );
} else while( value > m_flock.Count ) {
AddBoid( "boid"+m_flock.Count);
//if( value < m_flock.Count ) {
// m_flock.RemoveRange( 0, m_flock.Count - value );
//} else
m_flock = new List<Bird>();
while( value > m_flock.Count ) {
AddBird(m_name + m_flock.Count);
}
}
}
public FlockingModel( float maxSpeed, float maxForce, float neighbourDistance, float desiredSeparation, float tolerance ) {
m_maxSpeed = maxSpeed;
public FlockingModel(string moduleName, float maxSpeed, float maxForce, float neighbourDistance, float desiredSeparation, float tolerance, float border) {
m_name = moduleName;
m_maxSpeed = maxSpeed;
m_maxForce = maxForce;
m_neighbourDistance = neighbourDistance;
m_desiredSeparation = desiredSeparation;
m_tolerance = tolerance;
m_border = border;
}
void AddBoid (string name)
void AddBird (string name)
{
Boid boid = new Boid (name, this, m_flowMap);
Bird Bird = new Bird (name, this, m_flowMap);
// find an initial random location for this Boid
// find an initial random location for this Bird
// somewhere not within an obstacle
int xInit = m_rnd.Next(m_flowMap.LengthX);
int yInit = m_rnd.Next(m_flowMap.LengthY);
@ -77,28 +84,33 @@ namespace Flocking
zInit = m_rnd.Next(m_flowMap.LengthZ);
}
boid.Location = new Vector3 (Convert.ToSingle(xInit), Convert.ToSingle(yInit), Convert.ToSingle(zInit));
m_flock.Add (boid);
Bird.Location = new Vector3 (Convert.ToSingle(xInit), Convert.ToSingle(yInit), Convert.ToSingle(zInit));
m_flock.Add (Bird);
}
public float MaxSpeed {
get {return m_maxSpeed;}
set { m_maxSpeed = value; }
}
public float MaxForce {
get {return m_maxForce;}
set { m_maxForce = value; }
}
public float NeighbourDistance {
get {return m_neighbourDistance;}
set { m_neighbourDistance = value; }
}
public float DesiredSeparation {
get {return m_desiredSeparation;}
set { m_desiredSeparation = value; }
}
public float Tolerance {
get {return m_tolerance;}
set { m_tolerance = value; }
}
@ -106,14 +118,14 @@ namespace Flocking
{
m_flowMap = flowMap;
for (int i = 0; i < num; i++) {
AddBoid ("boid"+i );
AddBird (m_name + i );
}
}
public List<Boid> UpdateFlockPos ()
public List<Bird> UpdateFlockPos ()
{
foreach (Boid b in m_flock) {
b.MoveInSceneRelativeToFlock(m_flock); // Passing the entire list of boids to each boid individually
foreach (Bird b in m_flock) {
b.MoveInSceneRelativeToFlock(m_flock); // Passing the entire list of Birds to each Bird individually
}
return m_flock;

View File

@ -0,0 +1,648 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* https://github.com/JakDaniels/OpenSimBirds
* 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 System;
using System.Timers;
using System.Net;
using System.IO;
using System.Text;
using System.Collections.Generic;
using OpenMetaverse;
using Nini.Config;
using System.Threading;
using log4net;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using Mono.Addins;
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
[assembly: Addin("OpenSimBirds", "0.2")]
[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
namespace Flocking
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "OpenSimBirds")]
public class FlockingModule : INonSharedRegionModule
{
#region Fields
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
public string m_name = "OpenSimBirds";
private string m_regionConfigDir = "";
private Scene m_scene;
private ICommandConsole m_console;
private FlockingModel m_model;
private FlockingView m_view;
private bool m_startup = true;
private bool m_enabled = false;
private bool m_ready = false;
private uint m_frame = 0;
private int m_frameUpdateRate = 1;
private int m_chatChannel = 118;
private string m_birdPrim;
private int m_flockSize = 50;
private int m_maxFlockSize = 100;
private float m_maxSpeed;
private float m_maxForce;
private float m_neighbourDistance;
private float m_desiredSeparation;
private float m_tolerance;
private float m_borderSize;
private int m_maxHeight;
private Vector3 m_shoutPos = new Vector3(128f, 128f, 30f);
static object m_sync = new object();
private List<UUID> m_allowedControllers = new List<UUID>();
public IConfigSource m_config;
private UUID m_owner;
#endregion
#region IRegionModuleBase implementation
public string Name { get { return m_name; } }
public Type ReplaceableInterface { get { return null; } }
public bool IsSharedModule { get { return false; } }
public void Initialise (IConfigSource source)
{
m_config = source;
}
public void AddRegion (Scene scene)
{
IConfig cnf;
m_log.InfoFormat("[{0}]: Adding region {1} to this module", m_name, scene.RegionInfo.RegionName);
cnf = m_config.Configs["Startup"];
m_regionConfigDir = cnf.GetString("regionload_regionsdir", Path.Combine(Util.configDir(), "bin/Regions/"));
cnf = m_config.Configs[scene.RegionInfo.RegionName];
if (cnf == null)
{
m_log.InfoFormat("[{0}]: No region section [{1}] found in addon-modules/{2}/config/*.ini configuration files.", m_name, scene.RegionInfo.RegionName, m_name);
//string moduleConfigFile = Path.Combine(Util.configDir(),m_name + ".ini");
string moduleConfigFile = Path.Combine(m_regionConfigDir, "Regions.ini");
try
{
m_log.InfoFormat("[{0}]: Checking {1} for [{2}] section containing valid config keys", m_name, moduleConfigFile, scene.RegionInfo.RegionName);
m_config = new IniConfigSource(moduleConfigFile);
cnf = m_config.Configs[scene.RegionInfo.RegionName];
}
catch (Exception)
{
cnf = null;
}
if (cnf == null)
{
m_log.InfoFormat("[{0}]: No region section [{1}] found in configuration {2}. Birds in this region are set to Disabled", m_name, scene.RegionInfo.RegionName, moduleConfigFile);
m_enabled = false;
return;
}
}
m_startup = cnf.GetBoolean("BirdsModuleStartup", true);
if (m_startup)
{
m_scene = scene;
m_enabled = cnf.GetBoolean("BirdsEnabled", false);
m_chatChannel = cnf.GetInt("BirdsChatChannel", 118);
m_birdPrim = cnf.GetString("BirdsPrim", "birdPrim");
m_flockSize = cnf.GetInt("BirdsFlockSize", 20);
m_maxFlockSize = cnf.GetInt("BirdsMaxFlockSize", 100);
m_maxSpeed = cnf.GetFloat("BirdsMaxSpeed", 1.5f);
m_maxForce = cnf.GetFloat("BirdsMaxForce", 0.2f);
m_neighbourDistance = cnf.GetFloat("BirdsNeighbourDistance", 25f);
m_desiredSeparation = cnf.GetFloat("BirdsDesiredSeparation", 10f);
m_tolerance = cnf.GetFloat("BirdsTolerance", 5f);
m_borderSize = cnf.GetFloat("BirdsRegionBorderSize", 5f);
m_maxHeight = cnf.GetInt("BirdsMaxHeight", 75);
m_frameUpdateRate = cnf.GetInt("BirdsUpdateEveryNFrames", 1);
string allowedControllers = cnf.GetString("BirdsAllowedControllers", UUID.Zero.ToString());
if (allowedControllers != UUID.Zero.ToString())
{
string[] ac = allowedControllers.Split(new char[] { ',' });
UUID acUUID;
for (int i = 0; i < ac.Length; i++)
{
string value = ac[i].Trim();
if (value == "ESTATE_OWNER")
{
UUID eoUUID = m_scene.RegionInfo.EstateSettings.EstateOwner;
m_allowedControllers.Add(eoUUID);
m_log.InfoFormat("[{0}] Added Estate Owner UUID: {1} to list of allowed users", m_name, eoUUID.ToString());
continue;
}
if (value == "ESTATE_MANAGER")
{
foreach (UUID emUUID in m_scene.RegionInfo.EstateSettings.EstateManagers)
{
m_allowedControllers.Add(emUUID);
m_log.InfoFormat("[{0}] Added Estate Manager UUID: {1} to list of allowed users", m_name, emUUID.ToString());
}
continue;
}
if (UUID.TryParse(ac[i].Trim(), out acUUID))
{
m_allowedControllers.Add(acUUID);
m_log.InfoFormat("[{0}] Added UUID: {1} to list of allowed users", m_name, acUUID.ToString());
}
}
}
else
{
m_log.InfoFormat("[{0}] No command security was defined in the config. Any user may possibly configure this module from a script!", m_name);
}
m_log.InfoFormat("[{0}] Module is {1} listening for commands on channel {2} with Flock Size {3}", m_name, m_enabled ? "enabled and" : "disabled, but still", m_chatChannel, m_flockSize);
m_console = MainConsole.Instance;
//register commands with the scene
RegisterCommands();
//register handlers
m_scene.EventManager.OnFrame += FlockUpdate;
m_scene.EventManager.OnChatFromClient += SimChatSent; //listen for commands sent from the client
m_scene.EventManager.OnChatFromWorld += SimChatSent;
m_scene.EventManager.OnPrimsLoaded += PrimsLoaded;
// init module
m_model = new FlockingModel(m_name, m_maxSpeed, m_maxForce, m_neighbourDistance, m_desiredSeparation, m_tolerance, m_borderSize);
m_view = new FlockingView(m_name, m_scene);
m_view.BirdPrim = m_birdPrim;
m_frame = 0;
m_shoutPos = new Vector3(scene.RegionInfo.RegionSizeX / 2f, scene.RegionInfo.RegionSizeY / 2f, 30f);
FlockInitialise();
}
else m_log.InfoFormat("[{0}] Module is disabled in Region {1}", m_name, scene.RegionInfo.RegionName);
}
public void RegionLoaded (Scene scene)
{
if (m_startup)
{
// Mark Module Ready for duty
m_ready = true;
}
}
public void PrimsLoaded(Scene scene)
{
m_scene = scene;
ClearPersisted();
}
public void RemoveRegion (Scene scene)
{
m_log.InfoFormat("[{0}]: Removing region {1} from this module", m_name, scene.RegionInfo.RegionName);
if (m_startup) {
m_view.Clear();
scene.EventManager.OnFrame -= FlockUpdate;
scene.EventManager.OnChatFromClient -= SimChatSent;
scene.EventManager.OnChatFromWorld -= SimChatSent;
scene.EventManager.OnPrimsLoaded -= PrimsLoaded;
m_ready = false;
}
}
public void Close()
{
if (m_startup)
{
m_scene.EventManager.OnFrame -= FlockUpdate;
m_scene.EventManager.OnChatFromClient -= SimChatSent;
m_scene.EventManager.OnChatFromWorld -= SimChatSent;
m_scene.EventManager.OnPrimsLoaded -= PrimsLoaded;
m_ready = false;
}
}
#endregion
#region Helpers
public void FlockInitialise()
{
//make a flow map for this scene
FlowMap flowMap = new FlowMap(m_scene, m_maxHeight, m_borderSize);
flowMap.Initialise();
// Generate initial flock values
m_model.Initialise(m_flockSize, flowMap);
// who is the owner for the flock in this region
m_owner = m_scene.RegionInfo.EstateSettings.EstateOwner;
m_view.PostInitialize(m_owner);
}
public void ClearPersisted()
{
//really trash all possible birds that may have been persisted at last shutdown
int i;
for (i = m_flockSize; i < m_maxFlockSize; i++)
{
foreach (EntityBase e in m_scene.GetEntities())
{
if (e.Name == (string)(m_name + i))
{
m_log.InfoFormat("[{0}]: Removing old persisted prim {1} from region {2}", m_name, m_name + i, m_scene.RegionInfo.RegionName);
m_scene.DeleteSceneObject((SceneObjectGroup)e, false);
break;
}
}
}
m_scene.ForceClientUpdate();
}
#endregion
#region EventHandlers
public void FlockUpdate ()
{
if (!m_ready || !m_enabled || ((m_frame++ % m_frameUpdateRate) != 0))
{
return;
}
//m_log.InfoFormat("update my birds");
// work out where everyone has moved to
// and tell the scene to render the new positions
lock( m_sync ) {
List<Bird > birds = m_model.UpdateFlockPos ();
m_view.Render (birds);
}
}
protected void SimChatSent (Object x, OSChatMessage msg)
{
if (msg.Channel != m_chatChannel) return; // not for us
if (m_allowedControllers.Count > 0)
{
bool reject = true;
if (msg.SenderObject != null)
{
UUID ooUUID = ((SceneObjectPart)msg.SenderObject).OwnerID;
//m_log.InfoFormat("[{0}]: Message from object {1} with OwnerID: {2}", m_name, msg.SenderUUID, ooUUID);
if (m_allowedControllers.Contains(ooUUID)) reject = false;
}
if (m_allowedControllers.Contains(msg.SenderUUID)) reject = false;
if (reject) return; //not for us
}
// try and parse a valid cmd from this msg
string cmd = msg.Message; //.ToLower ();
//stick ui in the args so we know to respond in world
//bit of a hack - but lets us use CommandDelegate inWorld
string[] args = (cmd + " <ui>").Split (" ".ToCharArray ());
if (cmd.StartsWith ("stop")) {
HandleStopCmd (m_name, args);
} else if (cmd.StartsWith ("start")) {
HandleStartCmd (m_name, args);
} else if (cmd.StartsWith("enable")) {
HandleEnableCmd(m_name, args);
} else if (cmd.StartsWith("disable")) {
HandleDisableCmd(m_name, args);
} else if (cmd.StartsWith ("size")) {
HandleSetSizeCmd (m_name, args);
} else if (cmd.StartsWith ("stats")) {
HandleShowStatsCmd (m_name, args);
} else if (cmd.StartsWith ("prim")) {
HandleSetPrimCmd (m_name, args);
} else if (cmd.StartsWith("speed")) {
HandleSetMaxSpeedCmd(m_name, args);
} else if (cmd.StartsWith("force")) {
HandleSetMaxForceCmd(m_name, args);
} else if (cmd.StartsWith("distance")) {
HandleSetNeighbourDistanceCmd(m_name, args);
} else if (cmd.StartsWith("separation")) {
HandleSetDesiredSeparationCmd(m_name, args);
} else if (cmd.StartsWith("tolerance")) {
HandleSetToleranceCmd(m_name, args);
} else if (cmd.StartsWith ("framerate")) {
HandleSetFrameRateCmd (m_name, args);
}
}
#endregion
#region Command Handling
private void AddCommand (string cmd, string args, string help, CommandDelegate fn)
{
string argStr = "";
if (args.Trim ().Length > 0) {
argStr = " <" + args + "> ";
}
m_log.InfoFormat("[{0}]: Adding console command {1} - {2} to region {3}", m_name, "birds-" + cmd + argStr, help, m_scene.RegionInfo.RegionName);
//m_scene.AddCommand (this, "birds-" + cmd, "birds-" + cmd + argStr, help, fn);
m_console.Commands.AddCommand(m_name, false, "birds-" + cmd, "birds-" + cmd + argStr, help, fn);
}
private void RegisterCommands ()
{
AddCommand ("stop", "", "Stop Birds Flocking", HandleStopCmd);
AddCommand ("start", "", "Start Birds Flocking", HandleStartCmd);
AddCommand ("enable", "", "Enable Birds Flocking", HandleEnableCmd);
AddCommand ("disable", "", "Disable Birds Flocking", HandleDisableCmd);
AddCommand ("size", "num", "Adjust the size of the flock ", HandleSetSizeCmd);
AddCommand ("prim", "name", "Set the prim used for each bird to that passed in", HandleSetPrimCmd);
AddCommand ("speed", "num", "Set the maximum velocity each bird may achieve", HandleSetMaxSpeedCmd);
AddCommand("force", "num", "Set the maximum force each bird may accelerate", HandleSetMaxForceCmd);
AddCommand("distance", "num", "Set the maximum distance that other birds are to be considered in the same flock as us", HandleSetNeighbourDistanceCmd);
AddCommand("separation", "num", "How far away from other birds we would like to stay", HandleSetDesiredSeparationCmd);
AddCommand("tolerance", "num", "How close to the edges of things can we get without being worried", HandleSetToleranceCmd);
AddCommand("stats", "", "Show birds stats", HandleShowStatsCmd);
AddCommand("framerate", "num", "[debugging] only update birds every <num> frames", HandleSetFrameRateCmd);
}
private void RegisterScriptFunctions()
{
IScriptModuleComms comms = m_scene.RequestModuleInterface<IScriptModuleComms>();
if (comms != null)
{
comms.RegisterScriptInvocation(this, "birdsGetStats");
}
}
private string birdsGetStats(UUID host, UUID script, string stat)
{
return ""; //currently a placeholder
}
private bool ShouldHandleCmd ()
{
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) {
return false;
}
return true;
}
private bool IsInWorldCmd (ref string [] args)
{
if (args.Length > 0 && args [args.Length - 1].Equals ("<ui>")) {
m_log.InfoFormat("[{0}]: Inworld command detected in region {1}", m_name, m_scene.RegionInfo.RegionName);
return true;
}
return false;
}
private void ShowResponse (string response, bool inWorld)
{
if (inWorld) {
//IClientAPI ownerAPI = null;
//if (m_scene.TryGetClient (m_owner, out ownerAPI)) {
// ownerAPI.SendBlueBoxMessage (m_owner, "Birds", response);
//}
SendSimChat(response, m_chatChannel);
} else {
MainConsole.Instance.Output (response);
}
}
private void SendSimChat(string msg, int channel)
{
m_scene.SimChatBroadcast(Utils.StringToBytes(msg), ChatTypeEnum.Region, channel, m_shoutPos, m_name, UUID.Zero, false);
}
public void HandleDisableCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd ()) {
m_log.InfoFormat("[{0}]: Bird flocking is disabled in region {1}.", m_name, m_scene.RegionInfo.RegionName);
m_enabled = false;
//m_ready = false;
m_view.Clear();
}
}
public void HandleEnableCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
m_log.InfoFormat("[{0}]: Bird flocking is enabled in region {1}.", m_name, m_scene.RegionInfo.RegionName);
m_enabled = true;
//m_ready = true;
}
}
public void HandleStopCmd (string module, string[] args)
{
if (m_enabled && m_ready && ShouldHandleCmd())
{
m_log.InfoFormat("[{0}]: Bird flocking is stopped in region {1}.", m_name, m_scene.RegionInfo.RegionName);
m_enabled = false;
}
}
public void HandleStartCmd(string module, string[] args)
{
if (!m_enabled && m_ready && ShouldHandleCmd())
{
m_log.InfoFormat("[{0}]: Bird flocking is started in region {1}.", m_name, m_scene.RegionInfo.RegionName);
m_enabled = true;
FlockUpdate();
}
}
void HandleSetFrameRateCmd (string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
int frameRate = Convert.ToInt32( args[1] );
m_frameUpdateRate = frameRate;
m_log.InfoFormat("[{0}]: Bird updates set to every {1} frames in region {2}.", m_name, frameRate, m_scene.RegionInfo.RegionName);
}
}
public void HandleSetSizeCmd (string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
lock( m_sync ) {
int newSize = Convert.ToInt32(args[1]);
if (newSize > m_maxFlockSize) newSize = m_maxFlockSize;
m_view.Clear();
m_model.Size = newSize;
m_log.InfoFormat("[{0}]: Bird flock size is set to {1} in region {2}.", m_name, newSize, m_scene.RegionInfo.RegionName);
}
}
}
public void HandleShowStatsCmd (string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
bool inWorld = IsInWorldCmd (ref args);
int i;
int s=m_model.Size;
UUID primUuid;
List<ScenePresence> avatarsSitList = new List<ScenePresence>();
string avUuids;
if (inWorld)
{
m_log.InfoFormat("[{0}]: Sending bird statistics to region {1}.", m_name, m_scene.RegionInfo.RegionName);
}
ShowResponse("birds-started = " + (m_enabled ? "True" : "False"), inWorld);
ShowResponse("birds-enabled = " + (m_ready ? "True" : "False"), inWorld);
ShowResponse("birds-prim = " + m_view.BirdPrim, inWorld);
ShowResponse("birds-framerate = " + m_frameUpdateRate, inWorld);
ShowResponse("birds-maxsize = " + m_maxFlockSize, inWorld);
ShowResponse("birds-size = " + s, inWorld);
ShowResponse("birds-speed = " + m_model.MaxSpeed, inWorld);
ShowResponse("birds-force = " + m_model.MaxForce, inWorld);
ShowResponse("birds-distance = " + m_model.NeighbourDistance, inWorld);
ShowResponse("birds-separation = " + m_model.DesiredSeparation, inWorld);
ShowResponse("birds-tolerance = " + m_model.Tolerance, inWorld);
ShowResponse("birds-border = " + m_borderSize, inWorld);
for (i = 0; i < s; i++)
{
primUuid = UUID.Zero;
avatarsSitList.Clear();
avUuids = "";
foreach (EntityBase e in m_scene.GetEntities())
{
if (e.Name == m_name + i)
{
SceneObjectGroup sog = (SceneObjectGroup)e;
SceneObjectPart rootPart = sog.RootPart;
primUuid = rootPart.UUID;
avatarsSitList = sog.GetSittingAvatars();
foreach (ScenePresence av in avatarsSitList)
{
avUuids += av.UUID.ToString() + " ";
}
break;
}
}
ShowResponse("birds-prim" + i + " = " + m_name + i + " : " + primUuid.ToString() + " : " + avUuids.Trim(), inWorld);
}
}
}
public void HandleSetPrimCmd (string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
string primName = args[1];
lock(m_sync) {
m_view.BirdPrim = primName;
m_log.InfoFormat("[{0}]: Bird prim is set to {1} in region {2}.", m_name, primName, m_scene.RegionInfo.RegionName);
m_view.Clear();
}
}
}
public void HandleSetMaxSpeedCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
float maxSpeed = (float)Convert.ToDecimal(args[1]);
lock (m_sync)
{
m_model.MaxSpeed = maxSpeed;
m_log.InfoFormat("[{0}]: Birds maximum speed is set to {1} in region {2}.", m_name, maxSpeed, m_scene.RegionInfo.RegionName);
}
}
}
public void HandleSetMaxForceCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
float maxForce = (float)Convert.ToDecimal(args[1]);
lock (m_sync)
{
m_model.MaxForce = maxForce;
m_log.InfoFormat("[{0}]: Birds maximum force is set to {1} in region {2}.", m_name, maxForce, m_scene.RegionInfo.RegionName);
}
}
}
public void HandleSetNeighbourDistanceCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
float neighbourDistance = (float)Convert.ToDecimal(args[1]);
lock (m_sync)
{
m_model.NeighbourDistance = neighbourDistance;
m_log.InfoFormat("[{0}]: Birds neighbour distance is set to {1} in region {2}.", m_name, neighbourDistance, m_scene.RegionInfo.RegionName);
}
}
}
public void HandleSetDesiredSeparationCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
float desiredSeparation = (float)Convert.ToDecimal(args[1]);
lock (m_sync)
{
m_model.DesiredSeparation = desiredSeparation;
m_log.InfoFormat("[{0}]: Birds desired separation is set to {1} in region {2}.", m_name, desiredSeparation, m_scene.RegionInfo.RegionName);
}
}
}
public void HandleSetToleranceCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
float tolerance = (float)Convert.ToDecimal(args[1]);
lock (m_sync)
{
m_model.Tolerance = tolerance;
m_log.InfoFormat("[{0}]: Birds tolerance is set to {1} in region {2}.", m_name, tolerance, m_scene.RegionInfo.RegionName);
}
}
}
#endregion
}
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* https://github.com/JakDaniels/OpenSimBirds
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
@ -29,20 +30,24 @@ using System.Collections.Generic;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using log4net;
namespace Flocking
{
public class FlockingView
{
private Scene m_scene;
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private Scene m_scene;
private UUID m_owner;
private String m_boidPrim;
private String m_name;
private String m_birdPrim;
private Dictionary<string, SceneObjectGroup> m_sogMap = new Dictionary<string, SceneObjectGroup> ();
public FlockingView (Scene scene)
public FlockingView (String moduleName, Scene scene)
{
m_scene = scene;
m_name = moduleName;
m_scene = scene;
}
public void PostInitialize (UUID owner)
@ -50,8 +55,9 @@ namespace Flocking
m_owner = owner;
}
public String BoidPrim {
set{ m_boidPrim = value;}
public String BirdPrim {
get { return m_birdPrim; }
set{ m_birdPrim = value;}
}
public void Clear ()
@ -59,41 +65,61 @@ namespace Flocking
//trash everything we have
foreach (string name in m_sogMap.Keys)
{
RemoveSOGFromScene(name);
m_log.InfoFormat("[{0}]: Removing prim {1} from region {2}", m_name, name, m_scene.RegionInfo.RegionName);
SceneObjectGroup sog = m_sogMap[name];
m_scene.DeleteSceneObject(sog, false);
}
m_sogMap.Clear();
m_scene.ForceClientUpdate();
}
public void Render (List<Boid> boids)
public void Render(List<Bird> birds)
{
foreach (Boid boid in boids) {
DrawBoid (boid);
foreach (Bird bird in birds) {
DrawBird (bird);
}
}
private void DrawBoid (Boid boid)
private void DrawBird (Bird bird)
{
SceneObjectPart existing = m_scene.GetSceneObjectPart (boid.Id);
SceneObjectPart existing = m_scene.GetSceneObjectPart (bird.Id);
SceneObjectGroup sog;
SceneObjectPart rootPart;
if (existing == null) {
SceneObjectGroup group = findByName (m_boidPrim);
sog = CopyPrim (group, boid.Id);
m_sogMap [boid.Id] = sog;
m_log.InfoFormat("[{0}]: Adding prim {1} in region {2}", m_name, bird.Id, m_scene.RegionInfo.RegionName);
SceneObjectGroup group = findByName (m_birdPrim);
sog = CopyPrim (group, bird.Id);
rootPart = sog.RootPart;
rootPart.AddFlag(PrimFlags.Temporary);
rootPart.AddFlag(PrimFlags.Phantom);
//set prim to phantom
//sog.UpdatePrimFlags(rootPart.LocalId, false, false, true, false);
m_sogMap [bird.Id] = sog;
m_scene.AddNewSceneObject (sog, false);
// Fire script on_rez
sog.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 1);
rootPart.ParentGroup.ResumeScripts();
rootPart.ScheduleFullUpdate();
sog.DetachFromBackup();
} else {
sog = existing.ParentGroup;
m_sogMap[bird.Id] = sog;
//rootPart = sog.RootPart;
//set prim to phantom
//sog.UpdatePrimFlags(rootPart.LocalId, false, false, true, false);
}
Quaternion rotation = CalcRotationToEndpoint (sog, sog.AbsolutePosition, boid.Location);
sog.UpdateGroupRotationPR( boid.Location, rotation);
Quaternion rotation = CalcRotationToEndpoint (sog, sog.AbsolutePosition, bird.Location);
sog.UpdateGroupRotationPR( bird.Location, rotation);
}
private static Quaternion CalcRotationToEndpoint (SceneObjectGroup copy, Vector3 sv, Vector3 ev)
{
//llSetRot(llRotBetween(<1,0,0>,llVecNorm(targetPosition - llGetPos())));
// boid wil fly x forwards and Z up
// bird wil fly x forwards and Z up
Vector3 currDirVec = Vector3.UnitX;
Vector3 desiredDirVec = Vector3.Subtract (ev, sv);
@ -120,9 +146,9 @@ namespace Flocking
break;
}
}
// can't find it so make a default one
if (retVal == null) {
m_log.InfoFormat("[{0}]: Prim named {1} was not found in region {2}. Making default wooden sphere.", m_name, name, m_scene.RegionInfo.RegionName);
retVal = MakeDefaultPrim (name);
}
@ -132,9 +158,9 @@ namespace Flocking
private SceneObjectGroup MakeDefaultPrim (string name)
{
PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere ();
shape.Scale = new Vector3 (0.5f, 0.5f, 0.5f);
shape.Scale = new Vector3 (0.5f, 0.5f, 0.5f);
SceneObjectGroup prim = new SceneObjectGroup (m_owner, new Vector3 (128f, 128f, 25f), shape);
SceneObjectGroup prim = new SceneObjectGroup(m_owner, new Vector3((float)m_scene.RegionInfo.RegionSizeX / 2, (float)m_scene.RegionInfo.RegionSizeY / 2, 25f), shape);
prim.Name = name;
prim.DetachFromBackup ();
m_scene.AddNewSceneObject (prim, false);
@ -142,14 +168,6 @@ namespace Flocking
return prim;
}
private void RemoveSOGFromScene(string sogName)
{
SceneObjectGroup sog = m_sogMap[sogName];
m_scene.DeleteSceneObject(sog, false);
}
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flocking
{
class FlowEntry
{
public float X = 0;
public float Y = 0;
public float Z = 0;
public float Value = 0;
public FlowEntry(float x, float y, float z, float value)
{
X = x;
Y = y;
Z = z;
Value = value;
}
}
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* https://github.com/JakDaniels/OpenSimBirds
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
@ -26,6 +27,7 @@
*/
using System;
using System.Collections.Generic;
using OpenMetaverse;
using OpenSim.Region.Framework.Scenes;
@ -34,50 +36,63 @@ namespace Flocking
public class FlowMap
{
private Scene m_scene;
private float[,,] m_flowMap = new float[256,256,256];
public FlowMap (Scene scene)
private uint regionX;
private uint regionY;
private uint regionZ;
private float regionBorder;
private List<FlowEntry> m_flowMap = new List<FlowEntry>();
public FlowMap (Scene scene, int maxHeight, float borderSize)
{
m_scene = scene;
regionX = m_scene.RegionInfo.RegionSizeX;
regionY = m_scene.RegionInfo.RegionSizeY;
regionZ = (uint)maxHeight;
regionBorder = borderSize;
}
public int LengthX {
get {return 256;}
get {return (int)regionX;}
}
public int LengthY {
get {return 256;}
get {return (int)regionY;}
}
public int LengthZ {
get {return 256;}
get {return (int)regionZ;}
}
public int Border {
get {return (int)regionBorder;}
}
public void Initialise() {
//fill in the boundaries
for( int x = 0; x < 256; x++ ) {
for( int y = 0; y < 256; y++ ) {
m_flowMap[x,y,0] = 100f;
m_flowMap[x,y,255] = 100f;
for( int x = 0; x < regionX; x++ ) {
for( int y = 0; y < regionY; y++ ) {
m_flowMap.Add(new FlowEntry(x, y, 0, 100));
m_flowMap.Add(new FlowEntry(x, y, regionZ - 1, 100));
}
}
for( int x = 0; x < 256; x++ ) {
for( int z = 0; z < 256; z++ ) {
m_flowMap[x,0,z] = 100f;
m_flowMap[x,255,z] = 100f;
for( int x = 0; x < regionX; x++ ) {
for( int z = 0; z < regionZ; z++ ) {
m_flowMap.Add(new FlowEntry(x, 0, z, 100));
m_flowMap.Add(new FlowEntry(x, regionY - 1, z, 100));
}
}
for( int y = 0; y < 256; y++ ) {
for( int z = 0; z < 256; z++ ) {
m_flowMap[0,y,z] = 100f;
m_flowMap[255,y,z] = 100f;
for( int y = 0; y < regionY; y++ ) {
for( int z = 0; z < regionZ; z++ ) {
m_flowMap.Add(new FlowEntry(0, y, z, 100));
m_flowMap.Add(new FlowEntry(regionX - 1, y, z, 100));
}
}
//fill in the terrain
for( int x = 0; x < 256; x++ ) {
for( int y = 0; y < 256; y++ ) {
for( int x = 0; x < regionX; x++ ) {
for( int y = 0; y < regionY; y++ ) {
int zMax = Convert.ToInt32(m_scene.GetGroundHeight( x, y ));
for( int z = 1; z < zMax; z++ ) {
m_flowMap[x,y,z] = 100f;
if (x < regionX && y < regionY && z < zMax)
m_flowMap.Add(new FlowEntry(x, y, z, 100));
}
}
}
@ -86,8 +101,10 @@ namespace Flocking
foreach( EntityBase entity in m_scene.GetEntities() ) {
if( entity is SceneObjectGroup ) {
SceneObjectGroup sog = (SceneObjectGroup)entity;
//todo: ignore phantom
if (sog.IsPhantom)
continue;
float fmaxX, fminX, fmaxY, fminY, fmaxZ, fminZ;
int maxX, minX, maxY, minY, maxZ, minZ;
sog.GetAxisAlignedBoundingBoxRaw( out fminX, out fmaxX, out fminY, out fmaxY, out fminZ, out fmaxZ );
@ -102,7 +119,8 @@ namespace Flocking
for( int x = minX; x < maxX; x++ ) {
for( int y = minY; y < maxY; y++ ) {
for( int z = minZ; z < maxZ; z++ ) {
m_flowMap[x,y,z] = 100f;
if(x < maxX && y < maxY && z < maxX)
m_flowMap.Add(new FlowEntry(x, y, z, 100));
}
}
}
@ -127,12 +145,12 @@ namespace Flocking
public bool IsOutOfBounds(Vector3 targetPos) {
bool retVal = false;
if( targetPos.X < 5f ||
targetPos.X > 250f ||
targetPos.Y < 5f ||
targetPos.Y > 250f ||
targetPos.Z < 5f ||
targetPos.Z > 250f ) {
if( targetPos.X < regionBorder ||
targetPos.X > regionX - regionBorder ||
targetPos.Y < regionBorder ||
targetPos.Y > regionY - regionBorder ||
targetPos.Z < regionBorder ||
targetPos.Z > regionZ - regionBorder ) {
retVal = true;
}
@ -169,11 +187,20 @@ namespace Flocking
public bool IsWithinObstacle( int x, int y, int z ) {
bool retVal = false;
if( x > LengthX || y > LengthY || z > LengthZ ) {
FlowEntry flow = m_flowMap.Find(X => X.X == x && X.Y == y && X.Z == z);
if (flow == null)
return retVal;
if (x >= LengthX || y >= LengthY || z >= LengthZ)
{
retVal = true;
} else if( x < 0 || y < 0 || z < 0 ) {
}
else if (x < 0 || y < 0 || z < 0)
{
retVal = true;
} else if (m_flowMap[x,y,z] > 50f) {
} else if (flow.Value > 50) {
retVal = true;
}
return retVal;

100
README
View File

@ -1,100 +0,0 @@
Region Module - ability to control flocks within an OpenSim scene
To build from source
Add osboids source tree under opensim/addon-modules
./runprebuild.sh against opensim root to build this module into the solution
then xbuild, or build within Visual Studio / Monodevelop to produce the binaries
osboids has no external dependencies other than the dlls currently included in opensim.
The project generates a single dll - Flocking.dll which is copied into opensim/bin as part of the build step
Configuration
To become active, the module needs to be both referenced and enabled in the ini files. Otherwise it does nothing on startup
Entry is as follows:
[Boids]
enabled = true
removing the Boids group or setting enabled = false will switch off the module
In addition various config parameters are available to control the flock dynamics
flock-size = 100 the number of Boids to flock
max-speed = 3 how far each boid can travel per update
max-force = 0.25 the maximum acceleration allowed to the current velocity of the boid
neighbour-dist = 25 max distance for other boids to be considered in the same flock as us
desired-separation = 20 how far away from other boids we would like to stay
tolerance = 5 how close to the edges of things can we get without being worried
By default the module will create a flock of plain wooden spheres, however this can be overridden
boid-prim = fish01 names the prim to use to represent each boid in the flock
currently this prim needs to already exist in the scene - i.e. be rezzed in the region.
Eventually this will be an inventory lookup
Various runtime commands control the flocking module behaviour - described below. These can either be invoked
from the Console or in world by directing them to a chat channel. To specify which channel to use:
chat-channel = 118 the chat channel to listen for boid commands on
Runtime Commands
The following commands, which can either be issued on the Console, or via a chat channel in-world, control the behaviour
of the flock at runtime
flock-stop or /118 stop in chat - stop all flocking and remove boids from the region
flock-start or /118 start - start the flocking simulation
flock-size <num> or /118 size <num> - change the size of the flock
flock-prim <name> or /118 prim <name> - change the boid prim to that passed in - must be rezzed in the scene
flock-framerate <num> or /118 framerate - only update the flock positions every <num> frames - only really useful
- for photography and debugging boid behaviour
Boid prims
Any, currently rezzed in scene, object can be used as the boid prim. However fps is very much affected by the
complexity of the entity to use. It is easier to throw a single prim (or sculpty) around the scene than it is to
throw the constituent parts of a 200 linked prim dragon.
Tests show that <= 500 single prims can be flocked effectively - depending on system and network
However maybe <= 300 simple linksets can perform as well.
I intend to allow inventory items and UUIDs to represent the boids - this is not written yet however.
Prebuilt binaries etc.. to follow
Please Note
This module is currently only tested against opensim master. If it is found to work against a stable release,
then that behaviour ought to be considered as a bug - which I will attempt to fix in the next git push.
Status
probably made it to alpha by now ...
Next Steps
I want to improve the ability of the boids to avoid obstacles within the scene. Current avoidance is pretty basic, and
only functions correctly about fifty percent of the time. Need to improve this without increasing computational cost.
Licence: all files released under a BSD licence

196
README.md Normal file
View File

@ -0,0 +1,196 @@
INonSharedRegion Module - ability to control flocks of prims within an OpenSim scene.
To build from source:
Add OpenSimBirds source tree under opensim/addon-modules
e.g.
cd ~/opensim/addon-modules
git clone https://github.com/JakDaniels/OpenSimBirds.git
./runprebuild.sh against opensim root to build this module into the solution
then xbuild, or build within Visual Studio / Monodevelop to produce the binaries.
OpenSimBirds does not need a config in order to run, but without one it will not persist your bird flock settings from one
region restart to another!
The settings go in an .ini file in bin/addon-modules/OpenSimBirds/config/ *or* in your existing Regions.ini
OpenSimBirds has no external dependencies other than the dlls currently included in opensim.
The project generates a single dll - OpenSimBirds.Module.dll which is copied into opensim/bin as part of the build step
Configuration:
If you have the module added to or compiled with your OpenSim build then it will run a instance of itself once per region.
By default it will configure itself to some sensible defaults and will sit quietly in the background waiting for commands
from the console, or from in-world. It will not enable itself (i.e. rez some birds) until commanded to do so, or
configured to do so in the .ini file. It is possible to completely stop the module from doing anything (including
listening for commands), but you must have a particular setting in the .ini file for that region (see below) - in other
words, you must have a config for it!
To become active, the module needs to be enabled in the ini file or commanded to do so from inworld or the console.
Otherwise it does nothing on startup except listen for commands. If you are running multiple regions on one simulator you
can have different Birds settings per region in the configuration file, in the exact same way you can customize per Region
setting in Regions.ini
The configuration for this module can be placed in one of two places:
Option 1:
bin/addon-modules/OpenSimBirds/config/OpenSimBirds.ini
This follows the similar format as a Regions.ini file, where you specify setting for
each region using the [Region Name] section heading. There is an example .ini file
provided which should be edited and copied to the correct place above.
Option 2:
bin/Regions/Regions.ini
Add config parameters to the existing Regions.ini file under the appropriate region name heading.
The file is usually in the above location, but this could have been changed in OpenSim.ini via the
'regionload_regionsdir' parameter in the [Startup] section of OpenSim.ini
Here is an example config:
;; Set the Birds settings per named region
[Test Region 1]
BirdsModuleStartup = True ;this is the default and determines whether the module does anything
BirdsEnabled = True ;set to false to disable the birds from appearing in this region
BirdsFlockSize = 50 ;the number of birds to flock
BirdsMaxFlockSize = 100 ;the maximum flock size that can be created (keeps things sane)
BirdsMaxSpeed = 3 ;how far each bird can travel per update
BirdsMaxForce = 0.25 ;the maximum acceleration allowed to the current velocity of the bird
BirdsNeighbourDistance = 25 ;max distance for other birds to be considered in the same flock as us
BirdsDesiredSeparation = 20 ;how far away from other birds we would like to stay
BirdsTolerance = 5 ;how close to the edges of things can we get without being worried
BirdsBorderSize = 5 ;how close to the edge of a region can we get?
BirdsMaxHeight = 256 ;how high are we allowed to flock
BirdsUpdateEveryNFrames = 1 ;update bird positions every N simulator frames
BirdsPrim = SeaGull1 ;By default the module will create a flock of plain wooden spheres,
;however this can be overridden to the name of an existing prim that
;needs to already exist in the scene - i.e. be rezzed in the region.
;who is allowed to send commands via chat or script: list of UUIDs or ESTATE_OWNER or ESTATE_MANAGER
;or everyone if not specified
BirdsAllowedControllers = ESTATE_OWNER, ESTATE_MANAGER, 12345678-1234-1234-1234-123456789abc
Various runtime commands control the flocking module behaviour - described below. These can either be invoked
from the Console or in world by directing them to a chat channel either from the client's Local Chat or via a script.
You can specify which channel to use in the .ini:
BirdsChatChannel = 118 ;the chat channel to listen for Bird commands on
Runtime Commands:
The following commands, which can be issued on the Console or via in-world chat or scripted chat on the BirdsChatChannel
to control the birds at runtime:
birds-stop or /118 stop ;stop all birds flocking
birds-start or /118 start ;start all birds flocking
birds-enable or /118 enable ;enable the flocking simulation if disabled and rez new birds
birds-disable or /118 disable ;stop all birds and remove them from the scene
birds-prim <name> or /118 prim <name> ;change the bird prim to a prim already rezzed in the scene
birds-stats or /118 stats ;show all the parameters and list the bird prim names and uuids
birds-framerate <num> or /118 framerate <num> ;only update the flock positions every <num> frames
;only really useful for photography and debugging bird
;behaviour
These commands are great for playing with the flock dynamics in real time:
birds-size <num> or /118 size <num> ;change the size of the flock
birds-speed <num> or /118 speed <num> ;change the maximum velocity each bird may achieve
birds-force <num> or /118 force <num> ;change the maximum force each bird may accelerate
birds-distance <num> or /118 distance <num> ;change the maximum distance that other birds are to be considered in the same flock as us
birds-separation <num> or /118 separation <num> ;sets how far away from other birds we would like to stay
birds-tolerance <num> or /118 tolerance <num> ;sets how close to the edges of things can we get without being worried
Of course if distance is less than separation then the birds will never flock. The other way around and they will always
eventually form one or more flocks.
Security:
By default anyone can send commands to the module from within a script or via the in-world chat on the 'BirdsChatChannel' channel.
You should use a high negative value for channel if you want to allow script access, but not in-world chat. Further you can restrict
which users are allowed to control the module using the 'BirdsAllowedControllers' setting. This is a comma separated list of user UUIDs,
but it may also contain one of the pre-defined constants ESTATE_OWNER (evaluates to the UUID of the estate owner) and ESTATE_MANAGER
(evaluates to a list of estate manager UUIDS).
* For commands sent from in-world chat, it is the UUID of the avatar sending the command that is checked against the list.
* For commands sent from a script it is the UUID of the owner of the prim in which the script resides that is checked against the list.
Bird prims:
Any currently rezzed in-scene-object can be used as the bird prim. However fps is very much affected by the
complexity of the entity to use. It is easier to throw a single prim (or sculpty) around the scene than it is to
throw the constituent parts of a 200 linked prim dragon.
Tests show that <= 500 single prims can be flocked effectively - depending on system and network
However maybe <= 300 simple linksets can perform as well.
Network Traffic:
I tested the amount of network traffic generated by bird updates. 20 birds (each with 4 linked prims) takes up about 300kbps
in network position updates. 50 of the same birds generates about 750kbps traffic.
Each bird uses roughly 15kbps of network traffic. This is all measured using an update framerate of 1, i.e. birds' position
is updated every simulator frame.
Statistics:
The stats command in-world or via script returns data to BirdsChatChannel. The console command returns stats to the console.
All the the modules parameters are returned including a list of the active bird prims currently rezzed in the region,
and the UUIDs of those prims' root prim. Also included is a list of any avatar UUIDs that may be sitting on those prims. Here
is an example output:
birds-started = False
birds-enabled = True
birds-prim = SeaGull1
birds-framerate = 1
birds-maxsize = 100
birds-size = 20
birds-speed = 1.5
birds-force = 0.2
birds-distance = 25
birds-separation = 10
birds-tolerance = 5
birds-border = 5
birds-prim0 = OpenSimBirds0 : 01abef79-7fb2-4c8d-831e-62ce1ce878f1 :
birds-prim1 = OpenSimBirds1 : af85996d-af4d-4dda-bc89-721c51e09d0c :
birds-prim2 = OpenSimBirds2 : ca766390-1877-4b19-a29e-4590cf40aece :
birds-prim3 = OpenSimBirds3 : 6694bfa9-8e7f-4ac5-b336-ad13e5cfced2 :
birds-prim4 = OpenSimBirds4 : 1c6b152d-dcca-4fef-8979-b7ccc8139e1e :
birds-prim5 = OpenSimBirds5 : 08bba2cc-d427-4855-a7f0-57aa55109707 :
birds-prim6 = OpenSimBirds6 : bbeb8b6d-28d8-41a9-b8ce-dab3173bd454 :
birds-prim7 = OpenSimBirds7 : 45c73475-1f0f-487f-ac9f-87d30d0315e8 :
birds-prim8 = OpenSimBirds8 : d5891cc8-c196-4b05-82ef-3c7d0f703963 :
birds-prim9 = OpenSimBirds9 : 557b61e1-5fd6-4878-980e-e93cabcc078f :
birds-prim10 = OpenSimBirds10 : 7ff2c02d-d73c-4e49-a4e9-84b652dc70a9 :
birds-prim11 = OpenSimBirds11 : c2b0820c-ba20-4318-a0e8-ec6ad521f524 :
birds-prim12 = OpenSimBirds12 : e8e87309-7a47-4983-89a1-4bb11d05a40c :
birds-prim13 = OpenSimBirds13 : a351e0e3-ae99-48b8-877d-65156f437b33 :
birds-prim14 = OpenSimBirds14 : 150f1c3b-e9d9-4cda-9e03-69fb5286e436 :
birds-prim15 = OpenSimBirds15 : ebf63de1-d419-45d0-8eee-3db14295e401 :
birds-prim16 = OpenSimBirds16 : faad97af-4ee6-425c-b221-99ef53650e93 :
birds-prim17 = OpenSimBirds17 : d75ba544-bbc2-4f5a-9d7e-00e21ed6f191 :
birds-prim18 = OpenSimBirds18 : b91e42cb-ae5b-4f03-bf6e-dc03d52858b7 : 19cc284d-11c6-4ee7-a8de-f69788d08434
birds-prim19 = OpenSimBirds19 : 44aa3e14-56bc-43dd-afbd-7348c5dfe3a5 :
In the above example, there is one avatar sitting on bird-prim18. For more than one avatar the UUID list will be separated by spaces.
Please Note:
This module is currently only tested against opensim master.
Licence: all files released under a BSD licence
If you have any question please contact Jak Daniels, jak@ateb.co.uk
If you do fork or modify this project for the better, please let me know!! I would be interested to incorporate any enhancements you may make.

View File

@ -1,28 +0,0 @@
TODO:
[done] Expose Settings to ini Files
[done] Write in world Chat controller to allow maintenance of flocking in world.
[done - but basic] Handle Borders Correctly
[done - but basic] Handle Collision Avoidance
Replace above with a proper flow field implementation
Handle Wander
Handle Perching
Handle Predator Avoidance
Optimise it all...
Look at maintaining flocks across sim boundaries.
Look at exposing flocking as a service to scripts

BIN
config/OpenSimBirds.iar Normal file

Binary file not shown.

View File

@ -0,0 +1,22 @@
;; Set the Birds settings per named region
[Test Region 2]
BirdsModuleStartup = True ;this is the default and determines whether the module does anything
BirdsEnabled = True ;set to false to disable the birds from appearing in this region
BirdsChatChannel = 118 ;which channel do we listen on for in world commands
BirdsFlockSize = 20 ;the number of birds to flock
BirdsMaxSpeed = 1.5 ;how far each bird can travel per update
BirdsMaxForce = 0.20 ;the maximum acceleration allowed to the current velocity of the bird
BirdsNeighbourDistance = 25 ;max distance for other birds to be considered in the same flock as us
BirdsDesiredSeparation = 10 ;how far away from other birds we would like to stay
BirdsTolerance = 5 ;how close to the edges of things can we get without being worried
BirdsBorderSize = 5 ;how close to the edge of a region can we get?
BirdsMaxHeight = 75 ;how high are we allowed to flock
BirdsPrim = SeaGull1 ;By default the module will create a flock of plain wooden spheres,
;however this can be overridden to the name of an existing prim that
;needs to already exist in the scene - i.e. be rezzed in the region.
;who is allowed to send commands via chat or script: list of UUIDs or ESTATE_OWNER or ESTATE_MANAGER
;or everyone if not specified
BirdsAllowedControllers = ESTATE_OWNER, ESTATE_MANAGER, 12345678-1234-1234-1234-123456789abc

View File

@ -1,34 +1,33 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Core osboids Project -->
<?xml version="1.0" ?>
<Project frameworkVersion="v4_0" name="OpenSimBirds.Module" path="addon-modules/OpenSimBirds/Module" type="Library">
<Configuration name="Debug">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<Configuration name="Release">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<Project frameworkVersion="v3_5" name="Flocking" path="addon-modules/osboids/Flocking/" type="Library">
<Configuration name="Debug">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<Configuration name="Release">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<ReferencePath>../../../bin/</ReferencePath>
<Reference name="log4net" path="../../../bin/" />
<Reference name="System"/>
<Reference name="System.Drawing" />
<Reference name="System.Xml"/>
<Reference name="Nini" path="../../../bin/" />
<Reference name="OpenSim.Framework" path="../../../bin/" />
<Reference name="OpenSim.Framework.Communications" path="../../../bin/" />
<Reference name="OpenSim.Region.Framework" path="../../../bin/" />
<Reference name="OpenSim.Framework.Console" path="../../../bin/" />
<Reference name="OpenMetaverseTypes" path="../../../bin/" />
<Reference name="OpenMetaverse" path="../../../bin/" />
<Files>
<Match pattern="*.cs" recurse="true"/>
<Match pattern="*.addin.xml" path="Resources" buildAction="EmbeddedResource" recurse="true"/>
</Files>
</Project>
<ReferencePath>../../../bin/</ReferencePath>
<Reference name="System"/>
<Reference name="System.Net"/>
<Reference name="OpenMetaverse"/>
<Reference name="OpenMetaverseTypes"/>
<Reference name="OpenMetaverse.StructuredData"/>
<Reference name="OpenSim.Framework"/>
<Reference name="OpenSim.Framework.Communications"/>
<Reference name="OpenSim.Framework.Console"/>
<Reference name="OpenSim.Region.Framework"/>
<Reference name="OpenSim.Region.ScriptEngine.Shared"/>
<Reference name="Nini"/>
<Reference name="log4net"/>
<Reference name="Mono.Addins"/>
<Files>
<Match pattern="*.cs" recurse="true">
</Match>
</Files>
</Project>