Merge branch 'master' of ssh://opensimulator.org/var/git/opensim

integration
Diva Canto 2012-11-27 14:43:01 -08:00
commit a82f699f43
129 changed files with 3870 additions and 2790 deletions

View File

@ -132,6 +132,11 @@
</exec> </exec>
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.capabilities.handlers.tests)==0}" /> <fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.capabilities.handlers.tests)==0}" />
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.server.handlers.tests">
<arg value="./bin/OpenSim.Server.Handlers.Tests.dll" />
</exec>
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.server.handlers.tests)==0}" />
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.services.inventoryservice.tests"> <exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.services.inventoryservice.tests">
<arg value="./bin/OpenSim.Services.InventoryService.Tests.dll" /> <arg value="./bin/OpenSim.Services.InventoryService.Tests.dll" />
</exec> </exec>
@ -240,6 +245,11 @@
<arg value="-xml=test-results/OpenSim.Capabilities.Handlers.Tests.dll-Results.xml" /> <arg value="-xml=test-results/OpenSim.Capabilities.Handlers.Tests.dll-Results.xml" />
</exec> </exec>
<exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.server.handlers.tests">
<arg value="./bin/OpenSim.Server.Handlers.Tests.dll" />
<arg value="-xml=test-results/OpenSim.Server.Handlers.Tests.dll-Results.xml" />
</exec>
<exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.services.inventoryservice.tests"> <exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.services.inventoryservice.tests">
<arg value="./bin/OpenSim.Services.InventoryService.Tests.dll" /> <arg value="./bin/OpenSim.Services.InventoryService.Tests.dll" />
<arg value="-xml=test-results/OpenSim.Services.InventoryService.Tests.dll-Results.xml" /> <arg value="-xml=test-results/OpenSim.Services.InventoryService.Tests.dll-Results.xml" />

View File

@ -93,6 +93,7 @@ what it is today.
* Garmin Kawaguichi * Garmin Kawaguichi
* Gryc Ueusp * Gryc Ueusp
* Hiro Lecker * Hiro Lecker
* Iain Oliver
* Imaze Rhiano * Imaze Rhiano
* Intimidated * Intimidated
* Jeremy Bongio (IBM) * Jeremy Bongio (IBM)

View File

@ -42,7 +42,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Capabilities.Handlers.GetTexture.Tests namespace OpenSim.Capabilities.Handlers.GetTexture.Tests
{ {
[TestFixture] [TestFixture]
public class GetTextureHandlerTests public class GetTextureHandlerTests : OpenSimTestCase
{ {
[Test] [Test]
public void TestTextureNotFound() public void TestTextureNotFound()

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using OpenMetaverse;
using OpenSim.Framework;
namespace OpenSim.Data
{
public class XGroup
{
public UUID groupID;
public UUID ownerRoleID;
public string name;
public string charter;
public bool showInList;
public UUID insigniaID;
public int membershipFee;
public bool openEnrollment;
public bool allowPublish;
public bool maturePublish;
public UUID founderID;
public ulong everyonePowers;
public ulong ownersPowers;
public XGroup Clone()
{
return (XGroup)MemberwiseClone();
}
}
/// <summary>
/// Early stub interface for groups data, not final.
/// </summary>
/// <remarks>
/// Currently in-use only for regression test purposes. Needs to be filled out over time.
/// </remarks>
public interface IXGroupData
{
bool StoreGroup(XGroup group);
XGroup[] GetGroups(string field, string val);
XGroup[] GetGroups(string[] fields, string[] vals);
bool DeleteGroups(string field, string val);
bool DeleteGroups(string[] fields, string[] vals);
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using log4net;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Data;
namespace OpenSim.Data.Null
{
/// <summary>
/// Not a proper generic data handler yet - probably needs to actually store the data as well instead of relying
/// on descendent classes
/// </summary>
public class NullGenericDataHandler
{
protected List<T> Get<T>(string[] fields, string[] vals, List<T> inputEntities)
{
List<T> entities = inputEntities;
for (int i = 0; i < fields.Length; i++)
{
entities
= entities.Where(
e =>
{
FieldInfo fi = typeof(T).GetField(fields[i]);
if (fi == null)
throw new NotImplementedException(string.Format("No field {0} for val {1}", fields[i], vals[i]));
return fi.GetValue(e).ToString() == vals[i];
}
).ToList();
}
return entities;
}
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using log4net;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Data;
namespace OpenSim.Data.Null
{
public class NullXGroupData : NullGenericDataHandler, IXGroupData
{
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private Dictionary<UUID, XGroup> m_groups = new Dictionary<UUID, XGroup>();
public NullXGroupData(string connectionString, string realm) {}
public bool StoreGroup(XGroup group)
{
lock (m_groups)
{
m_groups[group.groupID] = group.Clone();
}
return true;
}
public XGroup[] GetGroups(string field, string val)
{
return GetGroups(new string[] { field }, new string[] { val });
}
public XGroup[] GetGroups(string[] fields, string[] vals)
{
lock (m_groups)
{
List<XGroup> origGroups = Get<XGroup>(fields, vals, m_groups.Values.ToList());
return origGroups.Select(g => g.Clone()).ToArray();
}
}
public bool DeleteGroups(string field, string val)
{
return DeleteGroups(new string[] { field }, new string[] { val });
}
public bool DeleteGroups(string[] fields, string[] vals)
{
lock (m_groups)
{
XGroup[] groupsToDelete = GetGroups(fields, vals);
Array.ForEach(groupsToDelete, g => m_groups.Remove(g.groupID));
}
return true;
}
}
}

View File

@ -49,7 +49,7 @@ using OpenSim.Data.SQLite;
namespace OpenSim.Data.Tests namespace OpenSim.Data.Tests
{ {
[TestFixture(Description = "Asset store tests (SQLite)")] [TestFixture(Description = "Asset store tests (SQLite)")]
public class SQLiteAssetTests : AssetTests<SqliteConnection, SQLiteAssetData> public class SQLiteAssetTests : AssetTests<SqliteConnection, SQLiteAssetData>
{ {
} }

View File

@ -33,6 +33,7 @@ using NUnit.Framework;
using NUnit.Framework.Constraints; using NUnit.Framework.Constraints;
using OpenMetaverse; using OpenMetaverse;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Tests.Common;
using log4net; using log4net;
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
@ -43,6 +44,12 @@ namespace OpenSim.Data.Tests
/// <summary>This is a base class for testing any Data service for any DBMS. /// <summary>This is a base class for testing any Data service for any DBMS.
/// Requires NUnit 2.5 or better (to support the generics). /// Requires NUnit 2.5 or better (to support the generics).
/// </summary> /// </summary>
/// <remarks>
/// FIXME: Should extend OpenSimTestCase but compile on mono 2.4.3 currently fails with
/// AssetTests`2 : System.MemberAccessException : Cannot create an instance of OpenSim.Data.Tests.AssetTests`2[TConn,TAssetData] because Type.ContainsGenericParameters is true.
/// and similar on EstateTests, InventoryTests and RegionTests.
/// Runs fine with mono 2.10.8.1, so easiest thing is to wait until min Mono version uplifts.
/// </remarks>
/// <typeparam name="TConn"></typeparam> /// <typeparam name="TConn"></typeparam>
/// <typeparam name="TService"></typeparam> /// <typeparam name="TService"></typeparam>
public class BasicDataServiceTest<TConn, TService> public class BasicDataServiceTest<TConn, TService>

View File

@ -36,6 +36,7 @@ using NUnit.Framework;
using NUnit.Framework.Constraints; using NUnit.Framework.Constraints;
using OpenMetaverse; using OpenMetaverse;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Tests.Common;
namespace OpenSim.Data.Tests namespace OpenSim.Data.Tests
{ {
@ -254,7 +255,7 @@ namespace OpenSim.Data.Tests
} }
[TestFixture] [TestFixture]
public class PropertyCompareConstraintTest public class PropertyCompareConstraintTest : OpenSimTestCase
{ {
public class HasInt public class HasInt
{ {

View File

@ -34,6 +34,7 @@ using System.Text;
using NUnit.Framework; using NUnit.Framework;
using OpenMetaverse; using OpenMetaverse;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Tests.Common;
namespace OpenSim.Data.Tests namespace OpenSim.Data.Tests
{ {
@ -158,7 +159,7 @@ namespace OpenSim.Data.Tests
} }
[TestFixture] [TestFixture]
public class PropertyScramblerTests public class PropertyScramblerTests : OpenSimTestCase
{ {
[Test] [Test]
public void TestScramble() public void TestScramble()

View File

@ -83,7 +83,8 @@ namespace OpenSim.Framework.Console
= "To enter an argument that contains spaces, surround the argument with double quotes.\nFor example, show object name \"My long object name\"\n"; = "To enter an argument that contains spaces, surround the argument with double quotes.\nFor example, show object name \"My long object name\"\n";
public const string ItemHelpText public const string ItemHelpText
= "For more information, type 'help <item>' where <item> is one of the following:"; = @"For more information, type 'help all' to get a list of all commands,
or type help <item>' where <item> is one of the following:";
/// <value> /// <value>
/// Commands organized by keyword in a tree /// Commands organized by keyword in a tree
@ -117,6 +118,10 @@ namespace OpenSim.Framework.Console
help.Add(ItemHelpText); help.Add(ItemHelpText);
help.AddRange(CollectModulesHelp(tree)); help.AddRange(CollectModulesHelp(tree));
} }
else if (helpParts.Count == 1 && helpParts[0] == "all")
{
help.AddRange(CollectAllCommandsHelp());
}
else else
{ {
help.AddRange(CollectHelp(helpParts)); help.AddRange(CollectHelp(helpParts));
@ -124,6 +129,28 @@ namespace OpenSim.Framework.Console
return help; return help;
} }
/// <summary>
/// Collects the help from all commands and return in alphabetical order.
/// </summary>
/// <returns></returns>
private List<string> CollectAllCommandsHelp()
{
List<string> help = new List<string>();
lock (m_modulesCommands)
{
foreach (List<CommandInfo> commands in m_modulesCommands.Values)
{
var ourHelpText = commands.ConvertAll(c => string.Format("{0} - {1}", c.help_text, c.long_help));
help.AddRange(ourHelpText);
}
}
help.Sort();
return help;
}
/// <summary> /// <summary>
/// See if we can find the requested command in order to display longer help /// See if we can find the requested command in order to display longer help
@ -711,7 +738,7 @@ namespace OpenSim.Framework.Console
/// </summary> /// </summary>
public void Prompt() public void Prompt()
{ {
string line = ReadLine(m_defaultPrompt + "# ", true, true); string line = ReadLine(DefaultPrompt + "# ", true, true);
if (line != String.Empty) if (line != String.Empty)
Output("Invalid command"); Output("Invalid command");

View File

@ -43,15 +43,7 @@ namespace OpenSim.Framework.Console
public object ConsoleScene { get; set; } public object ConsoleScene { get; set; }
/// <summary> public string DefaultPrompt { get; set; }
/// The default prompt text.
/// </summary>
public string DefaultPrompt
{
set { m_defaultPrompt = value; }
get { return m_defaultPrompt; }
}
protected string m_defaultPrompt;
public ConsoleBase(string defaultPrompt) public ConsoleBase(string defaultPrompt)
{ {

View File

@ -46,13 +46,18 @@ namespace OpenSim.Framework.Console
public ICommands Commands { get { return m_commands; } } public ICommands Commands { get { return m_commands; } }
public string DefaultPrompt { get; set; }
public void Prompt() {} public void Prompt() {}
public void RunCommand(string cmd) {} public void RunCommand(string cmd) {}
public string ReadLine(string p, bool isCommand, bool e) { return ""; } public string ReadLine(string p, bool isCommand, bool e) { return ""; }
public object ConsoleScene { get { return null; } } public object ConsoleScene {
get { return null; }
set {}
}
public void Output(string text, string level) {} public void Output(string text, string level) {}
public void Output(string text) {} public void Output(string text) {}

View File

@ -82,6 +82,11 @@ namespace OpenSim.Framework
ICommands Commands { get; } ICommands Commands { get; }
/// <summary>
/// The default prompt text.
/// </summary>
string DefaultPrompt { get; set; }
/// <summary> /// <summary>
/// Display a command prompt on the console and wait for user input /// Display a command prompt on the console and wait for user input
/// </summary> /// </summary>

View File

@ -32,7 +32,7 @@ namespace OpenSim.Framework
{ {
public interface IConsole public interface IConsole
{ {
object ConsoleScene { get; } object ConsoleScene { get; set; }
void Output(string text, string level); void Output(string text, string level);
void Output(string text); void Output(string text);

View File

@ -154,6 +154,11 @@ namespace OpenSim.Framework.Serialization
EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"] = (sbyte)AssetType.TrashFolder; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"] = (sbyte)AssetType.TrashFolder;
} }
public static string CreateOarLandDataPath(LandData ld)
{
return string.Format("{0}{1}.xml", ArchiveConstants.LANDDATA_PATH, ld.GlobalID);
}
/// <summary> /// <summary>
/// Create the filename used to store an object in an OpenSim Archive. /// Create the filename used to store an object in an OpenSim Archive.
/// </summary> /// </summary>

View File

@ -37,7 +37,7 @@ using OpenSim.Tests.Common;
namespace OpenSim.Framework.Serialization.Tests namespace OpenSim.Framework.Serialization.Tests
{ {
[TestFixture] [TestFixture]
public class LandDataSerializerTest public class LandDataSerializerTest : OpenSimTestCase
{ {
private LandData land; private LandData land;
private LandData landWithParcelAccessList; private LandData landWithParcelAccessList;

View File

@ -37,7 +37,7 @@ using OpenSim.Tests.Common;
namespace OpenSim.Framework.Serialization.Tests namespace OpenSim.Framework.Serialization.Tests
{ {
[TestFixture] [TestFixture]
public class RegionSettingsSerializerTests public class RegionSettingsSerializerTests : OpenSimTestCase
{ {
private string m_serializedRs = @"<?xml version=""1.0"" encoding=""utf-16""?> private string m_serializedRs = @"<?xml version=""1.0"" encoding=""utf-16""?>
<RegionSettings> <RegionSettings>

View File

@ -27,7 +27,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
@ -38,6 +37,8 @@ using log4net;
using log4net.Appender; using log4net.Appender;
using log4net.Core; using log4net.Core;
using log4net.Repository; using log4net.Repository;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Framework.Console; using OpenSim.Framework.Console;
using OpenSim.Framework.Monitoring; using OpenSim.Framework.Monitoring;
@ -45,16 +46,12 @@ using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer; using OpenSim.Framework.Servers.HttpServer;
using Timer=System.Timers.Timer; using Timer=System.Timers.Timer;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
namespace OpenSim.Framework.Servers namespace OpenSim.Framework.Servers
{ {
/// <summary> /// <summary>
/// Common base for the main OpenSimServers (user, grid, inventory, region, etc) /// Common base for the main OpenSimServers (user, grid, inventory, region, etc)
/// </summary> /// </summary>
public abstract class BaseOpenSimServer public abstract class BaseOpenSimServer : ServerBase
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@ -63,27 +60,6 @@ namespace OpenSim.Framework.Servers
/// server. /// server.
/// </summary> /// </summary>
private Timer m_periodicDiagnosticsTimer = new Timer(60 * 60 * 1000); private Timer m_periodicDiagnosticsTimer = new Timer(60 * 60 * 1000);
protected CommandConsole m_console;
protected OpenSimAppender m_consoleAppender;
protected IAppender m_logFileAppender = null;
/// <summary>
/// Time at which this server was started
/// </summary>
protected DateTime m_startuptime;
/// <summary>
/// Record the initial startup directory for info purposes
/// </summary>
protected string m_startupDirectory = Environment.CurrentDirectory;
/// <summary>
/// Server version information. Usually VersionInfo + information about git commit, operating system, etc.
/// </summary>
protected string m_version;
protected string m_pidFile = String.Empty;
/// <summary> /// <summary>
/// Random uuid for private data /// Random uuid for private data
@ -96,30 +72,13 @@ namespace OpenSim.Framework.Servers
get { return m_httpServer; } get { return m_httpServer; }
} }
public BaseOpenSimServer() public BaseOpenSimServer() : base()
{ {
m_startuptime = DateTime.Now;
m_version = VersionInfo.Version;
// Random uuid for private data // Random uuid for private data
m_osSecret = UUID.Random().ToString(); m_osSecret = UUID.Random().ToString();
m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics); m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics);
m_periodicDiagnosticsTimer.Enabled = true; m_periodicDiagnosticsTimer.Enabled = true;
// This thread will go on to become the console listening thread
Thread.CurrentThread.Name = "ConsoleThread";
ILoggerRepository repository = LogManager.GetRepository();
IAppender[] appenders = repository.GetAppenders();
foreach (IAppender appender in appenders)
{
if (appender.Name == "LogFileAppender")
{
m_logFileAppender = appender;
}
}
} }
/// <summary> /// <summary>
@ -127,83 +86,18 @@ namespace OpenSim.Framework.Servers
/// </summary> /// </summary>
protected virtual void StartupSpecific() protected virtual void StartupSpecific()
{ {
if (m_console != null) if (m_console == null)
{ return;
ILoggerRepository repository = LogManager.GetRepository();
IAppender[] appenders = repository.GetAppenders();
foreach (IAppender appender in appenders) RegisterCommonCommands();
{
if (appender.Name == "Console") m_console.Commands.AddCommand("General", false, "quit",
{ "quit",
m_consoleAppender = (OpenSimAppender)appender; "Quit the application", HandleQuit);
break;
}
}
if (null == m_consoleAppender) m_console.Commands.AddCommand("General", false, "shutdown",
{ "shutdown",
Notice("No appender named Console found (see the log4net config file for this executable)!"); "Quit the application", HandleQuit);
}
else
{
m_consoleAppender.Console = m_console;
// If there is no threshold set then the threshold is effectively everything.
if (null == m_consoleAppender.Threshold)
m_consoleAppender.Threshold = Level.All;
Notice(String.Format("Console log level is {0}", m_consoleAppender.Threshold));
}
m_console.Commands.AddCommand("General", false, "quit",
"quit",
"Quit the application", HandleQuit);
m_console.Commands.AddCommand("General", false, "shutdown",
"shutdown",
"Quit the application", HandleQuit);
m_console.Commands.AddCommand("General", false, "set log level",
"set log level <level>",
"Set the console logging level", HandleLogLevel);
m_console.Commands.AddCommand("General", false, "show info",
"show info",
"Show general information about the server", HandleShow);
m_console.Commands.AddCommand("General", false, "show threads",
"show threads",
"Show thread status", HandleShow);
m_console.Commands.AddCommand("General", false, "show uptime",
"show uptime",
"Show server uptime", HandleShow);
m_console.Commands.AddCommand("General", false, "show version",
"show version",
"Show server version", HandleShow);
m_console.Commands.AddCommand("General", false, "threads abort",
"threads abort <thread-id>",
"Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort);
m_console.Commands.AddCommand("General", false, "threads show",
"threads show",
"Show thread status. Synonym for \"show threads\"",
(string module, string[] args) => Notice(GetThreadsReport()));
m_console.Commands.AddCommand("General", false, "force gc",
"force gc",
"Manually invoke runtime garbage collection. For debugging purposes",
HandleForceGc);
}
}
private void HandleForceGc(string module, string[] args)
{
MainConsole.Instance.Output("Manually invoking runtime garbage collection");
GC.Collect();
} }
/// <summary> /// <summary>
@ -235,75 +129,12 @@ namespace OpenSim.Framework.Servers
m_log.Debug(sb); m_log.Debug(sb);
} }
/// <summary>
/// Get a report about the registered threads in this server.
/// </summary>
protected string GetThreadsReport()
{
// This should be a constant field.
string reportFormat = "{0,6} {1,35} {2,16} {3,13} {4,10} {5,30}";
StringBuilder sb = new StringBuilder();
Watchdog.ThreadWatchdogInfo[] threads = Watchdog.GetThreadsInfo();
sb.Append(threads.Length + " threads are being tracked:" + Environment.NewLine);
int timeNow = Environment.TickCount & Int32.MaxValue;
sb.AppendFormat(reportFormat, "ID", "NAME", "LAST UPDATE (MS)", "LIFETIME (MS)", "PRIORITY", "STATE");
sb.Append(Environment.NewLine);
foreach (Watchdog.ThreadWatchdogInfo twi in threads)
{
Thread t = twi.Thread;
sb.AppendFormat(
reportFormat,
t.ManagedThreadId,
t.Name,
timeNow - twi.LastTick,
timeNow - twi.FirstTick,
t.Priority,
t.ThreadState);
sb.Append("\n");
}
sb.Append("\n");
// For some reason mono 2.6.7 returns an empty threads set! Not going to confuse people by reporting
// zero active threads.
int totalThreads = Process.GetCurrentProcess().Threads.Count;
if (totalThreads > 0)
sb.AppendFormat("Total threads active: {0}\n\n", totalThreads);
sb.Append("Main threadpool (excluding script engine pools)\n");
sb.Append(Util.GetThreadPoolReport());
return sb.ToString();
}
/// <summary>
/// Return a report about the uptime of this server
/// </summary>
/// <returns></returns>
protected string GetUptimeReport()
{
StringBuilder sb = new StringBuilder(String.Format("Time now is {0}\n", DateTime.Now));
sb.Append(String.Format("Server has been running since {0}, {1}\n", m_startuptime.DayOfWeek, m_startuptime));
sb.Append(String.Format("That is an elapsed time of {0}\n", DateTime.Now - m_startuptime));
return sb.ToString();
}
/// <summary> /// <summary>
/// Performs initialisation of the scene, such as loading configuration from disk. /// Performs initialisation of the scene, such as loading configuration from disk.
/// </summary> /// </summary>
public virtual void Startup() public virtual void Startup()
{ {
m_log.Info("[STARTUP]: Beginning startup processing"); m_log.Info("[STARTUP]: Beginning startup processing");
EnhanceVersionInformation();
m_log.Info("[STARTUP]: OpenSimulator version: " + m_version + Environment.NewLine); m_log.Info("[STARTUP]: OpenSimulator version: " + m_version + Environment.NewLine);
// clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and // clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and
@ -338,257 +169,7 @@ namespace OpenSim.Framework.Servers
private void HandleQuit(string module, string[] args) private void HandleQuit(string module, string[] args)
{ {
Shutdown(); Shutdown();
} }
private void HandleLogLevel(string module, string[] cmd)
{
if (null == m_consoleAppender)
{
Notice("No appender named Console found (see the log4net config file for this executable)!");
return;
}
if (cmd.Length > 3)
{
string rawLevel = cmd[3];
ILoggerRepository repository = LogManager.GetRepository();
Level consoleLevel = repository.LevelMap[rawLevel];
if (consoleLevel != null)
m_consoleAppender.Threshold = consoleLevel;
else
Notice(
String.Format(
"{0} is not a valid logging level. Valid logging levels are ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF",
rawLevel));
}
Notice(String.Format("Console log level is {0}", m_consoleAppender.Threshold));
}
/// <summary>
/// Show help information
/// </summary>
/// <param name="helpArgs"></param>
protected virtual void ShowHelp(string[] helpArgs)
{
Notice("");
if (helpArgs.Length == 0)
{
Notice("set log level [level] - change the console logging level only. For example, off or debug.");
Notice("show info - show server information (e.g. startup path).");
Notice("show threads - list tracked threads");
Notice("show uptime - show server startup time and uptime.");
Notice("show version - show server version.");
Notice("");
return;
}
}
public virtual void HandleShow(string module, string[] cmd)
{
List<string> args = new List<string>(cmd);
args.RemoveAt(0);
string[] showParams = args.ToArray();
switch (showParams[0])
{
case "info":
ShowInfo();
break;
case "threads":
Notice(GetThreadsReport());
break;
case "uptime":
Notice(GetUptimeReport());
break;
case "version":
Notice(GetVersionText());
break;
}
}
public virtual void HandleThreadsAbort(string module, string[] cmd)
{
if (cmd.Length != 3)
{
MainConsole.Instance.Output("Usage: threads abort <thread-id>");
return;
}
int threadId;
if (!int.TryParse(cmd[2], out threadId))
{
MainConsole.Instance.Output("ERROR: Thread id must be an integer");
return;
}
if (Watchdog.AbortThread(threadId))
MainConsole.Instance.OutputFormat("Aborted thread with id {0}", threadId);
else
MainConsole.Instance.OutputFormat("ERROR - Thread with id {0} not found in managed threads", threadId);
}
protected void ShowInfo()
{
Notice(GetVersionText());
Notice("Startup directory: " + m_startupDirectory);
if (null != m_consoleAppender)
Notice(String.Format("Console log level: {0}", m_consoleAppender.Threshold));
}
protected string GetVersionText()
{
return String.Format("Version: {0} (interface version {1})", m_version, VersionInfo.MajorInterfaceVersion);
}
/// <summary>
/// Console output is only possible if a console has been established.
/// That is something that cannot be determined within this class. So
/// all attempts to use the console MUST be verified.
/// </summary>
/// <param name="msg"></param>
protected void Notice(string msg)
{
if (m_console != null)
{
m_console.Output(msg);
}
}
/// <summary>
/// Console output is only possible if a console has been established.
/// That is something that cannot be determined within this class. So
/// all attempts to use the console MUST be verified.
/// </summary>
/// <param name="format"></param>
/// <param name="components"></param>
protected void Notice(string format, params string[] components)
{
if (m_console != null)
m_console.OutputFormat(format, components);
}
/// <summary>
/// Enhance the version string with extra information if it's available.
/// </summary>
protected void EnhanceVersionInformation()
{
string buildVersion = string.Empty;
// The subversion information is deprecated and will be removed at a later date
// Add subversion revision information if available
// Try file "svn_revision" in the current directory first, then the .svn info.
// This allows to make the revision available in simulators not running from the source tree.
// FIXME: Making an assumption about the directory we're currently in - we do this all over the place
// elsewhere as well
string gitDir = "../.git/";
string gitRefPointerPath = gitDir + "HEAD";
string svnRevisionFileName = "svn_revision";
string svnFileName = ".svn/entries";
string manualVersionFileName = ".version";
string inputLine;
int strcmp;
if (File.Exists(manualVersionFileName))
{
using (StreamReader CommitFile = File.OpenText(manualVersionFileName))
buildVersion = CommitFile.ReadLine();
m_version += buildVersion ?? "";
}
else if (File.Exists(gitRefPointerPath))
{
// m_log.DebugFormat("[OPENSIM]: Found {0}", gitRefPointerPath);
string rawPointer = "";
using (StreamReader pointerFile = File.OpenText(gitRefPointerPath))
rawPointer = pointerFile.ReadLine();
// m_log.DebugFormat("[OPENSIM]: rawPointer [{0}]", rawPointer);
Match m = Regex.Match(rawPointer, "^ref: (.+)$");
if (m.Success)
{
// m_log.DebugFormat("[OPENSIM]: Matched [{0}]", m.Groups[1].Value);
string gitRef = m.Groups[1].Value;
string gitRefPath = gitDir + gitRef;
if (File.Exists(gitRefPath))
{
// m_log.DebugFormat("[OPENSIM]: Found gitRefPath [{0}]", gitRefPath);
using (StreamReader refFile = File.OpenText(gitRefPath))
{
string gitHash = refFile.ReadLine();
m_version += gitHash.Substring(0, 7);
}
}
}
}
else
{
// Remove the else logic when subversion mirror is no longer used
if (File.Exists(svnRevisionFileName))
{
StreamReader RevisionFile = File.OpenText(svnRevisionFileName);
buildVersion = RevisionFile.ReadLine();
buildVersion.Trim();
RevisionFile.Close();
}
if (string.IsNullOrEmpty(buildVersion) && File.Exists(svnFileName))
{
StreamReader EntriesFile = File.OpenText(svnFileName);
inputLine = EntriesFile.ReadLine();
while (inputLine != null)
{
// using the dir svn revision at the top of entries file
strcmp = String.Compare(inputLine, "dir");
if (strcmp == 0)
{
buildVersion = EntriesFile.ReadLine();
break;
}
else
{
inputLine = EntriesFile.ReadLine();
}
}
EntriesFile.Close();
}
m_version += string.IsNullOrEmpty(buildVersion) ? " " : ("." + buildVersion + " ").Substring(0, 6);
}
}
protected void CreatePIDFile(string path)
{
try
{
string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
FileStream fs = File.Create(path);
Byte[] buf = Encoding.ASCII.GetBytes(pidstring);
fs.Write(buf, 0, buf.Length);
fs.Close();
m_pidFile = path;
}
catch (Exception)
{
}
}
public string osSecret { public string osSecret {
// Secret uuid for the simulator // Secret uuid for the simulator
@ -607,20 +188,5 @@ namespace OpenSim.Framework.Servers
return StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version); return StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version);
} }
} }
protected void RemovePIDFile()
{
if (m_pidFile != String.Empty)
{
try
{
File.Delete(m_pidFile);
m_pidFile = String.Empty;
}
catch (Exception)
{
}
}
}
} }
} }

View File

@ -719,8 +719,11 @@ namespace OpenSim.Framework.Servers.HttpServer
if (DebugLevel == 5) if (DebugLevel == 5)
{ {
const int sampleLength = 80; const int sampleLength = 80;
char[] sampleChars = new char[sampleLength]; char[] sampleChars = new char[sampleLength + 3];
reader.Read(sampleChars, 0, sampleLength); reader.Read(sampleChars, 0, sampleLength);
sampleChars[80] = '.';
sampleChars[81] = '.';
sampleChars[82] = '.';
output = new string(sampleChars); output = new string(sampleChars);
} }
else else
@ -728,7 +731,7 @@ namespace OpenSim.Framework.Servers.HttpServer
output = reader.ReadToEnd(); output = reader.ReadToEnd();
} }
m_log.DebugFormat("[BASE HTTP SERVER]: {0}...", output.Replace("\n", @"\n")); m_log.DebugFormat("[BASE HTTP SERVER]: {0}", output.Replace("\n", @"\n"));
} }
} }
@ -1279,59 +1282,6 @@ namespace OpenSim.Framework.Servers.HttpServer
map["login"] = OSD.FromString("false"); map["login"] = OSD.FromString("false");
return map; return map;
} }
/// <summary>
/// A specific agent handler was provided. Such a handler is expecetd to have an
/// intimate, and highly specific relationship with the client. Consequently,
/// nothing is done here.
/// </summary>
/// <param name="handler"></param>
/// <param name="request"></param>
/// <param name="response"></param>
private bool HandleAgentRequest(IHttpAgentHandler handler, OSHttpRequest request, OSHttpResponse response)
{
// In the case of REST, then handler is responsible for ALL aspects of
// the request/response handling. Nothing is done here, not even encoding.
try
{
return handler.Handle(request, response);
}
catch (Exception e)
{
// If the handler did in fact close the stream, then this will blow
// chunks. So that that doesn't disturb anybody we throw away any
// and all exceptions raised. We've done our best to release the
// client.
try
{
m_log.Warn("[HTTP-AGENT]: Error - " + e.Message);
response.SendChunked = false;
response.KeepAlive = true;
response.StatusCode = (int)OSHttpStatusCode.ServerErrorInternalError;
//response.OutputStream.Close();
try
{
response.Send();
//response.FreeContext();
}
catch (SocketException f)
{
// This has to be here to prevent a Linux/Mono crash
m_log.Warn(
String.Format("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux. ", f.Message), f);
}
}
catch(Exception)
{
}
}
// Indicate that the request has been "handled"
return true;
}
public byte[] HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response) public byte[] HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response)
{ {

View File

@ -0,0 +1,677 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using log4net;
using log4net.Appender;
using log4net.Core;
using log4net.Repository;
using Nini.Config;
using OpenSim.Framework.Console;
using OpenSim.Framework.Monitoring;
namespace OpenSim.Framework.Servers
{
public class ServerBase
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public IConfigSource Config { get; protected set; }
/// <summary>
/// Console to be used for any command line output. Can be null, in which case there should be no output.
/// </summary>
protected ICommandConsole m_console;
protected OpenSimAppender m_consoleAppender;
protected FileAppender m_logFileAppender;
protected DateTime m_startuptime;
protected string m_startupDirectory = Environment.CurrentDirectory;
protected string m_pidFile = String.Empty;
/// <summary>
/// Server version information. Usually VersionInfo + information about git commit, operating system, etc.
/// </summary>
protected string m_version;
public ServerBase()
{
m_startuptime = DateTime.Now;
m_version = VersionInfo.Version;
EnhanceVersionInformation();
}
protected void CreatePIDFile(string path)
{
try
{
string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
using (FileStream fs = File.Create(path))
{
Byte[] buf = Encoding.ASCII.GetBytes(pidstring);
fs.Write(buf, 0, buf.Length);
}
m_pidFile = path;
m_log.InfoFormat("[SERVER BASE]: Created pid file {0}", m_pidFile);
}
catch (Exception e)
{
m_log.Warn(string.Format("[SERVER BASE]: Could not create PID file at {0} ", path), e);
}
}
protected void RemovePIDFile()
{
if (m_pidFile != String.Empty)
{
try
{
File.Delete(m_pidFile);
}
catch (Exception e)
{
m_log.Error(string.Format("[SERVER BASE]: Error whilst removing {0} ", m_pidFile), e);
}
m_pidFile = String.Empty;
}
}
public void RegisterCommonAppenders(IConfig startupConfig)
{
ILoggerRepository repository = LogManager.GetRepository();
IAppender[] appenders = repository.GetAppenders();
foreach (IAppender appender in appenders)
{
if (appender.Name == "Console")
{
m_consoleAppender = (OpenSimAppender)appender;
}
else if (appender.Name == "LogFileAppender")
{
m_logFileAppender = (FileAppender)appender;
}
}
if (null == m_consoleAppender)
{
Notice("No appender named Console found (see the log4net config file for this executable)!");
}
else
{
// FIXME: This should be done through an interface rather than casting.
m_consoleAppender.Console = (ConsoleBase)m_console;
// If there is no threshold set then the threshold is effectively everything.
if (null == m_consoleAppender.Threshold)
m_consoleAppender.Threshold = Level.All;
Notice(String.Format("Console log level is {0}", m_consoleAppender.Threshold));
}
if (m_logFileAppender != null && startupConfig != null)
{
string cfgFileName = startupConfig.GetString("LogFile", null);
if (cfgFileName != null)
{
m_logFileAppender.File = cfgFileName;
m_logFileAppender.ActivateOptions();
}
m_log.InfoFormat("[SERVER BASE]: Logging started to file {0}", m_logFileAppender.File);
}
}
/// <summary>
/// Register common commands once m_console has been set if it is going to be set
/// </summary>
public void RegisterCommonCommands()
{
if (m_console == null)
return;
m_console.Commands.AddCommand(
"General", false, "show info", "show info", "Show general information about the server", HandleShow);
m_console.Commands.AddCommand(
"General", false, "show version", "show version", "Show server version", HandleShow);
m_console.Commands.AddCommand(
"General", false, "show uptime", "show uptime", "Show server uptime", HandleShow);
m_console.Commands.AddCommand(
"General", false, "get log level", "get log level", "Get the current console logging level",
(mod, cmd) => ShowLogLevel());
m_console.Commands.AddCommand(
"General", false, "set log level", "set log level <level>",
"Set the console logging level for this session.", HandleSetLogLevel);
m_console.Commands.AddCommand(
"General", false, "config set",
"config set <section> <key> <value>",
"Set a config option. In most cases this is not useful since changed parameters are not dynamically reloaded. Neither do changed parameters persist - you will have to change a config file manually and restart.", HandleConfig);
m_console.Commands.AddCommand(
"General", false, "config get",
"config get [<section>] [<key>]",
"Synonym for config show",
HandleConfig);
m_console.Commands.AddCommand(
"General", false, "config show",
"config show [<section>] [<key>]",
"Show config information",
"If neither section nor field are specified, then the whole current configuration is printed." + Environment.NewLine
+ "If a section is given but not a field, then all fields in that section are printed.",
HandleConfig);
m_console.Commands.AddCommand(
"General", false, "config save",
"config save <path>",
"Save current configuration to a file at the given path", HandleConfig);
m_console.Commands.AddCommand(
"General", false, "command-script",
"command-script <script>",
"Run a command script from file", HandleScript);
m_console.Commands.AddCommand(
"General", false, "show threads",
"show threads",
"Show thread status", HandleShow);
m_console.Commands.AddCommand(
"General", false, "threads abort",
"threads abort <thread-id>",
"Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort);
m_console.Commands.AddCommand(
"General", false, "threads show",
"threads show",
"Show thread status. Synonym for \"show threads\"",
(string module, string[] args) => Notice(GetThreadsReport()));
m_console.Commands.AddCommand(
"General", false, "force gc",
"force gc",
"Manually invoke runtime garbage collection. For debugging purposes",
HandleForceGc);
}
private void HandleForceGc(string module, string[] args)
{
Notice("Manually invoking runtime garbage collection");
GC.Collect();
}
public virtual void HandleShow(string module, string[] cmd)
{
List<string> args = new List<string>(cmd);
args.RemoveAt(0);
string[] showParams = args.ToArray();
switch (showParams[0])
{
case "info":
ShowInfo();
break;
case "version":
Notice(GetVersionText());
break;
case "uptime":
Notice(GetUptimeReport());
break;
case "threads":
Notice(GetThreadsReport());
break;
}
}
/// <summary>
/// Change and load configuration file data.
/// </summary>
/// <param name="module"></param>
/// <param name="cmd"></param>
private void HandleConfig(string module, string[] cmd)
{
List<string> args = new List<string>(cmd);
args.RemoveAt(0);
string[] cmdparams = args.ToArray();
if (cmdparams.Length > 0)
{
string firstParam = cmdparams[0].ToLower();
switch (firstParam)
{
case "set":
if (cmdparams.Length < 4)
{
Notice("Syntax: config set <section> <key> <value>");
Notice("Example: config set ScriptEngine.DotNetEngine NumberOfScriptThreads 5");
}
else
{
IConfig c;
IConfigSource source = new IniConfigSource();
c = source.AddConfig(cmdparams[1]);
if (c != null)
{
string _value = String.Join(" ", cmdparams, 3, cmdparams.Length - 3);
c.Set(cmdparams[2], _value);
Config.Merge(source);
Notice("In section [{0}], set {1} = {2}", c.Name, cmdparams[2], _value);
}
}
break;
case "get":
case "show":
if (cmdparams.Length == 1)
{
foreach (IConfig config in Config.Configs)
{
Notice("[{0}]", config.Name);
string[] keys = config.GetKeys();
foreach (string key in keys)
Notice(" {0} = {1}", key, config.GetString(key));
}
}
else if (cmdparams.Length == 2 || cmdparams.Length == 3)
{
IConfig config = Config.Configs[cmdparams[1]];
if (config == null)
{
Notice("Section \"{0}\" does not exist.",cmdparams[1]);
break;
}
else
{
if (cmdparams.Length == 2)
{
Notice("[{0}]", config.Name);
foreach (string key in config.GetKeys())
Notice(" {0} = {1}", key, config.GetString(key));
}
else
{
Notice(
"config get {0} {1} : {2}",
cmdparams[1], cmdparams[2], config.GetString(cmdparams[2]));
}
}
}
else
{
Notice("Syntax: config {0} [<section>] [<key>]", firstParam);
Notice("Example: config {0} ScriptEngine.DotNetEngine NumberOfScriptThreads", firstParam);
}
break;
case "save":
if (cmdparams.Length < 2)
{
Notice("Syntax: config save <path>");
return;
}
string path = cmdparams[1];
Notice("Saving configuration file: {0}", path);
if (Config is IniConfigSource)
{
IniConfigSource iniCon = (IniConfigSource)Config;
iniCon.Save(path);
}
else if (Config is XmlConfigSource)
{
XmlConfigSource xmlCon = (XmlConfigSource)Config;
xmlCon.Save(path);
}
break;
}
}
}
private void HandleSetLogLevel(string module, string[] cmd)
{
if (cmd.Length != 4)
{
Notice("Usage: set log level <level>");
return;
}
if (null == m_consoleAppender)
{
Notice("No appender named Console found (see the log4net config file for this executable)!");
return;
}
string rawLevel = cmd[3];
ILoggerRepository repository = LogManager.GetRepository();
Level consoleLevel = repository.LevelMap[rawLevel];
if (consoleLevel != null)
m_consoleAppender.Threshold = consoleLevel;
else
Notice(
"{0} is not a valid logging level. Valid logging levels are ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF",
rawLevel);
ShowLogLevel();
}
private void ShowLogLevel()
{
Notice("Console log level is {0}", m_consoleAppender.Threshold);
}
protected virtual void HandleScript(string module, string[] parms)
{
if (parms.Length != 2)
{
Notice("Usage: command-script <path-to-script");
return;
}
RunCommandScript(parms[1]);
}
/// <summary>
/// Run an optional startup list of commands
/// </summary>
/// <param name="fileName"></param>
protected void RunCommandScript(string fileName)
{
if (m_console == null)
return;
if (File.Exists(fileName))
{
m_log.Info("[SERVER BASE]: Running " + fileName);
using (StreamReader readFile = File.OpenText(fileName))
{
string currentCommand;
while ((currentCommand = readFile.ReadLine()) != null)
{
currentCommand = currentCommand.Trim();
if (!(currentCommand == ""
|| currentCommand.StartsWith(";")
|| currentCommand.StartsWith("//")
|| currentCommand.StartsWith("#")))
{
m_log.Info("[SERVER BASE]: Running '" + currentCommand + "'");
m_console.RunCommand(currentCommand);
}
}
}
}
}
/// <summary>
/// Return a report about the uptime of this server
/// </summary>
/// <returns></returns>
protected string GetUptimeReport()
{
StringBuilder sb = new StringBuilder(String.Format("Time now is {0}\n", DateTime.Now));
sb.Append(String.Format("Server has been running since {0}, {1}\n", m_startuptime.DayOfWeek, m_startuptime));
sb.Append(String.Format("That is an elapsed time of {0}\n", DateTime.Now - m_startuptime));
return sb.ToString();
}
protected void ShowInfo()
{
Notice(GetVersionText());
Notice("Startup directory: " + m_startupDirectory);
if (null != m_consoleAppender)
Notice(String.Format("Console log level: {0}", m_consoleAppender.Threshold));
}
/// <summary>
/// Enhance the version string with extra information if it's available.
/// </summary>
protected void EnhanceVersionInformation()
{
string buildVersion = string.Empty;
// The subversion information is deprecated and will be removed at a later date
// Add subversion revision information if available
// Try file "svn_revision" in the current directory first, then the .svn info.
// This allows to make the revision available in simulators not running from the source tree.
// FIXME: Making an assumption about the directory we're currently in - we do this all over the place
// elsewhere as well
string gitDir = "../.git/";
string gitRefPointerPath = gitDir + "HEAD";
string svnRevisionFileName = "svn_revision";
string svnFileName = ".svn/entries";
string manualVersionFileName = ".version";
string inputLine;
int strcmp;
if (File.Exists(manualVersionFileName))
{
using (StreamReader CommitFile = File.OpenText(manualVersionFileName))
buildVersion = CommitFile.ReadLine();
m_version += buildVersion ?? "";
}
else if (File.Exists(gitRefPointerPath))
{
// m_log.DebugFormat("[SERVER BASE]: Found {0}", gitRefPointerPath);
string rawPointer = "";
using (StreamReader pointerFile = File.OpenText(gitRefPointerPath))
rawPointer = pointerFile.ReadLine();
// m_log.DebugFormat("[SERVER BASE]: rawPointer [{0}]", rawPointer);
Match m = Regex.Match(rawPointer, "^ref: (.+)$");
if (m.Success)
{
// m_log.DebugFormat("[SERVER BASE]: Matched [{0}]", m.Groups[1].Value);
string gitRef = m.Groups[1].Value;
string gitRefPath = gitDir + gitRef;
if (File.Exists(gitRefPath))
{
// m_log.DebugFormat("[SERVER BASE]: Found gitRefPath [{0}]", gitRefPath);
using (StreamReader refFile = File.OpenText(gitRefPath))
{
string gitHash = refFile.ReadLine();
m_version += gitHash.Substring(0, 7);
}
}
}
}
else
{
// Remove the else logic when subversion mirror is no longer used
if (File.Exists(svnRevisionFileName))
{
StreamReader RevisionFile = File.OpenText(svnRevisionFileName);
buildVersion = RevisionFile.ReadLine();
buildVersion.Trim();
RevisionFile.Close();
}
if (string.IsNullOrEmpty(buildVersion) && File.Exists(svnFileName))
{
StreamReader EntriesFile = File.OpenText(svnFileName);
inputLine = EntriesFile.ReadLine();
while (inputLine != null)
{
// using the dir svn revision at the top of entries file
strcmp = String.Compare(inputLine, "dir");
if (strcmp == 0)
{
buildVersion = EntriesFile.ReadLine();
break;
}
else
{
inputLine = EntriesFile.ReadLine();
}
}
EntriesFile.Close();
}
m_version += string.IsNullOrEmpty(buildVersion) ? " " : ("." + buildVersion + " ").Substring(0, 6);
}
}
protected string GetVersionText()
{
return String.Format("Version: {0} (interface version {1})", m_version, VersionInfo.MajorInterfaceVersion);
}
/// <summary>
/// Get a report about the registered threads in this server.
/// </summary>
protected string GetThreadsReport()
{
// This should be a constant field.
string reportFormat = "{0,6} {1,35} {2,16} {3,13} {4,10} {5,30}";
StringBuilder sb = new StringBuilder();
Watchdog.ThreadWatchdogInfo[] threads = Watchdog.GetThreadsInfo();
sb.Append(threads.Length + " threads are being tracked:" + Environment.NewLine);
int timeNow = Environment.TickCount & Int32.MaxValue;
sb.AppendFormat(reportFormat, "ID", "NAME", "LAST UPDATE (MS)", "LIFETIME (MS)", "PRIORITY", "STATE");
sb.Append(Environment.NewLine);
foreach (Watchdog.ThreadWatchdogInfo twi in threads)
{
Thread t = twi.Thread;
sb.AppendFormat(
reportFormat,
t.ManagedThreadId,
t.Name,
timeNow - twi.LastTick,
timeNow - twi.FirstTick,
t.Priority,
t.ThreadState);
sb.Append("\n");
}
sb.Append("\n");
// For some reason mono 2.6.7 returns an empty threads set! Not going to confuse people by reporting
// zero active threads.
int totalThreads = Process.GetCurrentProcess().Threads.Count;
if (totalThreads > 0)
sb.AppendFormat("Total threads active: {0}\n\n", totalThreads);
sb.Append("Main threadpool (excluding script engine pools)\n");
sb.Append(Util.GetThreadPoolReport());
return sb.ToString();
}
public virtual void HandleThreadsAbort(string module, string[] cmd)
{
if (cmd.Length != 3)
{
MainConsole.Instance.Output("Usage: threads abort <thread-id>");
return;
}
int threadId;
if (!int.TryParse(cmd[2], out threadId))
{
MainConsole.Instance.Output("ERROR: Thread id must be an integer");
return;
}
if (Watchdog.AbortThread(threadId))
MainConsole.Instance.OutputFormat("Aborted thread with id {0}", threadId);
else
MainConsole.Instance.OutputFormat("ERROR - Thread with id {0} not found in managed threads", threadId);
}
/// <summary>
/// Console output is only possible if a console has been established.
/// That is something that cannot be determined within this class. So
/// all attempts to use the console MUST be verified.
/// </summary>
/// <param name="msg"></param>
protected void Notice(string msg)
{
if (m_console != null)
{
m_console.Output(msg);
}
}
/// <summary>
/// Console output is only possible if a console has been established.
/// That is something that cannot be determined within this class. So
/// all attempts to use the console MUST be verified.
/// </summary>
/// <param name="format"></param>
/// <param name="components"></param>
protected void Notice(string format, params object[] components)
{
if (m_console != null)
m_console.OutputFormat(format, components);
}
}
}

View File

@ -35,11 +35,12 @@ using HttpServer;
using HttpServer.FormDecoders; using HttpServer.FormDecoders;
using NUnit.Framework; using NUnit.Framework;
using OpenSim.Framework.Servers.HttpServer; using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Tests.Common;
namespace OpenSim.Framework.Servers.Tests namespace OpenSim.Framework.Servers.Tests
{ {
[TestFixture] [TestFixture]
public class OSHttpTests public class OSHttpTests : OpenSimTestCase
{ {
// we need an IHttpClientContext for our tests // we need an IHttpClientContext for our tests
public class TestHttpClientContext: IHttpClientContext public class TestHttpClientContext: IHttpClientContext

View File

@ -29,11 +29,12 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using NUnit.Framework; using NUnit.Framework;
using OpenSim.Tests.Common;
namespace OpenSim.Framework.Servers.Tests namespace OpenSim.Framework.Servers.Tests
{ {
[TestFixture] [TestFixture]
public class VersionInfoTests public class VersionInfoTests : OpenSimTestCase
{ {
[Test] [Test]
public void TestVersionLength() public void TestVersionLength()

View File

@ -24,16 +24,17 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
using System.Collections.Generic; using System.Collections.Generic;
using OpenMetaverse; using OpenMetaverse;
using OpenMetaverse.StructuredData; using OpenMetaverse.StructuredData;
using NUnit.Framework; using NUnit.Framework;
using OpenSim.Tests.Common;
namespace OpenSim.Framework.Tests namespace OpenSim.Framework.Tests
{ {
[TestFixture] [TestFixture]
public class AgentCircuitDataTest public class AgentCircuitDataTest : OpenSimTestCase
{ {
private UUID AgentId; private UUID AgentId;
private AvatarAppearance AvAppearance; private AvatarAppearance AvAppearance;

View File

@ -38,7 +38,7 @@ using Animation = OpenSim.Framework.Animation;
namespace OpenSim.Framework.Tests namespace OpenSim.Framework.Tests
{ {
[TestFixture] [TestFixture]
public class AnimationTests public class AnimationTests : OpenSimTestCase
{ {
private Animation anim1 = null; private Animation anim1 = null;
private Animation anim2 = null; private Animation anim2 = null;

View File

@ -30,11 +30,12 @@ using System.Collections.Generic;
using System.Text; using System.Text;
using NUnit.Framework; using NUnit.Framework;
using OpenMetaverse; using OpenMetaverse;
using OpenSim.Tests.Common;
namespace OpenSim.Framework.Tests namespace OpenSim.Framework.Tests
{ {
[TestFixture] [TestFixture]
public class AssetBaseTest public class AssetBaseTest : OpenSimTestCase
{ {
[Test] [Test]
public void TestContainsReferences() public void TestContainsReferences()

View File

@ -28,11 +28,12 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using OpenMetaverse; using OpenMetaverse;
using OpenSim.Tests.Common;
namespace OpenSim.Framework.Tests namespace OpenSim.Framework.Tests
{ {
[TestFixture] [TestFixture]
public class CacheTests public class CacheTests : OpenSimTestCase
{ {
private Cache cache; private Cache cache;
private UUID cacheItemUUID; private UUID cacheItemUUID;

View File

@ -26,11 +26,12 @@
*/ */
using NUnit.Framework; using NUnit.Framework;
using OpenSim.Tests.Common;
namespace OpenSim.Framework.Tests namespace OpenSim.Framework.Tests
{ {
[TestFixture] [TestFixture]
public class LocationTest public class LocationTest : OpenSimTestCase
{ {
[Test] [Test]
public void locationRegionHandleRegionHandle() public void locationRegionHandleRegionHandle()

View File

@ -32,11 +32,12 @@ using OpenMetaverse.StructuredData;
using System; using System;
using System.Globalization; using System.Globalization;
using System.Threading; using System.Threading;
using OpenSim.Tests.Common;
namespace OpenSim.Framework.Tests namespace OpenSim.Framework.Tests
{ {
[TestFixture] [TestFixture]
public class MundaneFrameworkTests public class MundaneFrameworkTests : OpenSimTestCase
{ {
private bool m_RegionSettingsOnSaveEventFired; private bool m_RegionSettingsOnSaveEventFired;
private bool m_RegionLightShareDataOnSaveEventFired; private bool m_RegionLightShareDataOnSaveEventFired;

View File

@ -31,11 +31,12 @@ using NUnit.Framework;
using OpenMetaverse; using OpenMetaverse;
using OpenMetaverse.StructuredData; using OpenMetaverse.StructuredData;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Tests.Common;
namespace OpenSim.Framework.Tests namespace OpenSim.Framework.Tests
{ {
[TestFixture] [TestFixture]
public class PrimeNumberHelperTests public class PrimeNumberHelperTests : OpenSimTestCase
{ {
[Test] [Test]
public void TestGetPrime() public void TestGetPrime()

View File

@ -33,7 +33,7 @@ using OpenSim.Tests.Common;
namespace OpenSim.Framework.Tests namespace OpenSim.Framework.Tests
{ {
[TestFixture] [TestFixture]
public class UtilTests public class UtilTests : OpenSimTestCase
{ {
[Test] [Test]
public void VectorOperationTests() public void VectorOperationTests()

View File

@ -1741,12 +1741,16 @@ namespace OpenSim.Framework
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool) if (FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool)
{ {
threadPoolUsed = "SmartThreadPool"; // ROBUST currently leaves this the FireAndForgetMethod but never actually initializes the threadpool.
maxThreads = m_ThreadPool.MaxThreads; if (m_ThreadPool != null)
minThreads = m_ThreadPool.MinThreads; {
inUseThreads = m_ThreadPool.InUseThreads; threadPoolUsed = "SmartThreadPool";
allocatedThreads = m_ThreadPool.ActiveThreads; maxThreads = m_ThreadPool.MaxThreads;
waitingCallbacks = m_ThreadPool.WaitingCallbacks; minThreads = m_ThreadPool.MinThreads;
inUseThreads = m_ThreadPool.InUseThreads;
allocatedThreads = m_ThreadPool.ActiveThreads;
waitingCallbacks = m_ThreadPool.WaitingCallbacks;
}
} }
else if ( else if (
FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem

View File

@ -188,7 +188,6 @@ namespace OpenSim
// Make sure command line options take precedence // Make sure command line options take precedence
m_config.Source.Merge(argvSource); m_config.Source.Merge(argvSource);
IConfig enVars = m_config.Source.Configs["Environment"]; IConfig enVars = m_config.Source.Configs["Environment"];
if( enVars != null ) if( enVars != null )

View File

@ -82,8 +82,8 @@ namespace OpenSim
{ {
base.ReadExtraConfigSettings(); base.ReadExtraConfigSettings();
IConfig startupConfig = m_config.Source.Configs["Startup"]; IConfig startupConfig = Config.Configs["Startup"];
IConfig networkConfig = m_config.Source.Configs["Network"]; IConfig networkConfig = Config.Configs["Network"];
int stpMaxThreads = 15; int stpMaxThreads = 15;
@ -106,22 +106,6 @@ namespace OpenSim
m_timeInterval = startupConfig.GetInt("timer_Interval", 1200); m_timeInterval = startupConfig.GetInt("timer_Interval", 1200);
} }
if (m_logFileAppender != null)
{
if (m_logFileAppender is log4net.Appender.FileAppender)
{
log4net.Appender.FileAppender appender =
(log4net.Appender.FileAppender)m_logFileAppender;
string fileName = startupConfig.GetString("LogFile", String.Empty);
if (fileName != String.Empty)
{
appender.File = fileName;
appender.ActivateOptions();
}
m_log.InfoFormat("[LOGGING]: Logging started to file {0}", appender.File);
}
}
string asyncCallMethodStr = startupConfig.GetString("async_call_method", String.Empty); string asyncCallMethodStr = startupConfig.GetString("async_call_method", String.Empty);
FireAndForgetMethod asyncCallMethod; FireAndForgetMethod asyncCallMethod;
if (!String.IsNullOrEmpty(asyncCallMethodStr) && Utils.EnumTryParse<FireAndForgetMethod>(asyncCallMethodStr, out asyncCallMethod)) if (!String.IsNullOrEmpty(asyncCallMethodStr) && Utils.EnumTryParse<FireAndForgetMethod>(asyncCallMethodStr, out asyncCallMethod))
@ -164,7 +148,7 @@ namespace OpenSim
break; break;
case "rest": case "rest":
m_console = new RemoteConsole("Region"); m_console = new RemoteConsole("Region");
((RemoteConsole)m_console).ReadConfig(m_config.Source); ((RemoteConsole)m_console).ReadConfig(Config);
break; break;
default: default:
m_console = new LocalConsole("Region"); m_console = new LocalConsole("Region");
@ -174,6 +158,7 @@ namespace OpenSim
MainConsole.Instance = m_console; MainConsole.Instance = m_console;
RegisterCommonAppenders(Config.Configs["Startup"]);
RegisterConsoleCommands(); RegisterConsoleCommands();
base.StartupSpecific(); base.StartupSpecific();
@ -372,26 +357,6 @@ namespace OpenSim
"restart", "restart",
"Restart all sims in this instance", RunCommand); "Restart all sims in this instance", RunCommand);
m_console.Commands.AddCommand("General", false, "config set",
"config set <section> <key> <value>",
"Set a config option. In most cases this is not useful since changed parameters are not dynamically reloaded. Neither do changed parameters persist - you will have to change a config file manually and restart.", HandleConfig);
m_console.Commands.AddCommand("General", false, "config get",
"config get [<section>] [<key>]",
"Synonym for config show",
HandleConfig);
m_console.Commands.AddCommand("General", false, "config show",
"config show [<section>] [<key>]",
"Show config information",
"If neither section nor field are specified, then the whole current configuration is printed." + Environment.NewLine
+ "If a section is given but not a field, then all fields in that section are printed.",
HandleConfig);
m_console.Commands.AddCommand("General", false, "config save",
"config save <path>",
"Save current configuration to a file at the given path", HandleConfig);
m_console.Commands.AddCommand("General", false, "command-script", m_console.Commands.AddCommand("General", false, "command-script",
"command-script <script>", "command-script <script>",
"Run a command script from file", RunCommand); "Run a command script from file", RunCommand);
@ -501,35 +466,6 @@ namespace OpenSim
MainConsole.Instance.Output(""); MainConsole.Instance.Output("");
} }
/// <summary>
/// Run an optional startup list of commands
/// </summary>
/// <param name="fileName"></param>
private void RunCommandScript(string fileName)
{
if (File.Exists(fileName))
{
m_log.Info("[COMMANDFILE]: Running " + fileName);
using (StreamReader readFile = File.OpenText(fileName))
{
string currentCommand;
while ((currentCommand = readFile.ReadLine()) != null)
{
currentCommand = currentCommand.Trim();
if (!(currentCommand == ""
|| currentCommand.StartsWith(";")
|| currentCommand.StartsWith("//")
|| currentCommand.StartsWith("#")))
{
m_log.Info("[COMMANDFILE]: Running '" + currentCommand + "'");
m_console.RunCommand(currentCommand);
}
}
}
}
}
/// <summary> /// <summary>
/// Opens a file and uses it as input to the console command parser. /// Opens a file and uses it as input to the console command parser.
/// </summary> /// </summary>
@ -634,111 +570,9 @@ namespace OpenSim
bool changed = PopulateRegionEstateInfo(regInfo); bool changed = PopulateRegionEstateInfo(regInfo);
IScene scene; IScene scene;
CreateRegion(regInfo, true, out scene); CreateRegion(regInfo, true, out scene);
if (changed) if (changed)
regInfo.EstateSettings.Save(); regInfo.EstateSettings.Save();
}
/// <summary>
/// Change and load configuration file data.
/// </summary>
/// <param name="module"></param>
/// <param name="cmd"></param>
private void HandleConfig(string module, string[] cmd)
{
List<string> args = new List<string>(cmd);
args.RemoveAt(0);
string[] cmdparams = args.ToArray();
if (cmdparams.Length > 0)
{
string firstParam = cmdparams[0].ToLower();
switch (firstParam)
{
case "set":
if (cmdparams.Length < 4)
{
Notice("Syntax: config set <section> <key> <value>");
Notice("Example: config set ScriptEngine.DotNetEngine NumberOfScriptThreads 5");
}
else
{
IConfig c;
IConfigSource source = new IniConfigSource();
c = source.AddConfig(cmdparams[1]);
if (c != null)
{
string _value = String.Join(" ", cmdparams, 3, cmdparams.Length - 3);
c.Set(cmdparams[2], _value);
m_config.Source.Merge(source);
Notice("In section [{0}], set {1} = {2}", c.Name, cmdparams[2], _value);
}
}
break;
case "get":
case "show":
if (cmdparams.Length == 1)
{
foreach (IConfig config in m_config.Source.Configs)
{
Notice("[{0}]", config.Name);
string[] keys = config.GetKeys();
foreach (string key in keys)
Notice(" {0} = {1}", key, config.GetString(key));
}
}
else if (cmdparams.Length == 2 || cmdparams.Length == 3)
{
IConfig config = m_config.Source.Configs[cmdparams[1]];
if (config == null)
{
Notice("Section \"{0}\" does not exist.",cmdparams[1]);
break;
}
else
{
if (cmdparams.Length == 2)
{
Notice("[{0}]", config.Name);
foreach (string key in config.GetKeys())
Notice(" {0} = {1}", key, config.GetString(key));
}
else
{
Notice(
"config get {0} {1} : {2}",
cmdparams[1], cmdparams[2], config.GetString(cmdparams[2]));
}
}
}
else
{
Notice("Syntax: config {0} [<section>] [<key>]", firstParam);
Notice("Example: config {0} ScriptEngine.DotNetEngine NumberOfScriptThreads", firstParam);
}
break;
case "save":
if (cmdparams.Length < 2)
{
Notice("Syntax: config save <path>");
return;
}
if (Application.iniFilePath == cmdparams[1])
{
Notice("Path can not be " + Application.iniFilePath);
return;
}
Notice("Saving configuration file: " + cmdparams[1]);
m_config.Save(cmdparams[1]);
break;
}
}
} }
/// <summary> /// <summary>
@ -787,13 +621,6 @@ namespace OpenSim
switch (command) switch (command)
{ {
case "command-script":
if (cmdparams.Length > 0)
{
RunCommandScript(cmdparams[0]);
}
break;
case "backup": case "backup":
MainConsole.Instance.Output("Triggering save of pending object updates to persistent store"); MainConsole.Instance.Output("Triggering save of pending object updates to persistent store");
SceneManager.BackupCurrentScene(); SceneManager.BackupCurrentScene();
@ -837,12 +664,20 @@ namespace OpenSim
if (!SceneManager.TrySetCurrentScene(newRegionName)) if (!SceneManager.TrySetCurrentScene(newRegionName))
MainConsole.Instance.Output(String.Format("Couldn't select region {0}", newRegionName)); MainConsole.Instance.Output(String.Format("Couldn't select region {0}", newRegionName));
else
RefreshPrompt();
} }
else else
{ {
MainConsole.Instance.Output("Usage: change region <region name>"); MainConsole.Instance.Output("Usage: change region <region name>");
} }
}
/// <summary>
/// Refreshs prompt with the current selection details.
/// </summary>
private void RefreshPrompt()
{
string regionName = (SceneManager.CurrentScene == null ? "root" : SceneManager.CurrentScene.RegionInfo.RegionName); string regionName = (SceneManager.CurrentScene == null ? "root" : SceneManager.CurrentScene.RegionInfo.RegionName);
MainConsole.Instance.Output(String.Format("Currently selected region is {0}", regionName)); MainConsole.Instance.Output(String.Format("Currently selected region is {0}", regionName));
@ -864,6 +699,18 @@ namespace OpenSim
m_console.ConsoleScene = SceneManager.CurrentScene; m_console.ConsoleScene = SceneManager.CurrentScene;
} }
protected override void HandleRestartRegion(RegionInfo whichRegion)
{
base.HandleRestartRegion(whichRegion);
// Where we are restarting multiple scenes at once, a previous call to RefreshPrompt may have set the
// m_console.ConsoleScene to null (indicating all scenes).
if (m_console.ConsoleScene != null && whichRegion.RegionName == ((Scene)m_console.ConsoleScene).Name)
SceneManager.TrySetCurrentScene(whichRegion.RegionName);
RefreshPrompt();
}
/// <summary> /// <summary>
/// Turn on some debugging values for OpenSim. /// Turn on some debugging values for OpenSim.
/// </summary> /// </summary>

View File

@ -100,13 +100,7 @@ namespace OpenSim
/// <value> /// <value>
/// The config information passed into the OpenSimulator region server. /// The config information passed into the OpenSimulator region server.
/// </value> /// </value>
public OpenSimConfigSource ConfigSource public OpenSimConfigSource ConfigSource { get; private set; }
{
get { return m_config; }
set { m_config = value; }
}
protected OpenSimConfigSource m_config;
public List<IClientNetworkServer> ClientServers public List<IClientNetworkServer> ClientServers
{ {
@ -146,13 +140,14 @@ namespace OpenSim
protected virtual void LoadConfigSettings(IConfigSource configSource) protected virtual void LoadConfigSettings(IConfigSource configSource)
{ {
m_configLoader = new ConfigurationLoader(); m_configLoader = new ConfigurationLoader();
m_config = m_configLoader.LoadConfigSettings(configSource, envConfigSource, out m_configSettings, out m_networkServersInfo); ConfigSource = m_configLoader.LoadConfigSettings(configSource, envConfigSource, out m_configSettings, out m_networkServersInfo);
Config = ConfigSource.Source;
ReadExtraConfigSettings(); ReadExtraConfigSettings();
} }
protected virtual void ReadExtraConfigSettings() protected virtual void ReadExtraConfigSettings()
{ {
IConfig networkConfig = m_config.Source.Configs["Network"]; IConfig networkConfig = Config.Configs["Network"];
if (networkConfig != null) if (networkConfig != null)
{ {
proxyUrl = networkConfig.GetString("proxy_url", ""); proxyUrl = networkConfig.GetString("proxy_url", "");
@ -185,7 +180,7 @@ namespace OpenSim
/// </summary> /// </summary>
protected override void StartupSpecific() protected override void StartupSpecific()
{ {
IConfig startupConfig = m_config.Source.Configs["Startup"]; IConfig startupConfig = Config.Configs["Startup"];
if (startupConfig != null) if (startupConfig != null)
{ {
string pidFile = startupConfig.GetString("PIDFile", String.Empty); string pidFile = startupConfig.GetString("PIDFile", String.Empty);
@ -196,7 +191,7 @@ namespace OpenSim
} }
// Load the simulation data service // Load the simulation data service
IConfig simDataConfig = m_config.Source.Configs["SimulationDataStore"]; IConfig simDataConfig = Config.Configs["SimulationDataStore"];
if (simDataConfig == null) if (simDataConfig == null)
throw new Exception("Configuration file is missing the [SimulationDataStore] section. Have you copied OpenSim.ini.example to OpenSim.ini to reference config-include/ files?"); throw new Exception("Configuration file is missing the [SimulationDataStore] section. Have you copied OpenSim.ini.example to OpenSim.ini to reference config-include/ files?");
@ -204,7 +199,7 @@ namespace OpenSim
if (String.IsNullOrEmpty(module)) if (String.IsNullOrEmpty(module))
throw new Exception("Configuration file is missing the LocalServiceModule parameter in the [SimulationDataStore] section."); throw new Exception("Configuration file is missing the LocalServiceModule parameter in the [SimulationDataStore] section.");
m_simulationDataService = ServerUtils.LoadPlugin<ISimulationDataService>(module, new object[] { m_config.Source }); m_simulationDataService = ServerUtils.LoadPlugin<ISimulationDataService>(module, new object[] { Config });
if (m_simulationDataService == null) if (m_simulationDataService == null)
throw new Exception( throw new Exception(
string.Format( string.Format(
@ -212,7 +207,7 @@ namespace OpenSim
module)); module));
// Load the estate data service // Load the estate data service
IConfig estateDataConfig = m_config.Source.Configs["EstateDataStore"]; IConfig estateDataConfig = Config.Configs["EstateDataStore"];
if (estateDataConfig == null) if (estateDataConfig == null)
throw new Exception("Configuration file is missing the [EstateDataStore] section. Have you copied OpenSim.ini.example to OpenSim.ini to reference config-include/ files?"); throw new Exception("Configuration file is missing the [EstateDataStore] section. Have you copied OpenSim.ini.example to OpenSim.ini to reference config-include/ files?");
@ -220,7 +215,7 @@ namespace OpenSim
if (String.IsNullOrEmpty(module)) if (String.IsNullOrEmpty(module))
throw new Exception("Configuration file is missing the LocalServiceModule parameter in the [EstateDataStore] section"); throw new Exception("Configuration file is missing the LocalServiceModule parameter in the [EstateDataStore] section");
m_estateDataService = ServerUtils.LoadPlugin<IEstateDataService>(module, new object[] { m_config.Source }); m_estateDataService = ServerUtils.LoadPlugin<IEstateDataService>(module, new object[] { Config });
if (m_estateDataService == null) if (m_estateDataService == null)
throw new Exception( throw new Exception(
string.Format( string.Format(
@ -242,7 +237,7 @@ namespace OpenSim
} }
} }
protected virtual void AddPluginCommands(CommandConsole console) protected virtual void AddPluginCommands(ICommandConsole console)
{ {
List<string> topics = GetHelpTopics(); List<string> topics = GetHelpTopics();
@ -304,7 +299,7 @@ namespace OpenSim
// Called from base.StartUp() // Called from base.StartUp()
m_httpServerPort = m_networkServersInfo.HttpListenerPort; m_httpServerPort = m_networkServersInfo.HttpListenerPort;
SceneManager.OnRestartSim += handleRestartRegion; SceneManager.OnRestartSim += HandleRestartRegion;
// Only enable the watchdogs when all regions are ready. Otherwise we get false positives when cpu is // Only enable the watchdogs when all regions are ready. Otherwise we get false positives when cpu is
// heavily used during initial startup. // heavily used during initial startup.
@ -369,7 +364,7 @@ namespace OpenSim
} }
IClientNetworkServer clientServer; IClientNetworkServer clientServer;
Scene scene = SetupScene(regionInfo, proxyOffset, m_config.Source, out clientServer); Scene scene = SetupScene(regionInfo, proxyOffset, Config, out clientServer);
m_log.Info("[MODULES]: Loading Region's modules (old style)"); m_log.Info("[MODULES]: Loading Region's modules (old style)");
@ -451,10 +446,10 @@ namespace OpenSim
string estateOwnerPassword = null; string estateOwnerPassword = null;
string rawEstateOwnerUuid = null; string rawEstateOwnerUuid = null;
if (m_config.Source.Configs[ESTATE_SECTION_NAME] != null) if (Config.Configs[ESTATE_SECTION_NAME] != null)
{ {
string defaultEstateOwnerName string defaultEstateOwnerName
= m_config.Source.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateOwnerName", "").Trim(); = Config.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateOwnerName", "").Trim();
string[] ownerNames = defaultEstateOwnerName.Split(' '); string[] ownerNames = defaultEstateOwnerName.Split(' ');
if (ownerNames.Length >= 2) if (ownerNames.Length >= 2)
@ -464,9 +459,9 @@ namespace OpenSim
} }
// Info to be used only on Standalone Mode // Info to be used only on Standalone Mode
rawEstateOwnerUuid = m_config.Source.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateOwnerUUID", null); rawEstateOwnerUuid = Config.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateOwnerUUID", null);
estateOwnerEMail = m_config.Source.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateOwnerEMail", null); estateOwnerEMail = Config.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateOwnerEMail", null);
estateOwnerPassword = m_config.Source.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateOwnerPassword", null); estateOwnerPassword = Config.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateOwnerPassword", null);
} }
MainConsole.Instance.OutputFormat("Estate {0} has no owner set.", regionInfo.EstateSettings.EstateName); MainConsole.Instance.OutputFormat("Estate {0} has no owner set.", regionInfo.EstateSettings.EstateName);
@ -713,7 +708,7 @@ namespace OpenSim
return new Scene( return new Scene(
regionInfo, circuitManager, sceneGridService, regionInfo, circuitManager, sceneGridService,
simDataService, estateDataService, false, simDataService, estateDataService, false,
m_config.Source, m_version); Config, m_version);
} }
protected void ShutdownClientServer(RegionInfo whichRegion) protected void ShutdownClientServer(RegionInfo whichRegion)
@ -740,9 +735,11 @@ namespace OpenSim
} }
} }
public void handleRestartRegion(RegionInfo whichRegion) protected virtual void HandleRestartRegion(RegionInfo whichRegion)
{ {
m_log.Info("[OPENSIM]: Got restart signal from SceneManager"); m_log.InfoFormat(
"[OPENSIM]: Got restart signal from SceneManager for region {0} ({1},{2})",
whichRegion.RegionName, whichRegion.RegionLocX, whichRegion.RegionLocY);
ShutdownClientServer(whichRegion); ShutdownClientServer(whichRegion);
IScene scene; IScene scene;
@ -754,7 +751,7 @@ namespace OpenSim
protected override PhysicsScene GetPhysicsScene(string osSceneIdentifier) protected override PhysicsScene GetPhysicsScene(string osSceneIdentifier)
{ {
return GetPhysicsScene( return GetPhysicsScene(
m_configSettings.PhysicsEngine, m_configSettings.MeshEngineName, m_config.Source, osSceneIdentifier); m_configSettings.PhysicsEngine, m_configSettings.MeshEngineName, Config, osSceneIdentifier);
} }
/// <summary> /// <summary>
@ -888,7 +885,6 @@ namespace OpenSim
m_log.Info("[SHUTDOWN]: Closing all threads"); m_log.Info("[SHUTDOWN]: Closing all threads");
m_log.Info("[SHUTDOWN]: Killing listener thread"); m_log.Info("[SHUTDOWN]: Killing listener thread");
m_log.Info("[SHUTDOWN]: Killing clients"); m_log.Info("[SHUTDOWN]: Killing clients");
// TODO: implement this
m_log.Info("[SHUTDOWN]: Closing console and terminating"); m_log.Info("[SHUTDOWN]: Closing console and terminating");
try try
@ -897,7 +893,7 @@ namespace OpenSim
} }
catch (Exception e) catch (Exception e)
{ {
m_log.ErrorFormat("[SHUTDOWN]: Ignoring failure during shutdown - {0}", e); m_log.Error("[SHUTDOWN]: Ignoring failure during shutdown - ", e);
} }
} }
@ -991,9 +987,9 @@ namespace OpenSim
string defaultEstateName = null; string defaultEstateName = null;
if (m_config.Source.Configs[ESTATE_SECTION_NAME] != null) if (Config.Configs[ESTATE_SECTION_NAME] != null)
{ {
defaultEstateName = m_config.Source.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateName", null); defaultEstateName = Config.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateName", null);
if (defaultEstateName != null) if (defaultEstateName != null)
{ {
@ -1076,28 +1072,14 @@ namespace OpenSim
MainConsole.Instance.Output("Joining the estate failed. Please try again."); MainConsole.Instance.Output("Joining the estate failed. Please try again.");
} }
} }
} }
return true; // need to update the database return true; // need to update the database
} }
} }
public class OpenSimConfigSource public class OpenSimConfigSource
{ {
public IConfigSource Source; public IConfigSource Source;
public void Save(string path)
{
if (Source is IniConfigSource)
{
IniConfigSource iniCon = (IniConfigSource) Source;
iniCon.Save(path);
}
else if (Source is XmlConfigSource)
{
XmlConfigSource xmlCon = (XmlConfigSource) Source;
xmlCon.Save(path);
}
}
} }
} }

View File

@ -44,7 +44,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.ClientStack.Linden.Tests namespace OpenSim.Region.ClientStack.Linden.Tests
{ {
[TestFixture] [TestFixture]
public class EventQueueTests public class EventQueueTests : OpenSimTestCase
{ {
private TestScene m_scene; private TestScene m_scene;

View File

@ -43,7 +43,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.ClientStack.LindenUDP.Tests namespace OpenSim.Region.ClientStack.LindenUDP.Tests
{ {
[TestFixture] [TestFixture]
public class LLImageManagerTests public class LLImageManagerTests : OpenSimTestCase
{ {
private AssetBase m_testImageAsset; private AssetBase m_testImageAsset;
private Scene scene; private Scene scene;

View File

@ -39,7 +39,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
/// Tests for the LL packet handler /// Tests for the LL packet handler
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public class PacketHandlerTests public class PacketHandlerTests : OpenSimTestCase
{ {
// [Test] // [Test]
// /// <summary> // /// <summary>

View File

@ -47,7 +47,7 @@ namespace OpenSim.Region.CoreModules.Asset.Tests
/// At the moment we're only test the in-memory part of the FlotsamAssetCache. This is a considerable weakness. /// At the moment we're only test the in-memory part of the FlotsamAssetCache. This is a considerable weakness.
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public class FlotsamAssetCacheTests public class FlotsamAssetCacheTests : OpenSimTestCase
{ {
protected TestScene m_scene; protected TestScene m_scene;
protected FlotsamAssetCache m_cache; protected FlotsamAssetCache m_cache;

View File

@ -39,7 +39,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
{ {
[TestFixture] [TestFixture]
public class AvatarFactoryModuleTests public class AvatarFactoryModuleTests : OpenSimTestCase
{ {
/// <summary> /// <summary>
/// Only partial right now since we don't yet test that it's ended up in the avatar appearance service. /// Only partial right now since we don't yet test that it's ended up in the avatar appearance service.

View File

@ -40,7 +40,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Avatar.Friends.Tests namespace OpenSim.Region.CoreModules.Avatar.Friends.Tests
{ {
[TestFixture] [TestFixture]
public class FriendsModuleTests public class FriendsModuleTests : OpenSimTestCase
{ {
private FriendsModule m_fm; private FriendsModule m_fm;
private TestScene m_scene; private TestScene m_scene;

View File

@ -49,7 +49,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests
{ {
[TestFixture] [TestFixture]
public class InventoryAccessModuleTests public class InventoryAccessModuleTests : OpenSimTestCase
{ {
protected TestScene m_scene; protected TestScene m_scene;
protected BasicInventoryAccessModule m_iam; protected BasicInventoryAccessModule m_iam;

View File

@ -0,0 +1,137 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using log4net;
using Mono.Addins;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Server.Base;
using OpenSim.Services.Interfaces;
using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence
{
public class BasePresenceServiceConnector : IPresenceService
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected bool m_Enabled;
protected PresenceDetector m_PresenceDetector;
/// <summary>
/// Underlying presence service. Do not use directly.
/// </summary>
public IPresenceService m_PresenceService;
public Type ReplaceableInterface
{
get { return null; }
}
public void AddRegion(Scene scene)
{
if (!m_Enabled)
return;
// m_log.DebugFormat(
// "[LOCAL PRESENCE CONNECTOR]: Registering IPresenceService to scene {0}", scene.RegionInfo.RegionName);
scene.RegisterModuleInterface<IPresenceService>(this);
m_PresenceDetector.AddRegion(scene);
m_log.InfoFormat("[BASE PRESENCE SERVICE CONNECTOR]: Enabled for region {0}", scene.Name);
}
public void RemoveRegion(Scene scene)
{
if (!m_Enabled)
return;
m_PresenceDetector.RemoveRegion(scene);
}
public void RegionLoaded(Scene scene)
{
if (!m_Enabled)
return;
}
public void PostInitialise()
{
}
public void Close()
{
}
#region IPresenceService
public bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID)
{
m_log.Warn("[BASE PRESENCE SERVICE CONNECTOR]: LoginAgent connector not implemented at the simulators");
return false;
}
public bool LogoutAgent(UUID sessionID)
{
return m_PresenceService.LogoutAgent(sessionID);
}
public bool LogoutRegionAgents(UUID regionID)
{
return m_PresenceService.LogoutRegionAgents(regionID);
}
public bool ReportAgent(UUID sessionID, UUID regionID)
{
return m_PresenceService.ReportAgent(sessionID, regionID);
}
public PresenceInfo GetAgent(UUID sessionID)
{
return m_PresenceService.GetAgent(sessionID);
}
public PresenceInfo[] GetAgents(string[] userIDs)
{
// Don't bother potentially making a useless network call if we not going to ask for any users anyway.
if (userIDs.Length == 0)
return new PresenceInfo[0];
return m_PresenceService.GetAgents(userIDs);
}
#endregion
}
}

View File

@ -24,53 +24,29 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using log4net;
using Mono.Addins;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using OpenSim.Server.Base; using OpenSim.Server.Base;
using OpenSim.Services.Interfaces; using OpenSim.Services.Interfaces;
using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
using OpenMetaverse;
using log4net;
using Mono.Addins;
using Nini.Config;
namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence
{ {
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "LocalPresenceServicesConnector")] [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "LocalPresenceServicesConnector")]
public class LocalPresenceServicesConnector : ISharedRegionModule, IPresenceService public class LocalPresenceServicesConnector : BasePresenceServiceConnector, ISharedRegionModule, IPresenceService
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private bool m_Enabled = false;
private PresenceDetector m_PresenceDetector;
/// <summary>
/// Underlying presence service. Do not use directly.
/// </summary>
public IPresenceService m_PresenceService;
public LocalPresenceServicesConnector()
{
}
public LocalPresenceServicesConnector(IConfigSource source)
{
Initialise(source);
}
#region ISharedRegionModule #region ISharedRegionModule
public Type ReplaceableInterface
{
get { return null; }
}
public string Name public string Name
{ {
get { return "LocalPresenceServicesConnector"; } get { return "LocalPresenceServicesConnector"; }
@ -121,81 +97,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence
} }
} }
public void PostInitialise()
{
}
public void Close()
{
}
public void AddRegion(Scene scene)
{
if (!m_Enabled)
return;
// m_log.DebugFormat(
// "[LOCAL PRESENCE CONNECTOR]: Registering IPresenceService to scene {0}", scene.RegionInfo.RegionName);
scene.RegisterModuleInterface<IPresenceService>(this);
m_PresenceDetector.AddRegion(scene);
m_log.InfoFormat("[LOCAL PRESENCE CONNECTOR]: Enabled local presence for region {0}", scene.RegionInfo.RegionName);
}
public void RemoveRegion(Scene scene)
{
if (!m_Enabled)
return;
m_PresenceDetector.RemoveRegion(scene);
}
public void RegionLoaded(Scene scene)
{
if (!m_Enabled)
return;
}
#endregion #endregion
#region IPresenceService
public bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID)
{
m_log.Warn("[LOCAL PRESENCE CONNECTOR]: LoginAgent connector not implemented at the simulators");
return false;
}
public bool LogoutAgent(UUID sessionID)
{
return m_PresenceService.LogoutAgent(sessionID);
}
public bool LogoutRegionAgents(UUID regionID)
{
return m_PresenceService.LogoutRegionAgents(regionID);
}
public bool ReportAgent(UUID sessionID, UUID regionID)
{
return m_PresenceService.ReportAgent(sessionID, regionID);
}
public PresenceInfo GetAgent(UUID sessionID)
{
return m_PresenceService.GetAgent(sessionID);
}
public PresenceInfo[] GetAgents(string[] userIDs)
{
return m_PresenceService.GetAgents(userIDs);
}
#endregion
} }
} }

View File

@ -43,22 +43,12 @@ using Nini.Config;
namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence
{ {
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "RemotePresenceServicesConnector")] [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "RemotePresenceServicesConnector")]
public class RemotePresenceServicesConnector : ISharedRegionModule, IPresenceService public class RemotePresenceServicesConnector : BasePresenceServiceConnector, ISharedRegionModule
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
#region ISharedRegionModule #region ISharedRegionModule
private bool m_Enabled = false;
private PresenceDetector m_PresenceDetector;
private IPresenceService m_RemoteConnector;
public Type ReplaceableInterface
{
get { return null; }
}
public string Name public string Name
{ {
get { return "RemotePresenceServicesConnector"; } get { return "RemotePresenceServicesConnector"; }
@ -72,7 +62,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence
string name = moduleConfig.GetString("PresenceServices", ""); string name = moduleConfig.GetString("PresenceServices", "");
if (name == Name) if (name == Name)
{ {
m_RemoteConnector = new PresenceServicesConnector(source); m_PresenceService = new PresenceServicesConnector(source);
m_Enabled = true; m_Enabled = true;
@ -81,81 +71,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence
m_log.Info("[REMOTE PRESENCE CONNECTOR]: Remote presence enabled"); m_log.Info("[REMOTE PRESENCE CONNECTOR]: Remote presence enabled");
} }
} }
}
public void PostInitialise()
{
}
public void Close()
{
}
public void AddRegion(Scene scene)
{
if (!m_Enabled)
return;
scene.RegisterModuleInterface<IPresenceService>(this);
m_PresenceDetector.AddRegion(scene);
m_log.InfoFormat("[REMOTE PRESENCE CONNECTOR]: Enabled remote presence for region {0}", scene.RegionInfo.RegionName);
}
public void RemoveRegion(Scene scene)
{
if (!m_Enabled)
return;
m_PresenceDetector.RemoveRegion(scene);
}
public void RegionLoaded(Scene scene)
{
if (!m_Enabled)
return;
} }
#endregion #endregion
#region IPresenceService
public bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID)
{
m_log.Warn("[REMOTE PRESENCE CONNECTOR]: LoginAgent connector not implemented at the simulators");
return false;
}
public bool LogoutAgent(UUID sessionID)
{
return m_RemoteConnector.LogoutAgent(sessionID);
}
public bool LogoutRegionAgents(UUID regionID)
{
return m_RemoteConnector.LogoutRegionAgents(regionID);
}
public bool ReportAgent(UUID sessionID, UUID regionID)
{
return m_RemoteConnector.ReportAgent(sessionID, regionID);
}
public PresenceInfo GetAgent(UUID sessionID)
{
return m_RemoteConnector.GetAgent(sessionID);
}
public PresenceInfo[] GetAgents(string[] userIDs)
{
return m_RemoteConnector.GetAgents(userIDs);
}
#endregion
} }
} }

View File

@ -35,7 +35,6 @@ using NUnit.Framework;
using OpenMetaverse; using OpenMetaverse;
using OpenSim.Framework; using OpenSim.Framework;
using Nini.Config; using Nini.Config;
using OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence; using OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
@ -44,7 +43,7 @@ using OpenSim.Tests.Common;
namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence.Tests namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence.Tests
{ {
[TestFixture] [TestFixture]
public class PresenceConnectorsTests public class PresenceConnectorsTests : OpenSimTestCase
{ {
LocalPresenceServicesConnector m_LocalConnector; LocalPresenceServicesConnector m_LocalConnector;
private void SetUp() private void SetUp()
@ -56,7 +55,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence.Tests
config.Configs["PresenceService"].Set("LocalServiceModule", "OpenSim.Services.PresenceService.dll:PresenceService"); config.Configs["PresenceService"].Set("LocalServiceModule", "OpenSim.Services.PresenceService.dll:PresenceService");
config.Configs["PresenceService"].Set("StorageProvider", "OpenSim.Data.Null.dll"); config.Configs["PresenceService"].Set("StorageProvider", "OpenSim.Data.Null.dll");
m_LocalConnector = new LocalPresenceServicesConnector(config); m_LocalConnector = new LocalPresenceServicesConnector();
m_LocalConnector.Initialise(config);
// Let's stick in a test presence // Let's stick in a test presence
m_LocalConnector.m_PresenceService.LoginAgent(UUID.Zero.ToString(), UUID.Zero, UUID.Zero); m_LocalConnector.m_PresenceService.LoginAgent(UUID.Zero.ToString(), UUID.Zero, UUID.Zero);

View File

@ -552,13 +552,22 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// Validate User and Group UUID's // Validate User and Group UUID's
if (!ResolveUserUuid(scene, parcel.OwnerID)) if (parcel.IsGroupOwned)
parcel.OwnerID = m_rootScene.RegionInfo.EstateSettings.EstateOwner;
if (!ResolveGroupUuid(parcel.GroupID))
{ {
parcel.GroupID = UUID.Zero; if (!ResolveGroupUuid(parcel.GroupID))
parcel.IsGroupOwned = false; {
parcel.OwnerID = m_rootScene.RegionInfo.EstateSettings.EstateOwner;
parcel.GroupID = UUID.Zero;
parcel.IsGroupOwned = false;
}
}
else
{
if (!ResolveUserUuid(scene, parcel.OwnerID))
parcel.OwnerID = m_rootScene.RegionInfo.EstateSettings.EstateOwner;
if (!ResolveGroupUuid(parcel.GroupID))
parcel.GroupID = UUID.Zero;
} }
List<LandAccessEntry> accessList = new List<LandAccessEntry>(); List<LandAccessEntry> accessList = new List<LandAccessEntry>();
@ -571,8 +580,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
parcel.ParcelAccessList = accessList; parcel.ParcelAccessList = accessList;
// m_log.DebugFormat( // m_log.DebugFormat(
// "[ARCHIVER]: Adding parcel {0}, local id {1}, area {2}", // "[ARCHIVER]: Adding parcel {0}, local id {1}, owner {2}, group {3}, isGroupOwned {4}, area {5}",
// parcel.Name, parcel.LocalID, parcel.Area); // parcel.Name, parcel.LocalID, parcel.OwnerID, parcel.GroupID, parcel.IsGroupOwned, parcel.Area);
landData.Add(parcel); landData.Add(parcel);
} }

View File

@ -167,7 +167,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver
} }
scenesGroup.CalcSceneLocations(); scenesGroup.CalcSceneLocations();
m_archiveWriter = new TarArchiveWriter(m_saveStream); m_archiveWriter = new TarArchiveWriter(m_saveStream);
try try
@ -216,7 +215,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver
} }
} }
private void ArchiveOneRegion(Scene scene, string regionDir, Dictionary<UUID, AssetType> assetUuids) private void ArchiveOneRegion(Scene scene, string regionDir, Dictionary<UUID, AssetType> assetUuids)
{ {
m_log.InfoFormat("[ARCHIVER]: Writing region {0}", scene.RegionInfo.RegionName); m_log.InfoFormat("[ARCHIVER]: Writing region {0}", scene.RegionInfo.RegionName);
@ -540,7 +538,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver
xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y)); xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y));
} }
protected void Save(Scene scene, List<SceneObjectGroup> sceneObjects, string regionDir) protected void Save(Scene scene, List<SceneObjectGroup> sceneObjects, string regionDir)
{ {
if (regionDir != string.Empty) if (regionDir != string.Empty)
@ -560,8 +557,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
foreach (ILandObject lo in landObjects) foreach (ILandObject lo in landObjects)
{ {
LandData landData = lo.LandData; LandData landData = lo.LandData;
string landDataPath = String.Format("{0}{1}{2}.xml", string landDataPath
regionDir, ArchiveConstants.LANDDATA_PATH, landData.GlobalID.ToString()); = String.Format("{0}{1}", regionDir, ArchiveConstants.CreateOarLandDataPath(landData));
m_archiveWriter.WriteFile(landDataPath, LandDataSerializer.Serialize(landData, m_options)); m_archiveWriter.WriteFile(landDataPath, LandDataSerializer.Serialize(landData, m_options));
} }
@ -604,7 +601,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver
CloseArchive(String.Empty); CloseArchive(String.Empty);
} }
/// <summary> /// <summary>
/// Closes the archive and notifies that we're done. /// Closes the archive and notifies that we're done.
@ -629,6 +625,5 @@ namespace OpenSim.Region.CoreModules.World.Archiver
m_rootScene.EventManager.TriggerOarFileSaved(m_requestId, errorMessage); m_rootScene.EventManager.TriggerOarFileSaved(m_requestId, errorMessage);
} }
} }
} }

View File

@ -31,16 +31,19 @@ using System.IO;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using log4net.Config; using log4net.Config;
using Nini.Config;
using NUnit.Framework; using NUnit.Framework;
using OpenMetaverse; using OpenMetaverse;
using OpenMetaverse.Assets; using OpenMetaverse.Assets;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Framework.Serialization; using OpenSim.Framework.Serialization;
using OpenSim.Framework.Serialization.External; using OpenSim.Framework.Serialization.External;
using OpenSim.Region.CoreModules.World.Land;
using OpenSim.Region.CoreModules.World.Serialiser; using OpenSim.Region.CoreModules.World.Serialiser;
using OpenSim.Region.CoreModules.World.Terrain; using OpenSim.Region.CoreModules.World.Terrain;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization; using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups;
using OpenSim.Tests.Common; using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock; using OpenSim.Tests.Common.Mock;
using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants; using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants;
@ -69,9 +72,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
{ {
base.SetUp(); base.SetUp();
// FIXME: Do something about this - relying on statics in unit tests causes trouble sooner or later
new SceneManager();
m_archiverModule = new ArchiverModule(); m_archiverModule = new ArchiverModule();
m_serialiserModule = new SerialiserModule(); m_serialiserModule = new SerialiserModule();
TerrainModule terrainModule = new TerrainModule(); TerrainModule terrainModule = new TerrainModule();
@ -127,6 +127,53 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
return new SceneObjectPart(ownerId, shape, groupPosition, rotationOffset, offsetPosition) { Name = partName }; return new SceneObjectPart(ownerId, shape, groupPosition, rotationOffset, offsetPosition) { Name = partName };
} }
private void CreateTestObjects(Scene scene, out SceneObjectGroup sog1, out SceneObjectGroup sog2, out UUID ncAssetUuid)
{
SceneObjectPart part1 = CreateSceneObjectPart1();
sog1 = new SceneObjectGroup(part1);
scene.AddNewSceneObject(sog1, false);
AssetNotecard nc = new AssetNotecard();
nc.BodyText = "Hello World!";
nc.Encode();
ncAssetUuid = UUID.Random();
UUID ncItemUuid = UUID.Random();
AssetBase ncAsset
= AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero);
m_scene.AssetService.Store(ncAsset);
TaskInventoryItem ncItem
= new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid };
SceneObjectPart part2 = CreateSceneObjectPart2();
sog2 = new SceneObjectGroup(part2);
part2.Inventory.AddInventoryItem(ncItem, true);
scene.AddNewSceneObject(sog2, false);
}
private static void CreateSoundAsset(TarArchiveWriter tar, Assembly assembly, string soundDataResourceName, out byte[] soundData, out UUID soundUuid)
{
using (Stream resource = assembly.GetManifestResourceStream(soundDataResourceName))
{
using (BinaryReader br = new BinaryReader(resource))
{
// FIXME: Use the inspector instead
soundData = br.ReadBytes(99999999);
soundUuid = UUID.Parse("00000000-0000-0000-0000-000000000001");
string soundAssetFileName
= ArchiveConstants.ASSETS_PATH + soundUuid
+ ArchiveConstants.ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV];
tar.WriteFile(soundAssetFileName, soundData);
/*
AssetBase soundAsset = AssetHelpers.CreateAsset(soundUuid, soundData);
scene.AssetService.Store(soundAsset);
asset1FileName = ArchiveConstants.ASSETS_PATH + soundUuid + ".wav";
*/
}
}
}
/// <summary> /// <summary>
/// Test saving an OpenSim Region Archive. /// Test saving an OpenSim Region Archive.
@ -204,30 +251,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
// TODO: Test presence of more files and contents of files. // TODO: Test presence of more files and contents of files.
} }
private void CreateTestObjects(Scene scene, out SceneObjectGroup sog1, out SceneObjectGroup sog2, out UUID ncAssetUuid)
{
SceneObjectPart part1 = CreateSceneObjectPart1();
sog1 = new SceneObjectGroup(part1);
scene.AddNewSceneObject(sog1, false);
AssetNotecard nc = new AssetNotecard();
nc.BodyText = "Hello World!";
nc.Encode();
ncAssetUuid = UUID.Random();
UUID ncItemUuid = UUID.Random();
AssetBase ncAsset
= AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero);
m_scene.AssetService.Store(ncAsset);
TaskInventoryItem ncItem
= new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid };
SceneObjectPart part2 = CreateSceneObjectPart2();
sog2 = new SceneObjectGroup(part2);
part2.Inventory.AddInventoryItem(ncItem, true);
scene.AddNewSceneObject(sog2, false);
}
/// <summary> /// <summary>
/// Test saving an OpenSim Region Archive with the no assets option /// Test saving an OpenSim Region Archive with the no assets option
/// </summary> /// </summary>
@ -308,59 +331,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
// TODO: Test presence of more files and contents of files. // TODO: Test presence of more files and contents of files.
} }
/// <summary>
/// Test loading an OpenSim Region Archive where the scene object parts are not ordered by link number (e.g.
/// 2 can come after 3).
/// </summary>
[Test]
public void TestLoadOarUnorderedParts()
{
TestHelpers.InMethod();
UUID ownerId = TestHelpers.ParseTail(0xaaaa);
MemoryStream archiveWriteStream = new MemoryStream();
TarArchiveWriter tar = new TarArchiveWriter(archiveWriteStream);
tar.WriteFile(
ArchiveConstants.CONTROL_FILE_PATH,
new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
SceneObjectGroup sog1 = SceneHelpers.CreateSceneObject(1, ownerId, "obj1-", 0x11);
SceneObjectPart sop2
= SceneHelpers.CreateSceneObjectPart("obj1-Part2", TestHelpers.ParseTail(0x12), ownerId);
SceneObjectPart sop3
= SceneHelpers.CreateSceneObjectPart("obj1-Part3", TestHelpers.ParseTail(0x13), ownerId);
// Add the parts so they will be written out in reverse order to the oar
sog1.AddPart(sop3);
sop3.LinkNum = 3;
sog1.AddPart(sop2);
sop2.LinkNum = 2;
tar.WriteFile(
ArchiveConstants.CreateOarObjectPath(sog1.Name, sog1.UUID, sog1.AbsolutePosition),
SceneObjectSerializer.ToXml2Format(sog1));
tar.Close();
MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray());
lock (this)
{
m_scene.EventManager.OnOarFileLoaded += LoadCompleted;
m_archiverModule.DearchiveRegion(archiveReadStream);
}
Assert.That(m_lastErrorMessage, Is.Null);
SceneObjectPart part2 = m_scene.GetSceneObjectPart("obj1-Part2");
Assert.That(part2.LinkNum, Is.EqualTo(2));
SceneObjectPart part3 = m_scene.GetSceneObjectPart("obj1-Part3");
Assert.That(part3.LinkNum, Is.EqualTo(3));
}
/// <summary> /// <summary>
/// Test loading an OpenSim Region Archive. /// Test loading an OpenSim Region Archive.
/// </summary> /// </summary>
@ -435,50 +405,57 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
TestLoadedRegion(part1, soundItemName, soundData); TestLoadedRegion(part1, soundItemName, soundData);
} }
private static void CreateSoundAsset(TarArchiveWriter tar, Assembly assembly, string soundDataResourceName, out byte[] soundData, out UUID soundUuid) /// <summary>
/// Test loading an OpenSim Region Archive where the scene object parts are not ordered by link number (e.g.
/// 2 can come after 3).
/// </summary>
[Test]
public void TestLoadOarUnorderedParts()
{ {
using (Stream resource = assembly.GetManifestResourceStream(soundDataResourceName)) TestHelpers.InMethod();
UUID ownerId = TestHelpers.ParseTail(0xaaaa);
MemoryStream archiveWriteStream = new MemoryStream();
TarArchiveWriter tar = new TarArchiveWriter(archiveWriteStream);
tar.WriteFile(
ArchiveConstants.CONTROL_FILE_PATH,
new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
SceneObjectGroup sog1 = SceneHelpers.CreateSceneObject(1, ownerId, "obj1-", 0x11);
SceneObjectPart sop2
= SceneHelpers.CreateSceneObjectPart("obj1-Part2", TestHelpers.ParseTail(0x12), ownerId);
SceneObjectPart sop3
= SceneHelpers.CreateSceneObjectPart("obj1-Part3", TestHelpers.ParseTail(0x13), ownerId);
// Add the parts so they will be written out in reverse order to the oar
sog1.AddPart(sop3);
sop3.LinkNum = 3;
sog1.AddPart(sop2);
sop2.LinkNum = 2;
tar.WriteFile(
ArchiveConstants.CreateOarObjectPath(sog1.Name, sog1.UUID, sog1.AbsolutePosition),
SceneObjectSerializer.ToXml2Format(sog1));
tar.Close();
MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray());
lock (this)
{ {
using (BinaryReader br = new BinaryReader(resource)) m_scene.EventManager.OnOarFileLoaded += LoadCompleted;
{ m_archiverModule.DearchiveRegion(archiveReadStream);
// FIXME: Use the inspector instead
soundData = br.ReadBytes(99999999);
soundUuid = UUID.Parse("00000000-0000-0000-0000-000000000001");
string soundAssetFileName
= ArchiveConstants.ASSETS_PATH + soundUuid
+ ArchiveConstants.ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV];
tar.WriteFile(soundAssetFileName, soundData);
/*
AssetBase soundAsset = AssetHelpers.CreateAsset(soundUuid, soundData);
scene.AssetService.Store(soundAsset);
asset1FileName = ArchiveConstants.ASSETS_PATH + soundUuid + ".wav";
*/
}
} }
}
private void TestLoadedRegion(SceneObjectPart part1, string soundItemName, byte[] soundData) Assert.That(m_lastErrorMessage, Is.Null);
{
SceneObjectPart object1PartLoaded = m_scene.GetSceneObjectPart(part1.Name);
Assert.That(object1PartLoaded, Is.Not.Null, "object1 was not loaded"); SceneObjectPart part2 = m_scene.GetSceneObjectPart("obj1-Part2");
Assert.That(object1PartLoaded.Name, Is.EqualTo(part1.Name), "object1 names not identical"); Assert.That(part2.LinkNum, Is.EqualTo(2));
Assert.That(object1PartLoaded.GroupPosition, Is.EqualTo(part1.GroupPosition), "object1 group position not equal");
Assert.That(
object1PartLoaded.RotationOffset, Is.EqualTo(part1.RotationOffset), "object1 rotation offset not equal");
Assert.That(
object1PartLoaded.OffsetPosition, Is.EqualTo(part1.OffsetPosition), "object1 offset position not equal");
Assert.That(object1PartLoaded.SitTargetOrientation, Is.EqualTo(part1.SitTargetOrientation));
Assert.That(object1PartLoaded.SitTargetPosition, Is.EqualTo(part1.SitTargetPosition));
TaskInventoryItem loadedSoundItem = object1PartLoaded.Inventory.GetInventoryItems(soundItemName)[0]; SceneObjectPart part3 = m_scene.GetSceneObjectPart("obj1-Part3");
Assert.That(loadedSoundItem, Is.Not.Null, "loaded sound item was null"); Assert.That(part3.LinkNum, Is.EqualTo(3));
AssetBase loadedSoundAsset = m_scene.AssetService.Get(loadedSoundItem.AssetID.ToString());
Assert.That(loadedSoundAsset, Is.Not.Null, "loaded sound asset was null");
Assert.That(loadedSoundAsset.Data, Is.EqualTo(soundData), "saved and loaded sound data do not match");
Assert.Greater(m_scene.LandChannel.AllParcels().Count, 0, "incorrect number of parcels");
} }
/// <summary> /// <summary>
@ -538,8 +515,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
SerialiserModule serialiserModule = new SerialiserModule(); SerialiserModule serialiserModule = new SerialiserModule();
TerrainModule terrainModule = new TerrainModule(); TerrainModule terrainModule = new TerrainModule();
m_sceneHelpers = new SceneHelpers(); SceneHelpers m_sceneHelpers2 = new SceneHelpers();
TestScene scene2 = m_sceneHelpers.SetupScene(); TestScene scene2 = m_sceneHelpers2.SetupScene();
SceneHelpers.SetupSceneModules(scene2, archiverModule, serialiserModule, terrainModule); SceneHelpers.SetupSceneModules(scene2, archiverModule, serialiserModule, terrainModule);
// Make sure there's a valid owner for the owner we saved (this should have been wiped if the code is // Make sure there's a valid owner for the owner we saved (this should have been wiped if the code is
@ -562,6 +539,71 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
} }
} }
/// <summary>
/// Test OAR loading where the land parcel is group deeded.
/// </summary>
/// <remarks>
/// In this situation, the owner ID is set to the group ID.
/// </remarks>
[Test]
public void TestLoadOarDeededLand()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
UUID landID = TestHelpers.ParseTail(0x10);
MockGroupsServicesConnector groupsService = new MockGroupsServicesConnector();
IConfigSource configSource = new IniConfigSource();
IConfig config = configSource.AddConfig("Groups");
config.Set("Enabled", true);
config.Set("Module", "GroupsModule");
config.Set("DebugEnabled", true);
SceneHelpers.SetupSceneModules(
m_scene, configSource, new object[] { new GroupsModule(), groupsService, new LandManagementModule() });
// Create group in scene for loading
// FIXME: For now we'll put up with the issue that we'll get a group ID that varies across tests.
UUID groupID
= groupsService.CreateGroup(UUID.Zero, "group1", "", true, UUID.Zero, 3, true, true, true, UUID.Zero);
// Construct OAR
MemoryStream oarStream = new MemoryStream();
TarArchiveWriter tar = new TarArchiveWriter(oarStream);
tar.WriteDir(ArchiveConstants.LANDDATA_PATH);
tar.WriteFile(
ArchiveConstants.CONTROL_FILE_PATH,
new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
LandObject lo = new LandObject(groupID, true, null);
lo.SetLandBitmap(lo.BasicFullRegionLandBitmap());
LandData ld = lo.LandData;
ld.GlobalID = landID;
string ldPath = ArchiveConstants.CreateOarLandDataPath(ld);
tar.WriteFile(ldPath, LandDataSerializer.Serialize(ld, null));
tar.Close();
oarStream = new MemoryStream(oarStream.ToArray());
// Load OAR
lock (this)
{
m_scene.EventManager.OnOarFileLoaded += LoadCompleted;
m_archiverModule.DearchiveRegion(oarStream);
}
ILandObject rLo = m_scene.LandChannel.GetLandObject(16, 16);
LandData rLd = rLo.LandData;
Assert.That(rLd.GlobalID, Is.EqualTo(landID));
Assert.That(rLd.OwnerID, Is.EqualTo(groupID));
Assert.That(rLd.GroupID, Is.EqualTo(groupID));
Assert.That(rLd.IsGroupOwned, Is.EqualTo(true));
}
/// <summary> /// <summary>
/// Test loading the region settings of an OpenSim Region Archive. /// Test loading the region settings of an OpenSim Region Archive.
/// </summary> /// </summary>
@ -781,9 +823,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
} }
} }
// Save OAR // Save OAR
MemoryStream archiveWriteStream = new MemoryStream(); MemoryStream archiveWriteStream = new MemoryStream();
m_scene.EventManager.OnOarFileSaved += SaveCompleted; m_scene.EventManager.OnOarFileSaved += SaveCompleted;
@ -800,7 +840,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
// Check that the OAR contains the expected data // Check that the OAR contains the expected data
Assert.That(m_lastRequestId, Is.EqualTo(requestId)); Assert.That(m_lastRequestId, Is.EqualTo(requestId));
byte[] archive = archiveWriteStream.ToArray(); byte[] archive = archiveWriteStream.ToArray();
@ -892,7 +931,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
} }
ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup(); ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();
SceneManager.Instance.ForEachScene(delegate(Scene scene) m_sceneHelpers.SceneManager.ForEachScene(delegate(Scene scene)
{ {
scenesGroup.AddScene(scene); scenesGroup.AddScene(scene);
}); });
@ -950,13 +989,13 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
// Delete the current objects, to test that they're loaded from the OAR and didn't // Delete the current objects, to test that they're loaded from the OAR and didn't
// just remain in the scene. // just remain in the scene.
SceneManager.Instance.ForEachScene(delegate(Scene scene) m_sceneHelpers.SceneManager.ForEachScene(delegate(Scene scene)
{ {
scene.DeleteAllSceneObjects(); scene.DeleteAllSceneObjects();
}); });
// Create a "hole", to test that that the corresponding region isn't loaded from the OAR // Create a "hole", to test that that the corresponding region isn't loaded from the OAR
SceneManager.Instance.CloseScene(SceneManager.Instance.Scenes[1]); m_sceneHelpers.SceneManager.CloseScene(SceneManager.Instance.Scenes[1]);
// Check thay the OAR file contains the expected data // Check thay the OAR file contains the expected data
@ -971,10 +1010,32 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(m_lastErrorMessage, Is.Null); Assert.That(m_lastErrorMessage, Is.Null);
Assert.AreEqual(3, SceneManager.Instance.Scenes.Count); Assert.AreEqual(3, m_sceneHelpers.SceneManager.Scenes.Count);
TestLoadedRegion(part1, soundItemName, soundData); TestLoadedRegion(part1, soundItemName, soundData);
} }
private void TestLoadedRegion(SceneObjectPart part1, string soundItemName, byte[] soundData)
{
SceneObjectPart object1PartLoaded = m_scene.GetSceneObjectPart(part1.Name);
Assert.That(object1PartLoaded, Is.Not.Null, "object1 was not loaded");
Assert.That(object1PartLoaded.Name, Is.EqualTo(part1.Name), "object1 names not identical");
Assert.That(object1PartLoaded.GroupPosition, Is.EqualTo(part1.GroupPosition), "object1 group position not equal");
Assert.That(
object1PartLoaded.RotationOffset, Is.EqualTo(part1.RotationOffset), "object1 rotation offset not equal");
Assert.That(
object1PartLoaded.OffsetPosition, Is.EqualTo(part1.OffsetPosition), "object1 offset position not equal");
Assert.That(object1PartLoaded.SitTargetOrientation, Is.EqualTo(part1.SitTargetOrientation));
Assert.That(object1PartLoaded.SitTargetPosition, Is.EqualTo(part1.SitTargetPosition));
TaskInventoryItem loadedSoundItem = object1PartLoaded.Inventory.GetInventoryItems(soundItemName)[0];
Assert.That(loadedSoundItem, Is.Not.Null, "loaded sound item was null");
AssetBase loadedSoundAsset = m_scene.AssetService.Get(loadedSoundItem.AssetID.ToString());
Assert.That(loadedSoundAsset, Is.Not.Null, "loaded sound asset was null");
Assert.That(loadedSoundAsset.Data, Is.EqualTo(soundData), "saved and loaded sound data do not match");
Assert.Greater(m_scene.LandChannel.AllParcels().Count, 0, "incorrect number of parcels");
}
} }
} }

View File

@ -53,6 +53,11 @@ namespace OpenSim.Region.CoreModules.World.Estate
protected EstateManagementCommands m_commands; protected EstateManagementCommands m_commands;
/// <summary>
/// If false, region restart requests from the client are blocked even if they are otherwise legitimate.
/// </summary>
public bool AllowRegionRestartFromClient { get; set; }
private EstateTerrainXferHandler TerrainUploader; private EstateTerrainXferHandler TerrainUploader;
public TelehubManager m_Telehub; public TelehubManager m_Telehub;
@ -60,6 +65,53 @@ namespace OpenSim.Region.CoreModules.World.Estate
public event ChangeDelegate OnEstateInfoChange; public event ChangeDelegate OnEstateInfoChange;
public event MessageDelegate OnEstateMessage; public event MessageDelegate OnEstateMessage;
#region Region Module interface
public string Name { get { return "EstateManagementModule"; } }
public Type ReplaceableInterface { get { return null; } }
public void Initialise(IConfigSource source)
{
AllowRegionRestartFromClient = true;
IConfig config = source.Configs["EstateManagement"];
if (config != null)
AllowRegionRestartFromClient = config.GetBoolean("AllowRegionRestartFromClient", true);
}
public void AddRegion(Scene scene)
{
Scene = scene;
Scene.RegisterModuleInterface<IEstateModule>(this);
Scene.EventManager.OnNewClient += EventManager_OnNewClient;
Scene.EventManager.OnRequestChangeWaterHeight += changeWaterHeight;
m_Telehub = new TelehubManager(scene);
m_commands = new EstateManagementCommands(this);
m_commands.Initialise();
}
public void RemoveRegion(Scene scene) {}
public void RegionLoaded(Scene scene)
{
// Sets up the sun module based no the saved Estate and Region Settings
// DO NOT REMOVE or the sun will stop working
scene.TriggerEstateSunUpdate();
UserManager = scene.RequestModuleInterface<IUserManagement>();
}
public void Close()
{
m_commands.Close();
}
#endregion
#region Packet Data Responders #region Packet Data Responders
private void sendDetailedEstateData(IClientAPI remote_client, UUID invoice) private void sendDetailedEstateData(IClientAPI remote_client, UUID invoice)
@ -184,6 +236,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
Scene.RegionInfo.RegionSettings.TerrainTexture4 = texture; Scene.RegionInfo.RegionSettings.TerrainTexture4 = texture;
break; break;
} }
Scene.RegionInfo.RegionSettings.Save(); Scene.RegionInfo.RegionSettings.Save();
TriggerRegionInfoChange(); TriggerRegionInfoChange();
sendRegionInfoPacketToAll(); sendRegionInfoPacketToAll();
@ -215,6 +268,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
Scene.RegionInfo.RegionSettings.Elevation2NE = highValue; Scene.RegionInfo.RegionSettings.Elevation2NE = highValue;
break; break;
} }
Scene.RegionInfo.RegionSettings.Save(); Scene.RegionInfo.RegionSettings.Save();
TriggerRegionInfoChange(); TriggerRegionInfoChange();
sendRegionHandshakeToAll(); sendRegionHandshakeToAll();
@ -255,6 +309,12 @@ namespace OpenSim.Region.CoreModules.World.Estate
private void handleEstateRestartSimRequest(IClientAPI remoteClient, int timeInSeconds) private void handleEstateRestartSimRequest(IClientAPI remoteClient, int timeInSeconds)
{ {
if (!AllowRegionRestartFromClient)
{
remoteClient.SendAlertMessage("Region restart has been disabled on this simulator.");
return;
}
IRestartModule restartModule = Scene.RequestModuleInterface<IRestartModule>(); IRestartModule restartModule = Scene.RequestModuleInterface<IRestartModule>();
if (restartModule != null) if (restartModule != null)
{ {
@ -271,6 +331,10 @@ namespace OpenSim.Region.CoreModules.World.Estate
} }
restartModule.ScheduleRestart(UUID.Zero, "Region will restart in {0}", times.ToArray(), true); restartModule.ScheduleRestart(UUID.Zero, "Region will restart in {0}", times.ToArray(), true);
m_log.InfoFormat(
"User {0} requested restart of region {1} in {2} seconds",
remoteClient.Name, Scene.Name, times.Count != 0 ? times[0] : 0);
} }
} }
@ -295,7 +359,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
if ((estateAccessType & 4) != 0) // User add if ((estateAccessType & 4) != 0) // User add
{ {
if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true) || Scene.Permissions.BypassPermissions()) if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true))
{ {
if ((estateAccessType & 1) != 0) // All estates if ((estateAccessType & 1) != 0) // All estates
{ {
@ -325,9 +389,10 @@ namespace OpenSim.Region.CoreModules.World.Estate
} }
} }
if ((estateAccessType & 8) != 0) // User remove if ((estateAccessType & 8) != 0) // User remove
{ {
if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true) || Scene.Permissions.BypassPermissions()) if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true))
{ {
if ((estateAccessType & 1) != 0) // All estates if ((estateAccessType & 1) != 0) // All estates
{ {
@ -356,9 +421,10 @@ namespace OpenSim.Region.CoreModules.World.Estate
remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions"); remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions");
} }
} }
if ((estateAccessType & 16) != 0) // Group add if ((estateAccessType & 16) != 0) // Group add
{ {
if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true) || Scene.Permissions.BypassPermissions()) if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true))
{ {
if ((estateAccessType & 1) != 0) // All estates if ((estateAccessType & 1) != 0) // All estates
{ {
@ -387,9 +453,10 @@ namespace OpenSim.Region.CoreModules.World.Estate
remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions"); remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions");
} }
} }
if ((estateAccessType & 32) != 0) // Group remove if ((estateAccessType & 32) != 0) // Group remove
{ {
if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true) || Scene.Permissions.BypassPermissions()) if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true))
{ {
if ((estateAccessType & 1) != 0) // All estates if ((estateAccessType & 1) != 0) // All estates
{ {
@ -418,9 +485,10 @@ namespace OpenSim.Region.CoreModules.World.Estate
remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions"); remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions");
} }
} }
if ((estateAccessType & 64) != 0) // Ban add if ((estateAccessType & 64) != 0) // Ban add
{ {
if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, false) || Scene.Permissions.BypassPermissions()) if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, false))
{ {
EstateBan[] banlistcheck = Scene.RegionInfo.EstateSettings.EstateBans; EstateBan[] banlistcheck = Scene.RegionInfo.EstateSettings.EstateBans;
@ -495,9 +563,10 @@ namespace OpenSim.Region.CoreModules.World.Estate
remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions"); remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions");
} }
} }
if ((estateAccessType & 128) != 0) // Ban remove if ((estateAccessType & 128) != 0) // Ban remove
{ {
if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, false) || Scene.Permissions.BypassPermissions()) if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, false))
{ {
EstateBan[] banlistcheck = Scene.RegionInfo.EstateSettings.EstateBans; EstateBan[] banlistcheck = Scene.RegionInfo.EstateSettings.EstateBans;
@ -550,9 +619,10 @@ namespace OpenSim.Region.CoreModules.World.Estate
remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions"); remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions");
} }
} }
if ((estateAccessType & 256) != 0) // Manager add if ((estateAccessType & 256) != 0) // Manager add
{ {
if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true) || Scene.Permissions.BypassPermissions()) if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true))
{ {
if ((estateAccessType & 1) != 0) // All estates if ((estateAccessType & 1) != 0) // All estates
{ {
@ -581,9 +651,10 @@ namespace OpenSim.Region.CoreModules.World.Estate
remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions"); remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions");
} }
} }
if ((estateAccessType & 512) != 0) // Manager remove if ((estateAccessType & 512) != 0) // Manager remove
{ {
if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true) || Scene.Permissions.BypassPermissions()) if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true))
{ {
if ((estateAccessType & 1) != 0) // All estates if ((estateAccessType & 1) != 0) // All estates
{ {
@ -614,7 +685,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
} }
} }
public void handleOnEstateManageTelehub (IClientAPI client, UUID invoice, UUID senderID, string cmd, uint param1) public void handleOnEstateManageTelehub(IClientAPI client, UUID invoice, UUID senderID, string cmd, uint param1)
{ {
SceneObjectPart part; SceneObjectPart part;
@ -1072,45 +1143,6 @@ namespace OpenSim.Region.CoreModules.World.Estate
#endregion #endregion
#region Region Module interface
public string Name { get { return "EstateManagementModule"; } }
public Type ReplaceableInterface { get { return null; } }
public void Initialise(IConfigSource source) {}
public void AddRegion(Scene scene)
{
Scene = scene;
Scene.RegisterModuleInterface<IEstateModule>(this);
Scene.EventManager.OnNewClient += EventManager_OnNewClient;
Scene.EventManager.OnRequestChangeWaterHeight += changeWaterHeight;
m_Telehub = new TelehubManager(scene);
m_commands = new EstateManagementCommands(this);
m_commands.Initialise();
}
public void RemoveRegion(Scene scene) {}
public void RegionLoaded(Scene scene)
{
// Sets up the sun module based no the saved Estate and Region Settings
// DO NOT REMOVE or the sun will stop working
scene.TriggerEstateSunUpdate();
UserManager = scene.RequestModuleInterface<IUserManagement>();
}
public void Close()
{
m_commands.Close();
}
#endregion
#region Other Functions #region Other Functions
public void changeWaterHeight(float height) public void changeWaterHeight(float height)

View File

@ -1378,10 +1378,11 @@ namespace OpenSim.Region.CoreModules.World.Land
public void EventManagerOnIncomingLandDataFromStorage(List<LandData> data) public void EventManagerOnIncomingLandDataFromStorage(List<LandData> data)
{ {
// m_log.DebugFormat(
// "[LAND MANAGMENT MODULE]: Processing {0} incoming parcels on {1}", data.Count, m_scene.Name);
for (int i = 0; i < data.Count; i++) for (int i = 0; i < data.Count; i++)
{
IncomingLandObjectFromStorage(data[i]); IncomingLandObjectFromStorage(data[i]);
}
} }
public void IncomingLandObjectFromStorage(LandData data) public void IncomingLandObjectFromStorage(LandData data)

View File

@ -727,9 +727,10 @@ namespace OpenSim.Region.CoreModules.World.Land
int ty = min_y * 4; int ty = min_y * 4;
if (ty > ((int)Constants.RegionSize - 1)) if (ty > ((int)Constants.RegionSize - 1))
ty = ((int)Constants.RegionSize - 1); ty = ((int)Constants.RegionSize - 1);
LandData.AABBMin = LandData.AABBMin =
new Vector3((float) (min_x * 4), (float) (min_y * 4), new Vector3(
(float) m_scene.Heightmap[tx, ty]); (float)(min_x * 4), (float)(min_y * 4), m_scene != null ? (float)m_scene.Heightmap[tx, ty] : 0);
tx = max_x * 4; tx = max_x * 4;
if (tx > ((int)Constants.RegionSize - 1)) if (tx > ((int)Constants.RegionSize - 1))
@ -737,9 +738,11 @@ namespace OpenSim.Region.CoreModules.World.Land
ty = max_y * 4; ty = max_y * 4;
if (ty > ((int)Constants.RegionSize - 1)) if (ty > ((int)Constants.RegionSize - 1))
ty = ((int)Constants.RegionSize - 1); ty = ((int)Constants.RegionSize - 1);
LandData.AABBMax =
new Vector3((float) (max_x * 4), (float) (max_y * 4), LandData.AABBMax
(float) m_scene.Heightmap[tx, ty]); = new Vector3(
(float)(max_x * 4), (float)(max_y * 4), m_scene != null ? (float)m_scene.Heightmap[tx, ty] : 0);
LandData.Area = tempArea; LandData.Area = tempArea;
} }

View File

@ -41,7 +41,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.World.Land.Tests namespace OpenSim.Region.CoreModules.World.Land.Tests
{ {
[TestFixture] [TestFixture]
public class PrimCountModuleTests public class PrimCountModuleTests : OpenSimTestCase
{ {
protected UUID m_userId = new UUID("00000000-0000-0000-0000-100000000000"); protected UUID m_userId = new UUID("00000000-0000-0000-0000-100000000000");
protected UUID m_groupId = new UUID("00000000-0000-0000-8888-000000000000"); protected UUID m_groupId = new UUID("00000000-0000-0000-8888-000000000000");

View File

@ -44,7 +44,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.World.Media.Moap.Tests namespace OpenSim.Region.CoreModules.World.Media.Moap.Tests
{ {
[TestFixture] [TestFixture]
public class MoapTests public class MoapTests : OpenSimTestCase
{ {
protected TestScene m_scene; protected TestScene m_scene;
protected MoapModule m_module; protected MoapModule m_module;

View File

@ -26,6 +26,7 @@
*/ */
using System; using System;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Timers; using System.Timers;
using System.Threading; using System.Threading;
@ -264,7 +265,10 @@ namespace OpenSim.Region.CoreModules.World.Region
for (int i = 4 ; i < args.Length ; i++) for (int i = 4 ; i < args.Length ; i++)
times.Add(Convert.ToInt32(args[i])); times.Add(Convert.ToInt32(args[i]));
MainConsole.Instance.OutputFormat(
"Region {0} scheduled for restart in {1} seconds", m_Scene.Name, times.Sum());
ScheduleRestart(UUID.Zero, args[3], times.ToArray(), notice); ScheduleRestart(UUID.Zero, args[3], times.ToArray(), notice);
} }
} }
} }

View File

@ -39,7 +39,7 @@ using OpenSim.Tests.Common;
namespace OpenSim.Region.CoreModules.World.Serialiser.Tests namespace OpenSim.Region.CoreModules.World.Serialiser.Tests
{ {
[TestFixture] [TestFixture]
public class SerialiserTests public class SerialiserTests : OpenSimTestCase
{ {
private string xml = @" private string xml = @"
<SceneObjectGroup> <SceneObjectGroup>

View File

@ -43,8 +43,8 @@ namespace OpenSim.Region.CoreModules.World.Sound
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SoundModule")] [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SoundModule")]
public class SoundModule : INonSharedRegionModule, ISoundModule public class SoundModule : INonSharedRegionModule, ISoundModule
{ {
private static readonly ILog m_log = LogManager.GetLogger( // private static readonly ILog m_log = LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType); // MethodBase.GetCurrentMethod().DeclaringType);
private Scene m_scene; private Scene m_scene;

View File

@ -30,11 +30,12 @@ using NUnit.Framework;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes; using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
namespace OpenSim.Region.CoreModules.World.Terrain.Tests namespace OpenSim.Region.CoreModules.World.Terrain.Tests
{ {
[TestFixture] [TestFixture]
public class TerrainTest public class TerrainTest : OpenSimTestCase
{ {
[Test] [Test]
public void BrushTest() public void BrushTest()

View File

@ -66,7 +66,7 @@ namespace OpenSim.Region.CoreModules
public void Initialise(IConfigSource config) public void Initialise(IConfigSource config)
{ {
m_windConfig = config.Configs["Wind"]; m_windConfig = config.Configs["Wind"];
string desiredWindPlugin = m_dWindPluginName; // string desiredWindPlugin = m_dWindPluginName;
if (m_windConfig != null) if (m_windConfig != null)
{ {

View File

@ -740,7 +740,12 @@ namespace OpenSim.Region.Framework.Scenes
// //
// Out of memory // Out of memory
// Operating system has killed the plugin // Operating system has killed the plugin
m_sceneGraph.UnRecoverableError += RestartNow; m_sceneGraph.UnRecoverableError
+= () =>
{
m_log.ErrorFormat("[SCENE]: Restarting region {0} due to unrecoverable physics crash", Name);
RestartNow();
};
RegisterDefaultSceneEvents(); RegisterDefaultSceneEvents();
@ -1134,16 +1139,10 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
m_log.Error("[REGION]: Closing"); m_log.InfoFormat("[REGION]: Restarting region {0}", Name);
Close(); Close();
if (PhysicsScene != null)
{
PhysicsScene.Dispose();
}
m_log.Error("[REGION]: Firing Region Restart Message");
base.Restart(); base.Restart();
} }

View File

@ -100,23 +100,25 @@ namespace OpenSim.Region.Framework.Scenes
} }
private readonly List<Scene> m_localScenes = new List<Scene>(); private readonly List<Scene> m_localScenes = new List<Scene>();
private Scene m_currentScene = null;
public List<Scene> Scenes public List<Scene> Scenes
{ {
get { return new List<Scene>(m_localScenes); } get { return new List<Scene>(m_localScenes); }
} }
public Scene CurrentScene /// <summary>
{ /// Scene selected from the console.
get { return m_currentScene; } /// </summary>
} /// <value>
/// If null, then all scenes are considered selected (signalled as "Root" on the console).
/// </value>
public Scene CurrentScene { get; private set; }
public Scene CurrentOrFirstScene public Scene CurrentOrFirstScene
{ {
get get
{ {
if (m_currentScene == null) if (CurrentScene == null)
{ {
lock (m_localScenes) lock (m_localScenes)
{ {
@ -128,7 +130,7 @@ namespace OpenSim.Region.Framework.Scenes
} }
else else
{ {
return m_currentScene; return CurrentScene;
} }
} }
} }
@ -141,6 +143,13 @@ namespace OpenSim.Region.Framework.Scenes
public void Close() public void Close()
{ {
lock (m_localScenes)
{
for (int i = 0; i < m_localScenes.Count; i++)
{
m_localScenes[i].Close();
}
}
} }
public void Close(Scene cscene) public void Close(Scene cscene)
@ -171,8 +180,7 @@ namespace OpenSim.Region.Framework.Scenes
public void HandleRestart(RegionInfo rdata) public void HandleRestart(RegionInfo rdata)
{ {
m_log.Error("[SCENEMANAGER]: Got Restart message for region:" + rdata.RegionName + " Sending up to main"); Scene restartedScene = null;
int RegionSceneElement = -1;
lock (m_localScenes) lock (m_localScenes)
{ {
@ -180,19 +188,18 @@ namespace OpenSim.Region.Framework.Scenes
{ {
if (rdata.RegionName == m_localScenes[i].RegionInfo.RegionName) if (rdata.RegionName == m_localScenes[i].RegionInfo.RegionName)
{ {
RegionSceneElement = i; restartedScene = m_localScenes[i];
m_localScenes.RemoveAt(i);
break;
} }
} }
// Now we make sure the region is no longer known about by the SceneManager
// Prevents duplicates.
if (RegionSceneElement >= 0)
{
m_localScenes.RemoveAt(RegionSceneElement);
}
} }
// If the currently selected scene has been restarted, then we can't reselect here since we the scene
// hasn't yet been recreated. We will have to leave this to the caller.
if (CurrentScene == restartedScene)
CurrentScene = null;
// Send signal to main that we're restarting this sim. // Send signal to main that we're restarting this sim.
OnRestartSim(rdata); OnRestartSim(rdata);
} }
@ -334,14 +341,14 @@ namespace OpenSim.Region.Framework.Scenes
private void ForEachCurrentScene(Action<Scene> func) private void ForEachCurrentScene(Action<Scene> func)
{ {
if (m_currentScene == null) if (CurrentScene == null)
{ {
lock (m_localScenes) lock (m_localScenes)
m_localScenes.ForEach(func); m_localScenes.ForEach(func);
} }
else else
{ {
func(m_currentScene); func(CurrentScene);
} }
} }
@ -361,7 +368,7 @@ namespace OpenSim.Region.Framework.Scenes
|| (String.Compare(regionName, "..") == 0) || (String.Compare(regionName, "..") == 0)
|| (String.Compare(regionName, "/") == 0)) || (String.Compare(regionName, "/") == 0))
{ {
m_currentScene = null; CurrentScene = null;
return true; return true;
} }
else else
@ -372,7 +379,7 @@ namespace OpenSim.Region.Framework.Scenes
{ {
if (String.Compare(scene.RegionInfo.RegionName, regionName, true) == 0) if (String.Compare(scene.RegionInfo.RegionName, regionName, true) == 0)
{ {
m_currentScene = scene; CurrentScene = scene;
return true; return true;
} }
} }
@ -392,7 +399,7 @@ namespace OpenSim.Region.Framework.Scenes
{ {
if (scene.RegionInfo.RegionID == regionID) if (scene.RegionInfo.RegionID == regionID)
{ {
m_currentScene = scene; CurrentScene = scene;
return true; return true;
} }
} }

View File

@ -1698,8 +1698,16 @@ namespace OpenSim.Region.Framework.Scenes
// "[SCENE PRESENCE]: Avatar {0} received request to move to position {1} in {2}", // "[SCENE PRESENCE]: Avatar {0} received request to move to position {1} in {2}",
// Name, pos, m_scene.RegionInfo.RegionName); // Name, pos, m_scene.RegionInfo.RegionName);
if (pos.X < 0 || pos.X >= Constants.RegionSize // Allow move to another sub-region within a megaregion
|| pos.Y < 0 || pos.Y >= Constants.RegionSize Vector2 regionSize;
IRegionCombinerModule regionCombinerModule = m_scene.RequestModuleInterface<IRegionCombinerModule>();
if (regionCombinerModule != null)
regionSize = regionCombinerModule.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID);
else
regionSize = new Vector2(Constants.RegionSize);
if (pos.X < 0 || pos.X >= regionSize.X
|| pos.Y < 0 || pos.Y >= regionSize.Y
|| pos.Z < 0) || pos.Z < 0)
return; return;
@ -1713,7 +1721,16 @@ namespace OpenSim.Region.Framework.Scenes
// pos.Z = AbsolutePosition.Z; // pos.Z = AbsolutePosition.Z;
// } // }
float terrainHeight = (float)m_scene.Heightmap[(int)pos.X, (int)pos.Y]; // Get terrain height for sub-region in a megaregion if necessary
int X = (int)((m_scene.RegionInfo.RegionLocX * Constants.RegionSize) + pos.X);
int Y = (int)((m_scene.RegionInfo.RegionLocY * Constants.RegionSize) + pos.Y);
UUID target_regionID = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, X, Y).RegionID;
Scene targetScene = m_scene;
if (!SceneManager.Instance.TryGetScene(target_regionID, out targetScene))
targetScene = m_scene;
float terrainHeight = (float)targetScene.Heightmap[(int)(pos.X % Constants.RegionSize), (int)(pos.Y % Constants.RegionSize)];
pos.Z = Math.Max(terrainHeight, pos.Z); pos.Z = Math.Max(terrainHeight, pos.Z);
// Fudge factor. It appears that if one clicks "go here" on a piece of ground, the go here request is // Fudge factor. It appears that if one clicks "go here" on a piece of ground, the go here request is

View File

@ -37,7 +37,7 @@ using OpenSim.Tests.Common;
namespace OpenSim.Region.Framework.Scenes.Tests namespace OpenSim.Region.Framework.Scenes.Tests
{ {
[TestFixture] [TestFixture]
public class BorderTests public class BorderTests : OpenSimTestCase
{ {
[Test] [Test]
public void TestCross() public void TestCross()

View File

@ -41,7 +41,7 @@ using OpenSim.Tests.Common;
namespace OpenSim.Region.Framework.Scenes.Tests namespace OpenSim.Region.Framework.Scenes.Tests
{ {
[TestFixture, LongRunning] [TestFixture, LongRunning]
public class EntityManagerTests public class EntityManagerTests : OpenSimTestCase
{ {
static public Random random; static public Random random;
SceneObjectGroup found; SceneObjectGroup found;

View File

@ -40,7 +40,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests namespace OpenSim.Region.Framework.Scenes.Tests
{ {
[TestFixture] [TestFixture]
public class SceneGraphTests public class SceneGraphTests : OpenSimTestCase
{ {
[Test] [Test]
public void TestDuplicateObject() public void TestDuplicateObject()

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using NUnit.Framework;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Communications;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests
{
[TestFixture]
public class SceneManagerTests : OpenSimTestCase
{
[Test]
public void TestClose()
{
TestHelpers.InMethod();
SceneHelpers sh = new SceneHelpers();
Scene scene = sh.SetupScene();
sh.SceneManager.Close();
Assert.That(scene.ShuttingDown, Is.True);
}
}
}

View File

@ -29,6 +29,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using Nini.Config;
using NUnit.Framework; using NUnit.Framework;
using OpenMetaverse; using OpenMetaverse;
using OpenSim.Framework; using OpenSim.Framework;
@ -182,6 +183,10 @@ namespace OpenSim.Region.Framework.Scenes.Tests
/// <summary> /// <summary>
/// Test deleting an object from a scene. /// Test deleting an object from a scene.
/// </summary> /// </summary>
/// <remarks>
/// This is the most basic form of delete. For all more sophisticated forms of derez (done asynchrnously
/// and where object can be taken to user inventory, etc.), see SceneObjectDeRezTests.
/// </remarks>
[Test] [Test]
public void TestDeleteSceneObject() public void TestDeleteSceneObject()
{ {
@ -200,100 +205,6 @@ namespace OpenSim.Region.Framework.Scenes.Tests
Assert.That(retrievedPart, Is.Null); Assert.That(retrievedPart, Is.Null);
} }
/// <summary>
/// Test deleting an object asynchronously
/// </summary>
[Test]
public void TestDeleteSceneObjectAsync()
{
TestHelpers.InMethod();
//log4net.Config.XmlConfigurator.Configure();
UUID agentId = UUID.Parse("00000000-0000-0000-0000-000000000001");
TestScene scene = new SceneHelpers().SetupScene();
// Turn off the timer on the async sog deleter - we'll crank it by hand for this test.
AsyncSceneObjectGroupDeleter sogd = scene.SceneObjectGroupDeleter;
sogd.Enabled = false;
SceneObjectGroup so = SceneHelpers.AddSceneObject(scene);
IClientAPI client = SceneHelpers.AddScenePresence(scene, agentId).ControllingClient;
scene.DeRezObjects(client, new System.Collections.Generic.List<uint>() { so.LocalId }, UUID.Zero, DeRezAction.Delete, UUID.Zero);
SceneObjectPart retrievedPart = scene.GetSceneObjectPart(so.LocalId);
Assert.That(retrievedPart, Is.Not.Null);
Assert.That(so.IsDeleted, Is.False);
sogd.InventoryDeQueueAndDelete();
Assert.That(so.IsDeleted, Is.True);
SceneObjectPart retrievedPart2 = scene.GetSceneObjectPart(so.LocalId);
Assert.That(retrievedPart2, Is.Null);
}
/// <summary>
/// Test deleting an object asynchronously to user inventory.
/// </summary>
// [Test]
public void TestDeleteSceneObjectAsyncToUserInventory()
{
TestHelpers.InMethod();
TestHelpers.EnableLogging();
UUID agentId = UUID.Parse("00000000-0000-0000-0000-000000000001");
string myObjectName = "Fred";
TestScene scene = new SceneHelpers().SetupScene();
// Turn off the timer on the async sog deleter - we'll crank it by hand for this test.
AsyncSceneObjectGroupDeleter sogd = scene.SceneObjectGroupDeleter;
sogd.Enabled = false;
SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, myObjectName, agentId);
// Assert.That(
// scene.CommsManager.UserAdminService.AddUser(
// "Bob", "Hoskins", "test", "test@test.com", 1000, 1000, agentId),
// Is.EqualTo(agentId));
UserAccount ua = UserAccountHelpers.CreateUserWithInventory(scene, agentId);
InventoryFolderBase folder1
= UserInventoryHelpers.CreateInventoryFolder(scene.InventoryService, ua.PrincipalID, "folder1");
IClientAPI client = SceneHelpers.AddScenePresence(scene, agentId).ControllingClient;
scene.DeRezObjects(client, new List<uint>() { so.LocalId }, UUID.Zero, DeRezAction.Take, folder1.ID);
SceneObjectPart retrievedPart = scene.GetSceneObjectPart(so.LocalId);
Assert.That(retrievedPart, Is.Not.Null);
Assert.That(so.IsDeleted, Is.False);
sogd.InventoryDeQueueAndDelete();
Assert.That(so.IsDeleted, Is.True);
SceneObjectPart retrievedPart2 = scene.GetSceneObjectPart(so.LocalId);
Assert.That(retrievedPart2, Is.Null);
// SceneSetupHelpers.DeleteSceneObjectAsync(scene, part, DeRezAction.Take, userInfo.RootFolder.ID, client);
InventoryItemBase retrievedItem
= UserInventoryHelpers.GetInventoryItem(
scene.InventoryService, ua.PrincipalID, "folder1/" + myObjectName);
// Check that we now have the taken part in our inventory
Assert.That(retrievedItem, Is.Not.Null);
// Check that the taken part has actually disappeared
// SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId);
// Assert.That(retrievedPart, Is.Null);
}
/// <summary> /// <summary>
/// Changing a scene object uuid changes the root part uuid. This is a valid operation if the object is not /// Changing a scene object uuid changes the root part uuid. This is a valid operation if the object is not
/// in a scene and is useful if one wants to supply a UUID directly rather than use the one generated by /// in a scene and is useful if one wants to supply a UUID directly rather than use the one generated by
@ -329,4 +240,4 @@ namespace OpenSim.Region.Framework.Scenes.Tests
Assert.That(sog.Parts.Length, Is.EqualTo(2)); Assert.That(sog.Parts.Length, Is.EqualTo(2));
} }
} }
} }

View File

@ -33,22 +33,24 @@ using NUnit.Framework;
using OpenMetaverse; using OpenMetaverse;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Framework.Communications; using OpenSim.Framework.Communications;
using OpenSim.Region.CoreModules.Framework.InventoryAccess;
using OpenSim.Region.CoreModules.World.Permissions; using OpenSim.Region.CoreModules.World.Permissions;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common; using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock; using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests namespace OpenSim.Region.Framework.Scenes.Tests
{ {
/// <summary> /// <summary>
/// Tests derez of scene objects by users. /// Tests derez of scene objects.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This is at a level above the SceneObjectBasicTests, which act on the scene directly. /// This is at a level above the SceneObjectBasicTests, which act on the scene directly.
/// TODO: These tests are very incomplete - they only test for a few conditions. /// TODO: These tests are incomplete - need to test more kinds of derez (e.g. return object).
/// </remarks> /// </remarks>
[TestFixture] [TestFixture]
public class SceneObjectDeRezTests public class SceneObjectDeRezTests : OpenSimTestCase
{ {
/// <summary> /// <summary>
/// Test deleting an object from a scene. /// Test deleting an object from a scene.
@ -76,14 +78,20 @@ namespace OpenSim.Region.Framework.Scenes.Tests
= new SceneObjectPart(userId, PrimitiveBaseShape.Default, Vector3.Zero, Quaternion.Identity, Vector3.Zero); = new SceneObjectPart(userId, PrimitiveBaseShape.Default, Vector3.Zero, Quaternion.Identity, Vector3.Zero);
part.Name = "obj1"; part.Name = "obj1";
scene.AddNewSceneObject(new SceneObjectGroup(part), false); scene.AddNewSceneObject(new SceneObjectGroup(part), false);
List<uint> localIds = new List<uint>(); List<uint> localIds = new List<uint>();
localIds.Add(part.LocalId); localIds.Add(part.LocalId);
scene.DeRezObjects(client, localIds, UUID.Zero, DeRezAction.Delete, UUID.Zero); scene.DeRezObjects(client, localIds, UUID.Zero, DeRezAction.Delete, UUID.Zero);
// Check that object isn't deleted until we crank the sogd handle.
SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId);
Assert.That(retrievedPart, Is.Not.Null);
Assert.That(retrievedPart.ParentGroup.IsDeleted, Is.False);
sogd.InventoryDeQueueAndDelete(); sogd.InventoryDeQueueAndDelete();
SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId); SceneObjectPart retrievedPart2 = scene.GetSceneObjectPart(part.LocalId);
Assert.That(retrievedPart, Is.Null); Assert.That(retrievedPart2, Is.Null);
} }
/// <summary> /// <summary>
@ -124,6 +132,67 @@ namespace OpenSim.Region.Framework.Scenes.Tests
// Object should still be in the scene. // Object should still be in the scene.
SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId); SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId);
Assert.That(retrievedPart.UUID, Is.EqualTo(part.UUID)); Assert.That(retrievedPart.UUID, Is.EqualTo(part.UUID));
} }
/// <summary>
/// Test deleting an object asynchronously to user inventory.
/// </summary>
[Test]
public void TestDeleteSceneObjectAsyncToUserInventory()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
UUID agentId = UUID.Parse("00000000-0000-0000-0000-000000000001");
string myObjectName = "Fred";
TestScene scene = new SceneHelpers().SetupScene();
IConfigSource configSource = new IniConfigSource();
IConfig config = configSource.AddConfig("Modules");
config.Set("InventoryAccessModule", "BasicInventoryAccessModule");
SceneHelpers.SetupSceneModules(
scene, configSource, new object[] { new BasicInventoryAccessModule() });
SceneHelpers.SetupSceneModules(scene, new object[] { });
// Turn off the timer on the async sog deleter - we'll crank it by hand for this test.
AsyncSceneObjectGroupDeleter sogd = scene.SceneObjectGroupDeleter;
sogd.Enabled = false;
SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, myObjectName, agentId);
UserAccount ua = UserAccountHelpers.CreateUserWithInventory(scene, agentId);
InventoryFolderBase folder1
= UserInventoryHelpers.CreateInventoryFolder(scene.InventoryService, ua.PrincipalID, "folder1");
IClientAPI client = SceneHelpers.AddScenePresence(scene, agentId).ControllingClient;
scene.DeRezObjects(client, new List<uint>() { so.LocalId }, UUID.Zero, DeRezAction.Take, folder1.ID);
SceneObjectPart retrievedPart = scene.GetSceneObjectPart(so.LocalId);
Assert.That(retrievedPart, Is.Not.Null);
Assert.That(so.IsDeleted, Is.False);
sogd.InventoryDeQueueAndDelete();
Assert.That(so.IsDeleted, Is.True);
SceneObjectPart retrievedPart2 = scene.GetSceneObjectPart(so.LocalId);
Assert.That(retrievedPart2, Is.Null);
// SceneSetupHelpers.DeleteSceneObjectAsync(scene, part, DeRezAction.Take, userInfo.RootFolder.ID, client);
InventoryItemBase retrievedItem
= UserInventoryHelpers.GetInventoryItem(
scene.InventoryService, ua.PrincipalID, "folder1/" + myObjectName);
// Check that we now have the taken part in our inventory
Assert.That(retrievedItem, Is.Not.Null);
// Check that the taken part has actually disappeared
// SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId);
// Assert.That(retrievedPart, Is.Null);
}
} }
} }

View File

@ -40,7 +40,7 @@ using log4net;
namespace OpenSim.Region.Framework.Scenes.Tests namespace OpenSim.Region.Framework.Scenes.Tests
{ {
[TestFixture] [TestFixture]
public class SceneObjectLinkingTests public class SceneObjectLinkingTests : OpenSimTestCase
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

View File

@ -41,7 +41,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
/// Basic scene object resize tests /// Basic scene object resize tests
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public class SceneObjectResizeTests public class SceneObjectResizeTests : OpenSimTestCase
{ {
/// <summary> /// <summary>
/// Test resizing an object /// Test resizing an object

View File

@ -40,7 +40,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests namespace OpenSim.Region.Framework.Scenes.Tests
{ {
[TestFixture] [TestFixture]
public class SceneObjectScriptTests public class SceneObjectScriptTests : OpenSimTestCase
{ {
[Test] [Test]
public void TestAddScript() public void TestAddScript()

View File

@ -42,14 +42,16 @@ namespace OpenSim.Region.Framework.Scenes.Tests
/// Spatial scene object tests (will eventually cover root and child part position, rotation properties, etc.) /// Spatial scene object tests (will eventually cover root and child part position, rotation properties, etc.)
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public class SceneObjectSpatialTests public class SceneObjectSpatialTests : OpenSimTestCase
{ {
TestScene m_scene; TestScene m_scene;
UUID m_ownerId = TestHelpers.ParseTail(0x1); UUID m_ownerId = TestHelpers.ParseTail(0x1);
[SetUp] [SetUp]
public void SetUp() public override void SetUp()
{ {
base.SetUp();
m_scene = new SceneHelpers().SetupScene(); m_scene = new SceneHelpers().SetupScene();
} }

View File

@ -42,7 +42,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
/// Basic scene object status tests /// Basic scene object status tests
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public class SceneObjectStatusTests public class SceneObjectStatusTests : OpenSimTestCase
{ {
private TestScene m_scene; private TestScene m_scene;
private UUID m_ownerId = TestHelpers.ParseTail(0x1); private UUID m_ownerId = TestHelpers.ParseTail(0x1);

View File

@ -51,7 +51,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
/// Scene presence animation tests /// Scene presence animation tests
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public class ScenePresenceAnimationTests public class ScenePresenceAnimationTests : OpenSimTestCase
{ {
[Test] [Test]
public void TestFlyingAnimation() public void TestFlyingAnimation()

View File

@ -42,7 +42,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests namespace OpenSim.Region.Framework.Scenes.Tests
{ {
[TestFixture] [TestFixture]
public class ScenePresenceAutopilotTests public class ScenePresenceAutopilotTests : OpenSimTestCase
{ {
private TestScene m_scene; private TestScene m_scene;

View File

@ -43,7 +43,7 @@ using System.Threading;
namespace OpenSim.Region.Framework.Scenes.Tests namespace OpenSim.Region.Framework.Scenes.Tests
{ {
[TestFixture] [TestFixture]
public class ScenePresenceSitTests public class ScenePresenceSitTests : OpenSimTestCase
{ {
private TestScene m_scene; private TestScene m_scene;
private ScenePresence m_sp; private ScenePresence m_sp;

View File

@ -49,7 +49,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
/// Teleport tests in a standalone OpenSim /// Teleport tests in a standalone OpenSim
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public class ScenePresenceTeleportTests public class ScenePresenceTeleportTests : OpenSimTestCase
{ {
[TestFixtureSetUp] [TestFixtureSetUp]
public void FixtureInit() public void FixtureInit()

View File

@ -50,7 +50,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
/// Scene presence tests /// Scene presence tests
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public class SceneTests public class SceneTests : OpenSimTestCase
{ {
/// <summary> /// <summary>
/// Very basic scene update test. Should become more elaborate with time. /// Very basic scene update test. Should become more elaborate with time.

View File

@ -50,7 +50,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Tests namespace OpenSim.Region.Framework.Tests
{ {
[TestFixture] [TestFixture]
public class TaskInventoryTests public class TaskInventoryTests : OpenSimTestCase
{ {
[Test] [Test]
public void TestAddTaskInventoryItem() public void TestAddTaskInventoryItem()

View File

@ -38,7 +38,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests namespace OpenSim.Region.Framework.Scenes.Tests
{ {
[TestFixture] [TestFixture]
public class UuidGathererTests public class UuidGathererTests : OpenSimTestCase
{ {
protected IAssetService m_assetService; protected IAssetService m_assetService;
protected UuidGatherer m_uuidGatherer; protected UuidGatherer m_uuidGatherer;

View File

@ -36,7 +36,22 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
{ {
UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID); UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID);
void UpdateGroup(UUID RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish); void UpdateGroup(UUID RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish);
/// <summary>
/// Get the group record.
/// </summary>
/// <returns></returns>
/// <param name='RequestingAgentID'>The UUID of the user making the request.</param>
/// <param name='GroupID'>
/// The ID of the record to retrieve.
/// GroupName may be specified instead, in which case this parameter will be UUID.Zero
/// </param>
/// <param name='GroupName'>
/// The name of the group to retrieve.
/// GroupID may be specified instead, in which case this parmeter will be null.
/// </param>
GroupRecord GetGroupRecord(UUID RequestingAgentID, UUID GroupID, string GroupName); GroupRecord GetGroupRecord(UUID RequestingAgentID, UUID GroupID, string GroupName);
List<DirGroupsReplyData> FindGroups(UUID RequestingAgentID, string search); List<DirGroupsReplyData> FindGroups(UUID RequestingAgentID, string search);
List<GroupMembersData> GetGroupMembers(UUID RequestingAgentID, UUID GroupID); List<GroupMembersData> GetGroupMembers(UUID RequestingAgentID, UUID GroupID);

View File

@ -42,7 +42,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups.Tests
/// Basic groups module tests /// Basic groups module tests
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public class GroupsModuleTests public class GroupsModuleTests : OpenSimTestCase
{ {
[Test] [Test]
public void TestBasic() public void TestBasic()

View File

@ -54,13 +54,62 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
private bool m_debugEnabled = false; private bool m_debugEnabled = false;
public const GroupPowers m_DefaultEveryonePowers = GroupPowers.AllowSetHome | public const GroupPowers DefaultEveryonePowers
GroupPowers.Accountable | = GroupPowers.AllowSetHome
GroupPowers.JoinChat | | GroupPowers.Accountable
GroupPowers.AllowVoiceChat | | GroupPowers.JoinChat
GroupPowers.ReceiveNotices | | GroupPowers.AllowVoiceChat
GroupPowers.StartProposal | | GroupPowers.ReceiveNotices
GroupPowers.VoteOnProposal; | GroupPowers.StartProposal
| GroupPowers.VoteOnProposal;
// Would this be cleaner as (GroupPowers)ulong.MaxValue?
public const GroupPowers DefaultOwnerPowers
= GroupPowers.Accountable
| GroupPowers.AllowEditLand
| GroupPowers.AllowFly
| GroupPowers.AllowLandmark
| GroupPowers.AllowRez
| GroupPowers.AllowSetHome
| GroupPowers.AllowVoiceChat
| GroupPowers.AssignMember
| GroupPowers.AssignMemberLimited
| GroupPowers.ChangeActions
| GroupPowers.ChangeIdentity
| GroupPowers.ChangeMedia
| GroupPowers.ChangeOptions
| GroupPowers.CreateRole
| GroupPowers.DeedObject
| GroupPowers.DeleteRole
| GroupPowers.Eject
| GroupPowers.FindPlaces
| GroupPowers.Invite
| GroupPowers.JoinChat
| GroupPowers.LandChangeIdentity
| GroupPowers.LandDeed
| GroupPowers.LandDivideJoin
| GroupPowers.LandEdit
| GroupPowers.LandEjectAndFreeze
| GroupPowers.LandGardening
| GroupPowers.LandManageAllowed
| GroupPowers.LandManageBanned
| GroupPowers.LandManagePasses
| GroupPowers.LandOptions
| GroupPowers.LandRelease
| GroupPowers.LandSetSale
| GroupPowers.ModerateChat
| GroupPowers.ObjectManipulate
| GroupPowers.ObjectSetForSale
| GroupPowers.ReceiveNotices
| GroupPowers.RemoveMember
| GroupPowers.ReturnGroupOwned
| GroupPowers.ReturnGroupSet
| GroupPowers.ReturnNonGroup
| GroupPowers.RoleProperties
| GroupPowers.SendNotices
| GroupPowers.SetLandingPoint
| GroupPowers.StartProposal
| GroupPowers.VoteOnProposal;
private bool m_connectorEnabled = false; private bool m_connectorEnabled = false;
@ -219,59 +268,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
param["AllowPublish"] = allowPublish == true ? 1 : 0; param["AllowPublish"] = allowPublish == true ? 1 : 0;
param["MaturePublish"] = maturePublish == true ? 1 : 0; param["MaturePublish"] = maturePublish == true ? 1 : 0;
param["FounderID"] = founderID.ToString(); param["FounderID"] = founderID.ToString();
param["EveryonePowers"] = ((ulong)m_DefaultEveryonePowers).ToString(); param["EveryonePowers"] = ((ulong)DefaultEveryonePowers).ToString();
param["OwnerRoleID"] = OwnerRoleID.ToString(); param["OwnerRoleID"] = OwnerRoleID.ToString();
param["OwnersPowers"] = ((ulong)DefaultOwnerPowers).ToString();
// Would this be cleaner as (GroupPowers)ulong.MaxValue;
GroupPowers OwnerPowers = GroupPowers.Accountable
| GroupPowers.AllowEditLand
| GroupPowers.AllowFly
| GroupPowers.AllowLandmark
| GroupPowers.AllowRez
| GroupPowers.AllowSetHome
| GroupPowers.AllowVoiceChat
| GroupPowers.AssignMember
| GroupPowers.AssignMemberLimited
| GroupPowers.ChangeActions
| GroupPowers.ChangeIdentity
| GroupPowers.ChangeMedia
| GroupPowers.ChangeOptions
| GroupPowers.CreateRole
| GroupPowers.DeedObject
| GroupPowers.DeleteRole
| GroupPowers.Eject
| GroupPowers.FindPlaces
| GroupPowers.Invite
| GroupPowers.JoinChat
| GroupPowers.LandChangeIdentity
| GroupPowers.LandDeed
| GroupPowers.LandDivideJoin
| GroupPowers.LandEdit
| GroupPowers.LandEjectAndFreeze
| GroupPowers.LandGardening
| GroupPowers.LandManageAllowed
| GroupPowers.LandManageBanned
| GroupPowers.LandManagePasses
| GroupPowers.LandOptions
| GroupPowers.LandRelease
| GroupPowers.LandSetSale
| GroupPowers.ModerateChat
| GroupPowers.ObjectManipulate
| GroupPowers.ObjectSetForSale
| GroupPowers.ReceiveNotices
| GroupPowers.RemoveMember
| GroupPowers.ReturnGroupOwned
| GroupPowers.ReturnGroupSet
| GroupPowers.ReturnNonGroup
| GroupPowers.RoleProperties
| GroupPowers.SendNotices
| GroupPowers.SetLandingPoint
| GroupPowers.StartProposal
| GroupPowers.VoteOnProposal;
param["OwnersPowers"] = ((ulong)OwnerPowers).ToString();
Hashtable respData = XmlRpcCall(requestingAgentID, "groups.createGroup", param); Hashtable respData = XmlRpcCall(requestingAgentID, "groups.createGroup", param);
@ -612,8 +611,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
} }
return Roles; return Roles;
} }
public List<GroupRolesData> GetGroupRoles(UUID requestingAgentID, UUID GroupID) public List<GroupRolesData> GetGroupRoles(UUID requestingAgentID, UUID GroupID)
@ -676,7 +673,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
} }
return members; return members;
} }
public List<GroupRoleMembersData> GetGroupRoleMembers(UUID requestingAgentID, UUID GroupID) public List<GroupRoleMembersData> GetGroupRoleMembers(UUID requestingAgentID, UUID GroupID)
@ -727,9 +723,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
values.Add(data); values.Add(data);
} }
} }
return values;
return values;
} }
public GroupNoticeInfo GetGroupNotice(UUID requestingAgentID, UUID noticeID) public GroupNoticeInfo GetGroupNotice(UUID requestingAgentID, UUID noticeID)
{ {
Hashtable param = new Hashtable(); Hashtable param = new Hashtable();
@ -737,7 +734,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getGroupNotice", param); Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getGroupNotice", param);
if (respData.Contains("error")) if (respData.Contains("error"))
{ {
return null; return null;
@ -761,6 +757,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
return data; return data;
} }
public void AddGroupNotice(UUID requestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket) public void AddGroupNotice(UUID requestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket)
{ {
string binBucket = OpenMetaverse.Utils.BytesToHexString(binaryBucket, ""); string binBucket = OpenMetaverse.Utils.BytesToHexString(binaryBucket, "");
@ -777,8 +774,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
XmlRpcCall(requestingAgentID, "groups.addGroupNotice", param); XmlRpcCall(requestingAgentID, "groups.addGroupNotice", param);
} }
#endregion #endregion
#region GroupSessionTracking #region GroupSessionTracking

View File

@ -48,7 +48,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.OptionalModules.World.NPC.Tests namespace OpenSim.Region.OptionalModules.World.NPC.Tests
{ {
[TestFixture] [TestFixture]
public class NPCModuleTests public class NPCModuleTests : OpenSimTestCase
{ {
private TestScene m_scene; private TestScene m_scene;
private AvatarFactoryModule m_afMod; private AvatarFactoryModule m_afMod;

View File

@ -78,11 +78,17 @@ public sealed class BSCharacter : BSPhysObject
private float _PIDHoverTao; private float _PIDHoverTao;
public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying) public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying)
: base(parent_scene, localID, avName, "BSCharacter")
{ {
base.BaseInitialize(parent_scene, localID, avName, "BSCharacter");
_physicsActorType = (int)ActorTypes.Agent; _physicsActorType = (int)ActorTypes.Agent;
_position = pos; _position = pos;
// Old versions of ScenePresence passed only the height. If width and/or depth are zero,
// replace with the default values.
_size = size; _size = size;
if (_size.X == 0f) _size.X = PhysicsScene.Params.avatarCapsuleDepth;
if (_size.Y == 0f) _size.Y = PhysicsScene.Params.avatarCapsuleWidth;
_flying = isFlying; _flying = isFlying;
_orientation = OMV.Quaternion.Identity; _orientation = OMV.Quaternion.Identity;
_velocity = OMV.Vector3.Zero; _velocity = OMV.Vector3.Zero;
@ -131,6 +137,10 @@ public sealed class BSCharacter : BSPhysObject
// Set the velocity and compute the proper friction // Set the velocity and compute the proper friction
ForceVelocity = _velocity; ForceVelocity = _velocity;
// This will enable or disable the flying buoyancy of the avatar.
// Needs to be reset especially when an avatar is recreated after crossing a region boundry.
Flying = _flying;
BulletSimAPI.SetRestitution2(PhysBody.ptr, PhysicsScene.Params.avatarRestitution); BulletSimAPI.SetRestitution2(PhysBody.ptr, PhysicsScene.Params.avatarRestitution);
BulletSimAPI.SetMargin2(PhysShape.ptr, PhysicsScene.Params.collisionMargin); BulletSimAPI.SetMargin2(PhysShape.ptr, PhysicsScene.Params.collisionMargin);
BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale); BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
@ -171,8 +181,7 @@ public sealed class BSCharacter : BSPhysObject
get get
{ {
// Avatar capsule size is kept in the scale parameter. // Avatar capsule size is kept in the scale parameter.
// return _size; return _size;
return new OMV.Vector3(Scale.X * 2f, Scale.Y * 2f, Scale.Z);
} }
set { set {
@ -180,8 +189,8 @@ public sealed class BSCharacter : BSPhysObject
_size = value; _size = value;
ComputeAvatarScale(_size); ComputeAvatarScale(_size);
ComputeAvatarVolumeAndMass(); ComputeAvatarVolumeAndMass();
DetailLog("{0},BSCharacter.setSize,call,scale={1},density={2},volume={3},mass={4}", DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
LocalID, Scale, _avatarDensity, _avatarVolume, RawMass); LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
{ {
@ -199,9 +208,9 @@ public sealed class BSCharacter : BSPhysObject
set { BaseShape = value; } set { BaseShape = value; }
} }
// I want the physics engine to make an avatar capsule // I want the physics engine to make an avatar capsule
public override ShapeData.PhysicsShapeType PreferredPhysicalShape public override BSPhysicsShapeType PreferredPhysicalShape
{ {
get {return ShapeData.PhysicsShapeType.SHAPE_AVATAR; } get {return BSPhysicsShapeType.SHAPE_CAPSULE; }
} }
public override bool Grabbed { public override bool Grabbed {
@ -610,13 +619,19 @@ public sealed class BSCharacter : BSPhysObject
// The 'size' given by the simulator is the mid-point of the avatar // The 'size' given by the simulator is the mid-point of the avatar
// and X and Y are unspecified. // and X and Y are unspecified.
OMV.Vector3 newScale = OMV.Vector3.Zero; OMV.Vector3 newScale = size;
newScale.X = PhysicsScene.Params.avatarCapsuleRadius; // newScale.X = PhysicsScene.Params.avatarCapsuleWidth;
newScale.Y = PhysicsScene.Params.avatarCapsuleRadius; // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth;
// From the total height, remove the capsule half spheres that are at each end // From the total height, remove the capsule half spheres that are at each end
newScale.Z = size.Z- (newScale.X + newScale.Y); // The 1.15f came from ODE. Not sure what this factors in.
Scale = newScale; // newScale.Z = (size.Z * 1.15f) - (newScale.X + newScale.Y);
// The total scale height is the central cylindar plus the caps on the two ends.
newScale.Z = size.Z + (Math.Min(size.X, size.Y) * 2f);
// Convert diameters to radii and height to half height -- the way Bullet expects it.
Scale = newScale / 2f;
} }
// set _avatarVolume and _mass based on capsule size, _density and Scale // set _avatarVolume and _mass based on capsule size, _density and Scale

View File

@ -80,10 +80,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
private Quaternion m_referenceFrame = Quaternion.Identity; private Quaternion m_referenceFrame = Quaternion.Identity;
// Linear properties // Linear properties
private BSVMotor m_linearMotor = new BSVMotor("LinearMotor");
private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time
private Vector3 m_linearMotorOffset = Vector3.Zero; // the point of force can be offset from the center private Vector3 m_linearMotorOffset = Vector3.Zero; // the point of force can be offset from the center
private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL
private Vector3 m_newVelocity = Vector3.Zero; // velocity computed to be applied to body
private Vector3 m_linearFrictionTimescale = Vector3.Zero; private Vector3 m_linearFrictionTimescale = Vector3.Zero;
private float m_linearMotorDecayTimescale = 0; private float m_linearMotorDecayTimescale = 0;
private float m_linearMotorTimescale = 0; private float m_linearMotorTimescale = 0;
@ -93,6 +93,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// private Vector3 m_linearMotorOffset = Vector3.Zero; // private Vector3 m_linearMotorOffset = Vector3.Zero;
//Angular properties //Angular properties
private BSVMotor m_angularMotor = new BSVMotor("AngularMotor");
private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor
// private int m_angularMotorApply = 0; // application frame counter // private int m_angularMotorApply = 0; // application frame counter
private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity
@ -124,6 +125,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity. // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity.
//Attractor properties //Attractor properties
private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction");
private float m_verticalAttractionEfficiency = 1.0f; // damped private float m_verticalAttractionEfficiency = 1.0f; // damped
private float m_verticalAttractionTimescale = 500f; // Timescale > 300 means no vert attractor. private float m_verticalAttractionTimescale = 500f; // Timescale > 300 means no vert attractor.
@ -152,10 +154,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_angularDeflectionTimescale = Math.Max(pValue, 0.01f); m_angularDeflectionTimescale = Math.Max(pValue, 0.01f);
break; break;
case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
m_angularMotorDecayTimescale = Math.Max(pValue, 0.01f); m_angularMotorDecayTimescale = Math.Max(0.01f, Math.Min(pValue,120));
m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale;
break; break;
case Vehicle.ANGULAR_MOTOR_TIMESCALE: case Vehicle.ANGULAR_MOTOR_TIMESCALE:
m_angularMotorTimescale = Math.Max(pValue, 0.01f); m_angularMotorTimescale = Math.Max(pValue, 0.01f);
m_angularMotor.TimeScale = m_angularMotorTimescale;
break; break;
case Vehicle.BANKING_EFFICIENCY: case Vehicle.BANKING_EFFICIENCY:
m_bankingEfficiency = Math.Max(-1f, Math.Min(pValue, 1f)); m_bankingEfficiency = Math.Max(-1f, Math.Min(pValue, 1f));
@ -185,33 +189,40 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_linearDeflectionTimescale = Math.Max(pValue, 0.01f); m_linearDeflectionTimescale = Math.Max(pValue, 0.01f);
break; break;
case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE: case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE:
m_linearMotorDecayTimescale = Math.Max(pValue, 0.01f); m_linearMotorDecayTimescale = Math.Max(0.01f, Math.Min(pValue,120));
m_linearMotor.TargetValueDecayTimeScale = m_linearMotorDecayTimescale;
break; break;
case Vehicle.LINEAR_MOTOR_TIMESCALE: case Vehicle.LINEAR_MOTOR_TIMESCALE:
m_linearMotorTimescale = Math.Max(pValue, 0.01f); m_linearMotorTimescale = Math.Max(pValue, 0.01f);
m_linearMotor.TimeScale = m_linearMotorTimescale;
break; break;
case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY: case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY:
m_verticalAttractionEfficiency = Math.Max(0.1f, Math.Min(pValue, 1f)); m_verticalAttractionEfficiency = Math.Max(0.1f, Math.Min(pValue, 1f));
m_verticalAttractionMotor.Efficiency = m_verticalAttractionEfficiency;
break; break;
case Vehicle.VERTICAL_ATTRACTION_TIMESCALE: case Vehicle.VERTICAL_ATTRACTION_TIMESCALE:
m_verticalAttractionTimescale = Math.Max(pValue, 0.01f); m_verticalAttractionTimescale = Math.Max(pValue, 0.01f);
m_verticalAttractionMotor.TimeScale = m_verticalAttractionTimescale;
break; break;
// These are vector properties but the engine lets you use a single float value to // These are vector properties but the engine lets you use a single float value to
// set all of the components to the same value // set all of the components to the same value
case Vehicle.ANGULAR_FRICTION_TIMESCALE: case Vehicle.ANGULAR_FRICTION_TIMESCALE:
m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue); m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue);
m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
break; break;
case Vehicle.ANGULAR_MOTOR_DIRECTION: case Vehicle.ANGULAR_MOTOR_DIRECTION:
m_angularMotorDirection = new Vector3(pValue, pValue, pValue); m_angularMotorDirection = new Vector3(pValue, pValue, pValue);
// m_angularMotorApply = 100; m_angularMotor.SetTarget(m_angularMotorDirection);
break; break;
case Vehicle.LINEAR_FRICTION_TIMESCALE: case Vehicle.LINEAR_FRICTION_TIMESCALE:
m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue); m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue);
m_linearMotor.FrictionTimescale = m_linearFrictionTimescale;
break; break;
case Vehicle.LINEAR_MOTOR_DIRECTION: case Vehicle.LINEAR_MOTOR_DIRECTION:
m_linearMotorDirection = new Vector3(pValue, pValue, pValue); m_linearMotorDirection = new Vector3(pValue, pValue, pValue);
m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue); m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue);
m_linearMotor.SetTarget(m_linearMotorDirection);
break; break;
case Vehicle.LINEAR_MOTOR_OFFSET: case Vehicle.LINEAR_MOTOR_OFFSET:
m_linearMotorOffset = new Vector3(pValue, pValue, pValue); m_linearMotorOffset = new Vector3(pValue, pValue, pValue);
@ -227,6 +238,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{ {
case Vehicle.ANGULAR_FRICTION_TIMESCALE: case Vehicle.ANGULAR_FRICTION_TIMESCALE:
m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
break; break;
case Vehicle.ANGULAR_MOTOR_DIRECTION: case Vehicle.ANGULAR_MOTOR_DIRECTION:
// Limit requested angular speed to 2 rps= 4 pi rads/sec // Limit requested angular speed to 2 rps= 4 pi rads/sec
@ -234,14 +246,16 @@ namespace OpenSim.Region.Physics.BulletSPlugin
pValue.Y = Math.Max(-12.56f, Math.Min(pValue.Y, 12.56f)); pValue.Y = Math.Max(-12.56f, Math.Min(pValue.Y, 12.56f));
pValue.Z = Math.Max(-12.56f, Math.Min(pValue.Z, 12.56f)); pValue.Z = Math.Max(-12.56f, Math.Min(pValue.Z, 12.56f));
m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
// m_angularMotorApply = 100; m_angularMotor.SetTarget(m_angularMotorDirection);
break; break;
case Vehicle.LINEAR_FRICTION_TIMESCALE: case Vehicle.LINEAR_FRICTION_TIMESCALE:
m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
m_linearMotor.FrictionTimescale = m_linearFrictionTimescale;
break; break;
case Vehicle.LINEAR_MOTOR_DIRECTION: case Vehicle.LINEAR_MOTOR_DIRECTION:
m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z); m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z);
m_linearMotor.SetTarget(m_linearMotorDirection);
break; break;
case Vehicle.LINEAR_MOTOR_OFFSET: case Vehicle.LINEAR_MOTOR_OFFSET:
m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z); m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z);
@ -303,7 +317,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_VhoverEfficiency = 0; m_VhoverEfficiency = 0;
m_VhoverTimescale = 0; m_VhoverTimescale = 0;
m_VehicleBuoyancy = 0; m_VehicleBuoyancy = 0;
m_linearDeflectionEfficiency = 1; m_linearDeflectionEfficiency = 1;
m_linearDeflectionTimescale = 1; m_linearDeflectionTimescale = 1;
@ -319,6 +333,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_referenceFrame = Quaternion.Identity; m_referenceFrame = Quaternion.Identity;
m_flags = (VehicleFlag)0; m_flags = (VehicleFlag)0;
break; break;
case Vehicle.TYPE_SLED: case Vehicle.TYPE_SLED:
@ -351,10 +366,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_bankingMix = 1; m_bankingMix = 1;
m_referenceFrame = Quaternion.Identity; m_referenceFrame = Quaternion.Identity;
m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_MOTOR_UP); m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
m_flags &= | VehicleFlag.HOVER_TERRAIN_ONLY
~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | | VehicleFlag.HOVER_GLOBAL_HEIGHT
VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); | VehicleFlag.HOVER_UP_ONLY);
m_flags |= (VehicleFlag.NO_DEFLECTION_UP
| VehicleFlag.LIMIT_ROLL_ONLY
| VehicleFlag.LIMIT_MOTOR_UP);
break; break;
case Vehicle.TYPE_CAR: case Vehicle.TYPE_CAR:
m_linearMotorDirection = Vector3.Zero; m_linearMotorDirection = Vector3.Zero;
@ -510,6 +528,28 @@ namespace OpenSim.Region.Physics.BulletSPlugin
| VehicleFlag.HOVER_GLOBAL_HEIGHT); | VehicleFlag.HOVER_GLOBAL_HEIGHT);
break; break;
} }
// Update any physical parameters based on this type.
Refresh();
m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale,
m_linearMotorDecayTimescale, m_linearFrictionTimescale,
1f);
m_linearMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale,
m_angularMotorDecayTimescale, m_angularFrictionTimescale,
1f);
m_angularMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
m_verticalAttractionMotor = new BSVMotor("VerticalAttraction", m_verticalAttractionTimescale,
BSMotor.Infinite, BSMotor.InfiniteVector,
m_verticalAttractionEfficiency);
// Z goes away and we keep X and Y
m_verticalAttractionMotor.FrictionTimescale = new Vector3(BSMotor.Infinite, BSMotor.Infinite, 0.1f);
m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
// m_bankingMotor = new BSVMotor("BankingMotor", ...);
} }
// Some of the properties of this prim may have changed. // Some of the properties of this prim may have changed.
@ -518,13 +558,25 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{ {
if (IsActive) if (IsActive)
{ {
m_vehicleMass = Prim.Linkset.LinksetMass;
// Friction effects are handled by this vehicle code // Friction effects are handled by this vehicle code
BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, 0f); float friction = 0f;
BulletSimAPI.SetHitFraction2(Prim.PhysBody.ptr, 0f); BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, friction);
// BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, 0.8f); // Moderate angular movement introduced by Bullet.
// TODO: possibly set AngularFactor and LinearFactor for the type of vehicle.
// Maybe compute linear and angular factor and damping from params.
float angularDamping = PhysicsScene.Params.vehicleAngularDamping;
BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping);
VDetailLog("{0},BSDynamics.Refresh,zeroingFriction and adding damping", Prim.LocalID); // DEBUG DEBUG DEBUG: use uniform inertia to smooth movement added by Bullet
// Vector3 localInertia = new Vector3(1f, 1f, 1f);
Vector3 localInertia = new Vector3(m_vehicleMass, m_vehicleMass, m_vehicleMass);
BulletSimAPI.SetMassProps2(Prim.PhysBody.ptr, m_vehicleMass, localInertia);
VDetailLog("{0},BSDynamics.Refresh,frict={1},inert={2},aDamp={3}",
Prim.LocalID, friction, localInertia, angularDamping);
} }
} }
@ -551,111 +603,109 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{ {
if (!IsActive) return; if (!IsActive) return;
// DEBUG
// Because Bullet does apply forces to the vehicle, our last computed
// linear and angular velocities are not what is happening now.
// Vector3 externalAngularVelocity = Prim.ForceRotationalVelocity - m_lastAngularVelocity;
// m_lastAngularVelocity += (externalAngularVelocity * 0.5f) * pTimestep;
// m_lastAngularVelocity = Prim.ForceRotationalVelocity; // DEBUG: account for what Bullet did last time
// m_lastLinearVelocityVector = Prim.ForceVelocity * Quaternion.Inverse(Prim.ForceOrientation); // DEBUG:
// END DEBUG
m_vehicleMass = Prim.Linkset.LinksetMass;
MoveLinear(pTimestep); MoveLinear(pTimestep);
// Commented out for debug
MoveAngular(pTimestep); MoveAngular(pTimestep);
// Prim.ApplyTorqueImpulse(-Prim.RotationalVelocity * m_vehicleMass, false); // DEBUG DEBUG
// Prim.ForceRotationalVelocity = -Prim.RotationalVelocity; // DEBUG DEBUG
LimitRotation(pTimestep); LimitRotation(pTimestep);
// remember the position so next step we can limit absolute movement effects // remember the position so next step we can limit absolute movement effects
m_lastPositionVector = Prim.ForcePosition; m_lastPositionVector = Prim.ForcePosition;
VDetailLog("{0},BSDynamics.Step,frict={1},grav={2},inertia={3},mass={4}", // DEBUG DEBUG
Prim.LocalID,
BulletSimAPI.GetFriction2(Prim.PhysBody.ptr),
BulletSimAPI.GetGravity2(Prim.PhysBody.ptr),
Prim.Inertia,
m_vehicleMass
);
VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity); Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity);
}// end Step }
// Apply the effect of the linear motor. // Apply the effect of the linear motor.
// Also does hover and float. // Also does hover and float.
private void MoveLinear(float pTimestep) private void MoveLinear(float pTimestep)
{ {
// m_linearMotorDirection is the target direction we are moving relative to the vehicle coordinates Vector3 linearMotorContribution = m_linearMotor.Step(pTimestep);
// m_lastLinearVelocityVector is the current speed we are moving in that direction
if (m_linearMotorDirection.LengthSquared() > 0.001f)
{
Vector3 origDir = m_linearMotorDirection; // DEBUG
Vector3 origVel = m_lastLinearVelocityVector; // DEBUG
// DEBUG: the vehicle velocity rotated to be relative to vehicle coordinates for comparison
Vector3 vehicleVelocity = Prim.ForceVelocity * Quaternion.Inverse(Prim.ForceOrientation); // DEBUG
// Add (desiredVelocity - lastAppliedVelocity) / howLongItShouldTakeToComplete // Rotate new object velocity from vehicle relative to world coordinates
Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/(m_linearMotorTimescale) * pTimestep; linearMotorContribution *= Prim.ForceOrientation;
m_lastLinearVelocityVector += addAmount;
float decayFactor = (1.0f / m_linearMotorDecayTimescale) * pTimestep;
m_linearMotorDirection *= (1f - decayFactor);
// Rotate new object velocity from vehicle relative to world coordinates
m_newVelocity = m_lastLinearVelocityVector * Prim.ForceOrientation;
// Apply friction for next time
Vector3 frictionFactor = (Vector3.One / m_linearFrictionTimescale) * pTimestep;
m_lastLinearVelocityVector *= (Vector3.One - frictionFactor);
VDetailLog("{0},MoveLinear,nonZero,origlmDir={1},origlvVel={2},vehVel={3},add={4},decay={5},frict={6},lmDir={7},lvVec={8},newVel={9}",
Prim.LocalID, origDir, origVel, vehicleVelocity, addAmount, decayFactor, frictionFactor,
m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity);
}
else
{
// if what remains of direction is very small, zero it.
m_linearMotorDirection = Vector3.Zero;
m_lastLinearVelocityVector = Vector3.Zero;
m_newVelocity = Vector3.Zero;
VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID);
}
// m_newVelocity is velocity computed from linear motor in world coordinates
// ==================================================================
// Gravity and Buoyancy // Gravity and Buoyancy
// There is some gravity, make a gravity force vector that is applied after object velocity. // There is some gravity, make a gravity force vector that is applied after object velocity.
// m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
Vector3 grav = Prim.PhysicsScene.DefaultGravity * (1f - m_VehicleBuoyancy); Vector3 grav = Prim.PhysicsScene.DefaultGravity * (1f - m_VehicleBuoyancy);
/*
* RA: Not sure why one would do this unless we are hoping external forces are doing gravity, ...
// Preserve the current Z velocity
Vector3 vel_now = m_prim.Velocity;
m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity
*/
Vector3 pos = Prim.ForcePosition; Vector3 pos = Prim.ForcePosition;
// Vector3 accel = new Vector3(-(m_dir.X - m_lastLinearVelocityVector.X / 0.1f), -(m_dir.Y - m_lastLinearVelocityVector.Y / 0.1f), m_dir.Z - m_lastLinearVelocityVector.Z / 0.1f);
// If below the terrain, move us above the ground a little.
float terrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos); float terrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
Vector3 terrainHeightContribution = ComputeLinearTerrainHeightCorrection(pTimestep, ref pos, terrainHeight);
Vector3 hoverContribution = ComputeLinearHover(pTimestep, ref pos, terrainHeight);
ComputeLinearBlockingEndPoint(pTimestep, ref pos);
Vector3 limitMotorUpContribution = ComputeLinearMotorUp(pTimestep, pos, terrainHeight);
// ==================================================================
Vector3 newVelocity = linearMotorContribution
+ terrainHeightContribution
+ hoverContribution
+ limitMotorUpContribution;
// If not changing some axis, reduce out velocity
if ((m_flags & (VehicleFlag.NO_X)) != 0)
newVelocity.X = 0;
if ((m_flags & (VehicleFlag.NO_Y)) != 0)
newVelocity.Y = 0;
if ((m_flags & (VehicleFlag.NO_Z)) != 0)
newVelocity.Z = 0;
// ==================================================================
// Clamp REALLY high or low velocities
float newVelocityLengthSq = newVelocity.LengthSquared();
if (newVelocityLengthSq > 1e6f)
{
newVelocity /= newVelocity.Length();
newVelocity *= 1000f;
}
else if (newVelocityLengthSq < 1e-6f)
newVelocity = Vector3.Zero;
// ==================================================================
// Stuff new linear velocity into the vehicle
Prim.ForceVelocity = newVelocity;
// Prim.ApplyForceImpulse((m_newVelocity - Prim.Velocity) * m_vehicleMass, false); // DEBUG DEBUG
// Other linear forces are applied as forces.
Vector3 totalDownForce = grav * m_vehicleMass;
if (totalDownForce != Vector3.Zero)
{
Prim.AddForce(totalDownForce, false);
}
VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},primVel={4},totalDown={5}",
Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector,
newVelocity, Prim.Velocity, totalDownForce);
} // end MoveLinear()
public Vector3 ComputeLinearTerrainHeightCorrection(float pTimestep, ref Vector3 pos, float terrainHeight)
{
Vector3 ret = Vector3.Zero;
// If below the terrain, move us above the ground a little.
// Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset. // Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset.
// TODO: Add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass. // TODO: Add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass.
// Vector3 rotatedSize = m_prim.Size * m_prim.ForceOrientation; // Vector3 rotatedSize = m_prim.Size * m_prim.ForceOrientation;
// if (rotatedSize.Z < terrainHeight) // if (rotatedSize.Z < terrainHeight)
if (pos.Z < terrainHeight) if (pos.Z < terrainHeight)
{ {
// TODO: correct position by applying force rather than forcing position.
pos.Z = terrainHeight + 2; pos.Z = terrainHeight + 2;
Prim.ForcePosition = pos; Prim.ForcePosition = pos;
VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, terrainHeight, pos); VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, terrainHeight, pos);
} }
return ret;
}
public Vector3 ComputeLinearHover(float pTimestep, ref Vector3 pos, float terrainHeight)
{
Vector3 ret = Vector3.Zero;
// Check if hovering
// m_VhoverEfficiency: 0=bouncy, 1=totally damped // m_VhoverEfficiency: 0=bouncy, 1=totally damped
// m_VhoverTimescale: time to achieve height // m_VhoverTimescale: time to achieve height
if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0) if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
@ -694,28 +744,31 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// RA: where does the 50 come from? // RA: where does the 50 come from?
float verticalCorrectionVelocity = pTimestep * ((verticalError * 50.0f) / m_VhoverTimescale); float verticalCorrectionVelocity = pTimestep * ((verticalError * 50.0f) / m_VhoverTimescale);
// Replace Vertical speed with correction figure if significant // Replace Vertical speed with correction figure if significant
if (Math.Abs(verticalError) > 0.01f) if (verticalError > 0.01f)
{ {
m_newVelocity.Z += verticalCorrectionVelocity; ret = new Vector3(0f, 0f, verticalCorrectionVelocity);
//KF: m_VhoverEfficiency is not yet implemented //KF: m_VhoverEfficiency is not yet implemented
} }
else if (verticalError < -0.01) else if (verticalError < -0.01)
{ {
m_newVelocity.Z -= verticalCorrectionVelocity; ret = new Vector3(0f, 0f, -verticalCorrectionVelocity);
}
else
{
m_newVelocity.Z = 0f;
} }
} }
VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", Prim.LocalID, pos, m_newVelocity, m_VhoverHeight, m_VhoverTargetHeight); VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}",
Prim.LocalID, pos, ret, m_VhoverHeight, m_VhoverTargetHeight);
} }
return ret;
}
public bool ComputeLinearBlockingEndPoint(float pTimestep, ref Vector3 pos)
{
bool changed = false;
Vector3 posChange = pos - m_lastPositionVector; Vector3 posChange = pos - m_lastPositionVector;
if (m_BlockingEndPoint != Vector3.Zero) if (m_BlockingEndPoint != Vector3.Zero)
{ {
bool changed = false;
if (pos.X >= (m_BlockingEndPoint.X - (float)1)) if (pos.X >= (m_BlockingEndPoint.X - (float)1))
{ {
pos.X -= posChange.X + 1; pos.X -= posChange.X + 1;
@ -748,75 +801,45 @@ namespace OpenSim.Region.Physics.BulletSPlugin
Prim.LocalID, m_BlockingEndPoint, posChange, pos); Prim.LocalID, m_BlockingEndPoint, posChange, pos);
} }
} }
return changed;
}
#region downForce public Vector3 ComputeLinearMotorUp(float pTimestep, Vector3 pos, float terrainHeight)
Vector3 downForce = Vector3.Zero; {
Vector3 ret = Vector3.Zero;
if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
{ {
// If the vehicle is motoring into the sky, get it going back down. // If the vehicle is motoring into the sky, get it going back down.
// Is this an angular force or both linear and angular??
float distanceAboveGround = pos.Z - terrainHeight; float distanceAboveGround = pos.Z - terrainHeight;
if (distanceAboveGround > 2f) if (distanceAboveGround > 1f)
{ {
// downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep); // downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep);
// downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale); // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale);
downForce = new Vector3(0, 0, -distanceAboveGround); ret = new Vector3(0, 0, -distanceAboveGround);
} }
// TODO: this calculation is all wrong. From the description at // TODO: this calculation is all wrong. From the description at
// (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce
// has a decay factor. This says this force should // has a decay factor. This says this force should
// be computed with a motor. // be computed with a motor.
VDetailLog("{0},MoveLinear,limitMotorUp,distAbove={1},downForce={2}", VDetailLog("{0},MoveLinear,limitMotorUp,distAbove={1},downForce={2}",
Prim.LocalID, distanceAboveGround, downForce); Prim.LocalID, distanceAboveGround, ret);
} }
#endregion // downForce return ret;
}
// If not changing some axis, reduce out velocity
if ((m_flags & (VehicleFlag.NO_X)) != 0)
m_newVelocity.X = 0;
if ((m_flags & (VehicleFlag.NO_Y)) != 0)
m_newVelocity.Y = 0;
if ((m_flags & (VehicleFlag.NO_Z)) != 0)
m_newVelocity.Z = 0;
// Clamp REALLY high or low velocities
if (m_newVelocity.LengthSquared() > 1e6f)
{
m_newVelocity /= m_newVelocity.Length();
m_newVelocity *= 1000f;
}
else if (m_newVelocity.LengthSquared() < 1e-6f)
m_newVelocity = Vector3.Zero;
// Stuff new linear velocity into the vehicle
Prim.ForceVelocity = m_newVelocity;
// Prim.ApplyForceImpulse((m_newVelocity - Prim.Velocity) * m_vehicleMass, false); // DEBUG DEBUG
Vector3 totalDownForce = downForce + grav;
if (totalDownForce != Vector3.Zero)
{
Prim.AddForce(totalDownForce * m_vehicleMass, false);
// Prim.ApplyForceImpulse(totalDownForce * m_vehicleMass, false);
}
VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},primVel={4},totalDown={5}",
Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, Prim.Velocity, totalDownForce);
} // end MoveLinear()
// =======================================================================
// ======================================================================= // =======================================================================
// Apply the effect of the angular motor. // Apply the effect of the angular motor.
private void MoveAngular(float pTimestep) private void MoveAngular(float pTimestep)
{ {
// m_angularMotorDirection // angular velocity requested by LSL motor // m_angularMotorDirection // angular velocity requested by LSL motor
// m_angularMotorApply // application frame counter
// m_angularMotorVelocity // current angular motor velocity (ramps up and down) // m_angularMotorVelocity // current angular motor velocity (ramps up and down)
// m_angularMotorTimescale // motor angular velocity ramp up rate // m_angularMotorTimescale // motor angular velocity ramp up time
// m_angularMotorDecayTimescale // motor angular velocity decay rate // m_angularMotorDecayTimescale // motor angular velocity decay rate
// m_angularFrictionTimescale // body angular velocity decay rate // m_angularFrictionTimescale // body angular velocity decay rate
// m_lastAngularVelocity // what was last applied to body // m_lastAngularVelocity // what was last applied to body
/*
if (m_angularMotorDirection.LengthSquared() > 0.0001) if (m_angularMotorDirection.LengthSquared() > 0.0001)
{ {
Vector3 origVel = m_angularMotorVelocity; Vector3 origVel = m_angularMotorVelocity;
@ -835,141 +858,35 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{ {
m_angularMotorVelocity = Vector3.Zero; m_angularMotorVelocity = Vector3.Zero;
} }
*/
#region Vertical attactor Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep);
Vector3 vertattr = Vector3.Zero; // ==================================================================
Vector3 deflection = Vector3.Zero; // NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
Vector3 banking = Vector3.Zero; if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
// If vertical attaction timescale is reasonable and we applied an angular force last time...
if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero)
{ {
float VAservo = pTimestep * 0.2f / m_verticalAttractionTimescale; angularMotorContribution.X = 0f;
if (Prim.IsColliding) angularMotorContribution.Y = 0f;
VAservo = pTimestep * 0.05f / (m_verticalAttractionTimescale); VDetailLog("{0},MoveAngular,noDeflectionUp,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution);
VAservo *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
// Create a vector of the vehicle "up" in world coordinates
Vector3 verticalError = Vector3.UnitZ * Prim.ForceOrientation;
// verticalError.X and .Y are the World error amounts. They are 0 when there is no
// error (Vehicle Body is 'vertical'), and .Z will be 1. As the body leans to its
// side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall
// and .Z will go // negative. Similar for tilt and |.Y|. .X and .Y must be
// modulated to prevent a stable inverted body.
// Error is 0 (no error) to +/- 2 (max error)
if (verticalError.Z < 0.0f)
{
verticalError.X = 2.0f - verticalError.X;
verticalError.Y = 2.0f - verticalError.Y;
}
// scale it by VAservo (timestep and timescale)
verticalError = verticalError * VAservo;
// As the body rotates around the X axis, then verticalError.Y increases; Rotated around Y
// then .X increases, so change Body angular velocity X based on Y, and Y based on X.
// Z is not changed.
vertattr.X = verticalError.Y;
vertattr.Y = - verticalError.X;
vertattr.Z = 0f;
// scaling appears better usingsquare-law
Vector3 angularVelocity = Prim.ForceRotationalVelocity;
float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
vertattr.X += bounce * angularVelocity.X;
vertattr.Y += bounce * angularVelocity.Y;
VDetailLog("{0},MoveAngular,verticalAttraction,VAservo={1},effic={2},verticalError={3},bounce={4},vertattr={5}",
Prim.LocalID, VAservo, m_verticalAttractionEfficiency, verticalError, bounce, vertattr);
}
#endregion // Vertical attactor
#region Deflection
if (m_angularDeflectionEfficiency != 0)
{
// Compute a scaled vector that points in the preferred axis (X direction)
Vector3 scaledDefaultDirection =
new Vector3((pTimestep * 10 * (m_angularDeflectionEfficiency / m_angularDeflectionTimescale)), 0, 0);
// Adding the current vehicle orientation and reference frame displaces the orientation to the frame.
// Rotate the scaled default axix relative to the actual vehicle direction giving where it should point.
Vector3 preferredAxisOfMotion = scaledDefaultDirection * Quaternion.Add(Prim.ForceOrientation, m_referenceFrame);
// Scale by efficiency and timescale
deflection = (preferredAxisOfMotion * (m_angularDeflectionEfficiency) / m_angularDeflectionTimescale) * pTimestep;
VDetailLog("{0},MoveAngular,Deflection,perfAxis={1},deflection={2}",
Prim.LocalID, preferredAxisOfMotion, deflection);
// This deflection computation is not correct.
deflection = Vector3.Zero;
} }
#endregion Vector3 verticalAttractionContribution = ComputeAngularVerticalAttraction(pTimestep);
#region Banking Vector3 deflectionContribution = ComputeAngularDeflection(pTimestep);
if (m_bankingEfficiency != 0) Vector3 bankingContribution = ComputeAngularBanking(pTimestep);
{
Vector3 dir = Vector3.One * Prim.ForceOrientation;
float mult = (m_bankingMix*m_bankingMix)*-1*(m_bankingMix < 0 ? -1 : 1);
//Changes which way it banks in and out of turns
//Use the square of the efficiency, as it looks much more how SL banking works // ==================================================================
float effSquared = (m_bankingEfficiency*m_bankingEfficiency); m_lastVertAttractor = verticalAttractionContribution;
if (m_bankingEfficiency < 0)
effSquared *= -1; //Keep the negative!
float mix = Math.Abs(m_bankingMix);
if (m_angularMotorVelocity.X == 0)
{
/*if (!parent.Orientation.ApproxEquals(this.m_referenceFrame, 0.25f))
{
Vector3 axisAngle;
float angle;
parent.Orientation.GetAxisAngle(out axisAngle, out angle);
Vector3 rotatedVel = parent.Velocity * parent.Orientation;
if ((rotatedVel.X < 0 && axisAngle.Y > 0) || (rotatedVel.X > 0 && axisAngle.Y < 0))
m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (1f) * 10;
else
m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (-1f) * 10;
}*/
}
else
banking.Z += (effSquared*(mult*mix))*(m_angularMotorVelocity.X) * 4;
if (!Prim.IsColliding && Math.Abs(m_angularMotorVelocity.X) > mix)
//If they are colliding, we probably shouldn't shove the prim around... probably
{
float angVelZ = m_angularMotorVelocity.X*-1;
/*if(angVelZ > mix)
angVelZ = mix;
else if(angVelZ < -mix)
angVelZ = -mix;*/
//This controls how fast and how far the banking occurs
Vector3 bankingRot = new Vector3(angVelZ*(effSquared*mult), 0, 0);
if (bankingRot.X > 3)
bankingRot.X = 3;
else if (bankingRot.X < -3)
bankingRot.X = -3;
bankingRot *= Prim.ForceOrientation;
banking += bankingRot;
}
m_angularMotorVelocity.X *= m_bankingEfficiency == 1 ? 0.0f : 1 - m_bankingEfficiency;
VDetailLog("{0},MoveAngular,Banking,bEff={1},angMotVel={2},banking={3}",
Prim.LocalID, m_bankingEfficiency, m_angularMotorVelocity, banking);
}
#endregion
m_lastVertAttractor = vertattr;
// Sum velocities // Sum velocities
m_lastAngularVelocity = m_angularMotorVelocity + vertattr + banking + deflection; m_lastAngularVelocity = angularMotorContribution
+ verticalAttractionContribution
#region Linear Motor Offset + bankingContribution
+ deflectionContribution;
// ==================================================================
//Offset section //Offset section
if (m_linearMotorOffset != Vector3.Zero) if (m_linearMotorOffset != Vector3.Zero)
{ {
@ -985,8 +902,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// //
// The torque created is the linear velocity crossed with the offset // The torque created is the linear velocity crossed with the offset
// NOTE: this computation does should be in the linear section // TODO: this computation should be in the linear section
// because there we know the impulse being applied. // because that is where we know the impulse being applied.
Vector3 torqueFromOffset = Vector3.Zero; Vector3 torqueFromOffset = Vector3.Zero;
// torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse); // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse);
if (float.IsNaN(torqueFromOffset.X)) if (float.IsNaN(torqueFromOffset.X))
@ -1000,15 +917,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
VDetailLog("{0},BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset); VDetailLog("{0},BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset);
} }
#endregion // ==================================================================
if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
{
m_lastAngularVelocity.X = 0;
m_lastAngularVelocity.Y = 0;
VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity);
}
if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f)) if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f))
{ {
m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero. m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero.
@ -1021,18 +930,166 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// The above calculates the absolute angular velocity needed. Angular velocity is massless. // The above calculates the absolute angular velocity needed. Angular velocity is massless.
// Since we are stuffing the angular velocity directly into the object, the computed // Since we are stuffing the angular velocity directly into the object, the computed
// velocity needs to be scaled by the timestep. // velocity needs to be scaled by the timestep.
Vector3 applyAngularForce = ((m_lastAngularVelocity * pTimestep) - Prim.ForceRotationalVelocity); // Also remove any motion that is on the object so added motion is only from vehicle.
Vector3 applyAngularForce = ((m_lastAngularVelocity * pTimestep)
- Prim.ForceRotationalVelocity);
// Unscale the force by the angular factor so it overwhelmes the Bullet additions.
Prim.ForceRotationalVelocity = applyAngularForce; Prim.ForceRotationalVelocity = applyAngularForce;
// Decay the angular movement for next time VDetailLog("{0},MoveAngular,done,angMotor={1},vertAttr={2},bank={3},deflect={4},newAngForce={5},lastAngular={6}",
Vector3 decayamount = (Vector3.One / m_angularFrictionTimescale) * pTimestep; Prim.LocalID,
m_lastAngularVelocity *= Vector3.One - decayamount; angularMotorContribution, verticalAttractionContribution,
bankingContribution, deflectionContribution,
VDetailLog("{0},MoveAngular,done,newRotVel={1},decay={2},lastAngular={3}", applyAngularForce, m_lastAngularVelocity
Prim.LocalID, applyAngularForce, decayamount, m_lastAngularVelocity); );
} }
} //end MoveAngular }
public Vector3 ComputeAngularVerticalAttraction(float pTimestep)
{
Vector3 ret = Vector3.Zero;
// If vertical attaction timescale is reasonable and we applied an angular force last time...
if (m_verticalAttractionTimescale < 500)
{
Vector3 verticalError = Vector3.UnitZ * Prim.ForceOrientation;
verticalError.Normalize();
m_verticalAttractionMotor.SetCurrent(verticalError);
m_verticalAttractionMotor.SetTarget(Vector3.UnitZ);
ret = m_verticalAttractionMotor.Step(pTimestep);
/*
// Take a vector pointing up and convert it from world to vehicle relative coords.
Vector3 verticalError = Vector3.UnitZ * Prim.ForceOrientation;
verticalError.Normalize();
// If vertical attraction correction is needed, the vector that was pointing up (UnitZ)
// is now leaning to one side (rotated around the X axis) and the Y value will
// go from zero (nearly straight up) to one (completely to the side) or leaning
// front-to-back (rotated around the Y axis) and the value of X will be between
// zero and one.
// The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees.
// If verticalError.Z is negative, the vehicle is upside down. Add additional push.
if (verticalError.Z < 0f)
{
verticalError.X = 2f - verticalError.X;
verticalError.Y = 2f - verticalError.Y;
}
// Y error means needed rotation around X axis and visa versa.
verticalAttractionContribution.X = verticalError.Y;
verticalAttractionContribution.Y = - verticalError.X;
verticalAttractionContribution.Z = 0f;
// scale by the time scale and timestep
Vector3 unscaledContrib = verticalAttractionContribution;
verticalAttractionContribution /= m_verticalAttractionTimescale;
verticalAttractionContribution *= pTimestep;
// apply efficiency
Vector3 preEfficiencyContrib = verticalAttractionContribution;
float efficencySquared = m_verticalAttractionEfficiency * m_verticalAttractionEfficiency;
verticalAttractionContribution *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
VDetailLog("{0},MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},preEff={3},eff={4},effSq={5},vertAttr={6}",
Prim.LocalID, verticalError, unscaledContrib, preEfficiencyContrib,
m_verticalAttractionEfficiency, efficencySquared,
verticalAttractionContribution);
*/
}
return ret;
}
public Vector3 ComputeAngularDeflection(float pTimestep)
{
Vector3 ret = Vector3.Zero;
if (m_angularDeflectionEfficiency != 0)
{
// Compute a scaled vector that points in the preferred axis (X direction)
Vector3 scaledDefaultDirection =
new Vector3((pTimestep * 10 * (m_angularDeflectionEfficiency / m_angularDeflectionTimescale)), 0, 0);
// Adding the current vehicle orientation and reference frame displaces the orientation to the frame.
// Rotate the scaled default axix relative to the actual vehicle direction giving where it should point.
Vector3 preferredAxisOfMotion = scaledDefaultDirection * Quaternion.Add(Prim.ForceOrientation, m_referenceFrame);
// Scale by efficiency and timescale
ret = (preferredAxisOfMotion * (m_angularDeflectionEfficiency) / m_angularDeflectionTimescale) * pTimestep;
VDetailLog("{0},MoveAngular,Deflection,perfAxis={1},deflection={2}", Prim.LocalID, preferredAxisOfMotion, ret);
// This deflection computation is not correct.
ret = Vector3.Zero;
}
return ret;
}
public Vector3 ComputeAngularBanking(float pTimestep)
{
Vector3 ret = Vector3.Zero;
if (m_bankingEfficiency != 0)
{
Vector3 dir = Vector3.One * Prim.ForceOrientation;
float mult = (m_bankingMix * m_bankingMix) * -1 * (m_bankingMix < 0 ? -1 : 1);
//Changes which way it banks in and out of turns
//Use the square of the efficiency, as it looks much more how SL banking works
float effSquared = (m_bankingEfficiency * m_bankingEfficiency);
if (m_bankingEfficiency < 0)
effSquared *= -1; //Keep the negative!
float mix = Math.Abs(m_bankingMix);
if (m_angularMotorVelocity.X == 0)
{
// The vehicle is stopped
/*if (!parent.Orientation.ApproxEquals(this.m_referenceFrame, 0.25f))
{
Vector3 axisAngle;
float angle;
parent.Orientation.GetAxisAngle(out axisAngle, out angle);
Vector3 rotatedVel = parent.Velocity * parent.Orientation;
if ((rotatedVel.X < 0 && axisAngle.Y > 0) || (rotatedVel.X > 0 && axisAngle.Y < 0))
m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (1f) * 10;
else
m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (-1f) * 10;
}*/
}
else
{
ret.Z += (effSquared * (mult * mix)) * (m_angularMotorVelocity.X) * 4;
}
//If they are colliding, we probably shouldn't shove the prim around... probably
if (!Prim.IsColliding && Math.Abs(m_angularMotorVelocity.X) > mix)
{
float angVelZ = m_angularMotorVelocity.X * -1;
/*if(angVelZ > mix)
angVelZ = mix;
else if(angVelZ < -mix)
angVelZ = -mix;*/
//This controls how fast and how far the banking occurs
Vector3 bankingRot = new Vector3(angVelZ * (effSquared * mult), 0, 0);
if (bankingRot.X > 3)
bankingRot.X = 3;
else if (bankingRot.X < -3)
bankingRot.X = -3;
bankingRot *= Prim.ForceOrientation;
ret += bankingRot;
}
m_angularMotorVelocity.X *= m_bankingEfficiency == 1 ? 0.0f : 1 - m_bankingEfficiency;
VDetailLog("{0},MoveAngular,Banking,bEff={1},angMotVel={2},effSq={3},mult={4},mix={5},banking={6}",
Prim.LocalID, m_bankingEfficiency, m_angularMotorVelocity, effSquared, mult, mix, ret);
}
return ret;
}
// This is from previous instantiations of XXXDynamics.cs.
// Applies roll reference frame.
// TODO: is this the right way to separate the code to do this operation?
// Should this be in MoveAngular()?
internal void LimitRotation(float timestep) internal void LimitRotation(float timestep)
{ {
Quaternion rotq = Prim.ForceOrientation; Quaternion rotq = Prim.ForceOrientation;

View File

@ -82,9 +82,9 @@ public abstract class BSLinkset
// Some linksets have a preferred physical shape. // Some linksets have a preferred physical shape.
// Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected. // Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected.
public virtual ShapeData.PhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor) public virtual BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
{ {
return ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; return BSPhysicsShapeType.SHAPE_UNKNOWN;
} }
// Linksets move around the children so the linkset might need to compute the child position // Linksets move around the children so the linkset might need to compute the child position

View File

@ -42,12 +42,12 @@ public sealed class BSLinksetCompound : BSLinkset
} }
// For compound implimented linksets, if there are children, use compound shape for the root. // For compound implimented linksets, if there are children, use compound shape for the root.
public override ShapeData.PhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor) public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
{ {
ShapeData.PhysicsShapeType ret = ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
if (IsRoot(requestor) && HasAnyChildren) if (IsRoot(requestor) && HasAnyChildren)
{ {
ret = ShapeData.PhysicsShapeType.SHAPE_COMPOUND; ret = BSPhysicsShapeType.SHAPE_COMPOUND;
} }
// DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret); // DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret);
return ret; return ret;

View File

@ -0,0 +1,191 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyrightD
* 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.Collections.Generic;
using System.Text;
using System.Reflection;
using Nini.Config;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public struct MaterialAttributes
{
// Material type values that correspond with definitions for LSL
public enum Material : int
{
Stone = 0,
Metal,
Glass,
Wood,
Flesh,
Plastic,
Rubber,
Light,
// Hereafter are BulletSim additions
Avatar,
NumberOfTypes // the count of types in the enum.
}
// Names must be in the order of the above enum.
public static string[] MaterialNames = { "Stone", "Metal", "Glass", "Wood",
"Flesh", "Plastic", "Rubber", "Light", "Avatar" };
public static string[] MaterialAttribs = { "Density", "Friction", "Restitution",
"ccdMotionThreshold", "ccdSweptSphereRadius" };
public MaterialAttributes(string t, float d, float f, float r, float ccdM, float ccdS)
{
type = t;
density = d;
friction = f;
restitution = r;
ccdMotionThreshold = ccdM;
ccdSweptSphereRadius = ccdS;
}
public string type;
public float density;
public float friction;
public float restitution;
public float ccdMotionThreshold;
public float ccdSweptSphereRadius;
}
public static class BSMaterials
{
public static MaterialAttributes[] Attributes;
static BSMaterials()
{
// Attribute sets for both the non-physical and physical instances of materials.
Attributes = new MaterialAttributes[(int)MaterialAttributes.Material.NumberOfTypes * 2];
}
// This is where all the default material attributes are defined.
public static void InitializeFromDefaults(ConfigurationParameters parms)
{
// public static string[] MaterialNames = { "Stone", "Metal", "Glass", "Wood",
// "Flesh", "Plastic", "Rubber", "Light", "Avatar" };
float dFriction = parms.defaultFriction;
float dRestitution = parms.defaultRestitution;
float dDensity = parms.defaultDensity;
float dCcdM = parms.ccdMotionThreshold;
float dCcdS = parms.ccdSweptSphereRadius;
Attributes[(int)MaterialAttributes.Material.Stone] =
new MaterialAttributes("stone",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Metal] =
new MaterialAttributes("metal",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Glass] =
new MaterialAttributes("glass",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Wood] =
new MaterialAttributes("wood",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Flesh] =
new MaterialAttributes("flesh",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Plastic] =
new MaterialAttributes("plastic",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Rubber] =
new MaterialAttributes("rubber",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Light] =
new MaterialAttributes("light",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Avatar] =
new MaterialAttributes("avatar",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Stone + (int)MaterialAttributes.Material.NumberOfTypes] =
new MaterialAttributes("stonePhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Metal + (int)MaterialAttributes.Material.NumberOfTypes] =
new MaterialAttributes("metalPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
new MaterialAttributes("glassPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
new MaterialAttributes("woodPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
new MaterialAttributes("fleshPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
new MaterialAttributes("plasticPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
new MaterialAttributes("rubberPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Light + (int)MaterialAttributes.Material.NumberOfTypes] =
new MaterialAttributes("lightPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
Attributes[(int)MaterialAttributes.Material.Avatar + (int)MaterialAttributes.Material.NumberOfTypes] =
new MaterialAttributes("avatarPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
}
// Under the [BulletSim] section, one can change the individual material
// attribute values. The format of the configuration parameter is:
// <materialName><Attribute>["Physical"] = floatValue
// For instance:
// [BulletSim]
// StoneFriction = 0.2
// FleshRestitutionPhysical = 0.8
// Materials can have different parameters for their static and
// physical instantiations. When setting the non-physical value,
// both values are changed. Setting the physical value only changes
// the physical value.
public static void InitializefromParameters(IConfig pConfig)
{
int matType = 0;
foreach (string matName in MaterialAttributes.MaterialNames)
{
foreach (string attribName in MaterialAttributes.MaterialAttribs)
{
string paramName = matName + attribName;
if (pConfig.Contains(paramName))
{
float paramValue = pConfig.GetFloat(paramName);
SetAttributeValue(matType, attribName, paramValue);
// set the physical value also
SetAttributeValue(matType + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
}
paramName += "Physical";
if (pConfig.Contains(paramName))
{
float paramValue = pConfig.GetFloat(paramName);
SetAttributeValue(matType + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
}
}
matType++;
}
}
private static void SetAttributeValue(int matType, string attribName, float val)
{
MaterialAttributes thisAttrib = Attributes[matType];
FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName);
if (fieldInfo != null)
{
fieldInfo.SetValue(thisAttrib, val);
Attributes[matType] = thisAttrib;
}
}
public static MaterialAttributes GetAttributes(MaterialAttributes.Material type, bool isPhysical)
{
int ind = (int)type;
if (isPhysical) ind += (int)MaterialAttributes.Material.NumberOfTypes;
return Attributes[ind];
}
}
}

View File

@ -1,104 +1,169 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using OpenMetaverse; using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public abstract class BSMotor public abstract class BSMotor
{ {
public virtual void Reset() { } // Timescales and other things can be turned off by setting them to 'infinite'.
public virtual void Zero() { } public const float Infinite = 10000f;
} public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite);
// Can all the incremental stepping be replaced with motor classes?
public class BSVMotor : BSMotor public BSMotor(string useName)
{ {
public Vector3 FrameOfReference { get; set; } UseName = useName;
public Vector3 Offset { get; set; } PhysicsScene = null;
}
public float TimeScale { get; set; } public virtual void Reset() { }
public float TargetValueDecayTimeScale { get; set; } public virtual void Zero() { }
public Vector3 CurrentValueReductionTimescale { get; set; }
public float Efficiency { get; set; } public string UseName { get; private set; }
// Used only for outputting debug information. Might not be set so check for null.
public Vector3 TargetValue { get; private set; } public BSScene PhysicsScene { get; set; }
public Vector3 CurrentValue { get; private set; } protected void MDetailLog(string msg, params Object[] parms)
{
if (PhysicsScene != null)
{
BSVMotor(float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) if (PhysicsScene.VehicleLoggingEnabled)
{ {
TimeScale = timeScale; PhysicsScene.DetailLog(msg, parms);
TargetValueDecayTimeScale = decayTimeScale; }
CurrentValueReductionTimescale = frictionTimeScale; }
Efficiency = efficiency; }
} }
public void SetCurrent(Vector3 current) // Can all the incremental stepping be replaced with motor classes?
{ public class BSVMotor : BSMotor
CurrentValue = current; {
} public Vector3 FrameOfReference { get; set; }
public void SetTarget(Vector3 target) public Vector3 Offset { get; set; }
{
TargetValue = target; public float TimeScale { get; set; }
} public float TargetValueDecayTimeScale { get; set; }
public Vector3 Step(float timeStep) public Vector3 FrictionTimescale { get; set; }
{ public float Efficiency { get; set; }
if (CurrentValue.LengthSquared() > 0.001f)
{ public Vector3 TargetValue { get; private set; }
// Vector3 origDir = Target; // DEBUG public Vector3 CurrentValue { get; private set; }
// Vector3 origVel = CurrentValue; // DEBUG
public BSVMotor(string useName)
// Add (desiredVelocity - currentAppliedVelocity) / howLongItShouldTakeToComplete : base(useName)
Vector3 addAmount = (TargetValue - CurrentValue)/(TargetValue) * timeStep; {
CurrentValue += addAmount; TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
Efficiency = 1f;
float decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep; FrictionTimescale = BSMotor.InfiniteVector;
TargetValue *= (1f - decayFactor); CurrentValue = TargetValue = Vector3.Zero;
}
Vector3 frictionFactor = (Vector3.One / CurrentValueReductionTimescale) * timeStep; public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency)
CurrentValue *= (Vector3.One - frictionFactor); : this(useName)
} {
else TimeScale = timeScale;
{ TargetValueDecayTimeScale = decayTimeScale;
// if what remains of direction is very small, zero it. FrictionTimescale = frictionTimeScale;
TargetValue = Vector3.Zero; Efficiency = efficiency;
CurrentValue = Vector3.Zero; CurrentValue = TargetValue = Vector3.Zero;
}
// VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID); public void SetCurrent(Vector3 current)
} {
return CurrentValue; CurrentValue = current;
} }
} public void SetTarget(Vector3 target)
{
public class BSFMotor : BSMotor TargetValue = target;
{ }
public float TimeScale { get; set; } public Vector3 Step(float timeStep)
public float DecayTimeScale { get; set; } {
public float Friction { get; set; } Vector3 returnCurrent = Vector3.Zero;
public float Efficiency { get; set; } if (!CurrentValue.ApproxEquals(TargetValue, 0.01f))
{
public float Target { get; private set; } Vector3 origTarget = TargetValue; // DEBUG
public float CurrentValue { get; private set; } Vector3 origCurrVal = CurrentValue; // DEBUG
BSFMotor(float timeScale, float decayTimescale, float friction, float efficiency) // Addition = (desiredVector - currentAppliedVector) / secondsItShouldTakeToComplete
{ Vector3 addAmount = (TargetValue - CurrentValue)/TimeScale * timeStep;
} CurrentValue += addAmount;
public void SetCurrent(float target)
{ returnCurrent = CurrentValue;
}
public void SetTarget(float target) // The desired value reduces to zero which also reduces the difference with current.
{ // If the decay time is infinite, don't decay at all.
} float decayFactor = 0f;
public float Step(float timeStep) if (TargetValueDecayTimeScale != BSMotor.Infinite)
{ {
return 0f; decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
} TargetValue *= (1f - decayFactor);
} }
public class BSPIDMotor : BSMotor
{ Vector3 frictionFactor = Vector3.Zero;
// TODO: write and use this one if (FrictionTimescale != BSMotor.InfiniteVector)
BSPIDMotor() {
{ // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
} frictionFactor.X = FrictionTimescale.X == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.X) * timeStep;
} frictionFactor.Y = FrictionTimescale.Y == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.Y) * timeStep;
} frictionFactor.Z = FrictionTimescale.Z == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.Z) * timeStep;
CurrentValue *= (Vector3.One - frictionFactor);
}
MDetailLog("{0},BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},timeScale={5},addAmnt={6},targetDecay={7},decayFact={8},fricTS={9},frictFact={10}",
BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
timeStep, TimeScale, addAmount,
TargetValueDecayTimeScale, decayFactor,
FrictionTimescale, frictionFactor);
MDetailLog("{0},BSVMotor.Step,nonZero,{1},curr={2},target={3},add={4},decay={5},frict={6},ret={7}",
BSScene.DetailLogZero, UseName, CurrentValue, TargetValue,
addAmount, decayFactor, frictionFactor, returnCurrent);
}
else
{
// Difference between what we have and target is small. Motor is done.
CurrentValue = Vector3.Zero;
TargetValue = Vector3.Zero;
MDetailLog("{0},BSVMotor.Step,zero,{1},curr={2},target={3},ret={4}",
BSScene.DetailLogZero, UseName, TargetValue, CurrentValue, returnCurrent);
}
return returnCurrent;
}
public override string ToString()
{
return String.Format("<{0},curr={1},targ={2},decayTS={3},frictTS={4}>",
UseName, CurrentValue, TargetValue, TargetValueDecayTimeScale, FrictionTimescale);
}
}
public class BSFMotor : BSMotor
{
public float TimeScale { get; set; }
public float DecayTimeScale { get; set; }
public float Friction { get; set; }
public float Efficiency { get; set; }
public float Target { get; private set; }
public float CurrentValue { get; private set; }
public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency)
: base(useName)
{
}
public void SetCurrent(float target)
{
}
public void SetTarget(float target)
{
}
public float Step(float timeStep)
{
return 0f;
}
}
public class BSPIDMotor : BSMotor
{
// TODO: write and use this one
public BSPIDMotor(string useName)
: base(useName)
{
}
}
}

View File

@ -47,7 +47,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
*/ */
public abstract class BSPhysObject : PhysicsActor public abstract class BSPhysObject : PhysicsActor
{ {
protected void BaseInitialize(BSScene parentScene, uint localID, string name, string typeName) protected BSPhysObject()
{
}
protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName)
{ {
PhysicsScene = parentScene; PhysicsScene = parentScene;
LocalID = localID; LocalID = localID;
@ -91,9 +94,9 @@ public abstract class BSPhysObject : PhysicsActor
public PrimitiveBaseShape BaseShape { get; protected set; } public PrimitiveBaseShape BaseShape { get; protected set; }
// Some types of objects have preferred physical representations. // Some types of objects have preferred physical representations.
// Returns SHAPE_UNKNOWN if there is no preference. // Returns SHAPE_UNKNOWN if there is no preference.
public virtual ShapeData.PhysicsShapeType PreferredPhysicalShape public virtual BSPhysicsShapeType PreferredPhysicalShape
{ {
get { return ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; } get { return BSPhysicsShapeType.SHAPE_UNKNOWN; }
} }
// When the physical properties are updated, an EntityProperty holds the update values. // When the physical properties are updated, an EntityProperty holds the update values.

View File

@ -47,7 +47,6 @@ public sealed class BSPrim : BSPhysObject
// _size is what the user passed. Scale is what we pass to the physics engine with the mesh. // _size is what the user passed. Scale is what we pass to the physics engine with the mesh.
// Often Scale is unity because the meshmerizer will apply _size when creating the mesh. // Often Scale is unity because the meshmerizer will apply _size when creating the mesh.
private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user
// private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer
private bool _grabbed; private bool _grabbed;
private bool _isSelected; private bool _isSelected;
@ -88,13 +87,13 @@ public sealed class BSPrim : BSPhysObject
public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size,
OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical)
: base(parent_scene, localID, primName, "BSPrim")
{ {
// m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID); // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID);
base.BaseInitialize(parent_scene, localID, primName, "BSPrim");
_physicsActorType = (int)ActorTypes.Prim; _physicsActorType = (int)ActorTypes.Prim;
_position = pos; _position = pos;
_size = size; _size = size;
Scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type Scale = size; // the scale will be set by CreateGeom depending on object type
_orientation = rotation; _orientation = rotation;
_buoyancy = 1f; _buoyancy = 1f;
_velocity = OMV.Vector3.Zero; _velocity = OMV.Vector3.Zero;
@ -155,6 +154,8 @@ public sealed class BSPrim : BSPhysObject
public override OMV.Vector3 Size { public override OMV.Vector3 Size {
get { return _size; } get { return _size; }
set { set {
// We presume the scale and size are the same. If scale must be changed for
// the physical shape, that is done when the geometry is built.
_size = value; _size = value;
ForceBodyShapeRebuild(false); ForceBodyShapeRebuild(false);
} }
@ -170,7 +171,7 @@ public sealed class BSPrim : BSPhysObject
} }
} }
// Whatever the linkset wants is what I want. // Whatever the linkset wants is what I want.
public override ShapeData.PhysicsShapeType PreferredPhysicalShape public override BSPhysicsShapeType PreferredPhysicalShape
{ get { return Linkset.PreferredPhysicalShape(this); } } { get { return Linkset.PreferredPhysicalShape(this); } }
public override bool ForceBodyShapeRebuild(bool inTaintTime) public override bool ForceBodyShapeRebuild(bool inTaintTime)
@ -274,19 +275,19 @@ public sealed class BSPrim : BSPhysObject
if (!Linkset.IsRoot(this)) if (!Linkset.IsRoot(this))
_position = Linkset.Position(this); _position = Linkset.Position(this);
// don't do the GetObjectPosition for root elements because this function is called a zillion times // don't do the GetObjectPosition for root elements because this function is called a zillion times.
// _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
return _position; return _position;
} }
set { set {
// If you must push the position into the physics engine, use ForcePosition. // If the position must be forced into the physics engine, use ForcePosition.
if (_position == value) if (_position == value)
{ {
return; return;
} }
_position = value; _position = value;
// TODO: what does it mean to set the position of a child prim?? Rebuild the constraint? // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint?
PositionSanityCheck(); PositionSanityCheck(false);
PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
{ {
// DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
@ -302,7 +303,7 @@ public sealed class BSPrim : BSPhysObject
} }
set { set {
_position = value; _position = value;
PositionSanityCheck(); // PositionSanityCheck(); // Don't do this! Causes a loop and caller should know better.
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
ActivateIfPhysical(false); ActivateIfPhysical(false);
} }
@ -311,52 +312,42 @@ public sealed class BSPrim : BSPhysObject
// Check that the current position is sane and, if not, modify the position to make it so. // Check that the current position is sane and, if not, modify the position to make it so.
// Check for being below terrain and being out of bounds. // Check for being below terrain and being out of bounds.
// Returns 'true' of the position was made sane by some action. // Returns 'true' of the position was made sane by some action.
private bool PositionSanityCheck() private bool PositionSanityCheck(bool inTaintTime)
{ {
bool ret = false; bool ret = false;
// If totally below the ground, move the prim up
// TODO: figure out the right solution for this... only for dynamic objects?
/*
float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
OMV.Vector3 upForce = OMV.Vector3.Zero;
if (Position.Z < terrainHeight) if (Position.Z < terrainHeight)
{ {
DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
_position.Z = terrainHeight + 2.0f; float targetHeight = terrainHeight + (Size.Z / 2f);
// Upforce proportional to the distance away from the terrain. Correct the error in 1 sec.
upForce.Z = (terrainHeight - Position.Z) * 1f;
ret = true; ret = true;
} }
*/
if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
{ {
float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position);
// TODO: a floating motor so object will bob in the water // TODO: a floating motor so object will bob in the water
if (Position.Z < waterHeight) if (Math.Abs(Position.Z - waterHeight) > 0.1f)
{ {
_position.Z = waterHeight; // Upforce proportional to the distance away from the water. Correct the error in 1 sec.
upForce.Z = (waterHeight - Position.Z) * 1f;
ret = true; ret = true;
} }
} }
// TODO: check for out of bounds // TODO: check for out of bounds
return ret;
}
// A version of the sanity check that also makes sure a new position value is // The above code computes a force to apply to correct any out-of-bounds problems. Apply same.
// pushed to the physics engine. This routine would be used by anyone // TODO: This should be intergrated with a geneal physics action mechanism.
// who is not already pushing the value. // TODO: This should be moderated with PID'ness.
private bool PositionSanityCheck(bool inTaintTime) if (ret)
{
bool ret = false;
if (PositionSanityCheck())
{ {
// The new position value must be pushed into the physics engine but we can't // Apply upforce and overcome gravity.
// just assign to "Position" because of potential call loops. AddForce(upForce - PhysicsScene.DefaultGravity, false, inTaintTime);
PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck", delegate()
{
DetailLog("{0},BSPrim.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
ForcePosition = _position;
});
ret = true;
} }
return ret; return ret;
} }
@ -940,6 +931,7 @@ public sealed class BSPrim : BSPhysObject
public override void AddForce(OMV.Vector3 force, bool pushforce) { public override void AddForce(OMV.Vector3 force, bool pushforce) {
AddForce(force, pushforce, false); AddForce(force, pushforce, false);
} }
// Applying a force just adds this to the total force on the object.
public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
// for an object, doesn't matter if force is a pushforce or not // for an object, doesn't matter if force is a pushforce or not
if (force.IsFinite()) if (force.IsFinite())
@ -971,6 +963,7 @@ public sealed class BSPrim : BSPhysObject
}); });
} }
// An impulse force is scaled by the mass of the object.
public void ApplyForceImpulse(OMV.Vector3 impulse, bool inTaintTime) public void ApplyForceImpulse(OMV.Vector3 impulse, bool inTaintTime)
{ {
OMV.Vector3 applyImpulse = impulse; OMV.Vector3 applyImpulse = impulse;
@ -1387,67 +1380,34 @@ public sealed class BSPrim : BSPhysObject
public override void UpdateProperties(EntityProperties entprop) public override void UpdateProperties(EntityProperties entprop)
{ {
/*
UpdatedProperties changed = 0;
// assign to the local variables so the normal set action does not happen
// if (_position != entprop.Position)
if (!_position.ApproxEquals(entprop.Position, POSITION_TOLERANCE))
{
_position = entprop.Position;
changed |= UpdatedProperties.Position;
}
// if (_orientation != entprop.Rotation)
if (!_orientation.ApproxEquals(entprop.Rotation, ROTATION_TOLERANCE))
{
_orientation = entprop.Rotation;
changed |= UpdatedProperties.Rotation;
}
// if (_velocity != entprop.Velocity)
if (!_velocity.ApproxEquals(entprop.Velocity, VELOCITY_TOLERANCE))
{
_velocity = entprop.Velocity;
changed |= UpdatedProperties.Velocity;
}
// if (_acceleration != entprop.Acceleration)
if (!_acceleration.ApproxEquals(entprop.Acceleration, ACCELERATION_TOLERANCE))
{
_acceleration = entprop.Acceleration;
changed |= UpdatedProperties.Acceleration;
}
// if (_rotationalVelocity != entprop.RotationalVelocity)
if (!_rotationalVelocity.ApproxEquals(entprop.RotationalVelocity, ROTATIONAL_VELOCITY_TOLERANCE))
{
_rotationalVelocity = entprop.RotationalVelocity;
changed |= UpdatedProperties.RotationalVel;
}
if (changed != 0)
{
// Only update the position of single objects and linkset roots
if (this._parentPrim == null)
{
base.RequestPhysicsterseUpdate();
}
}
*/
// Don't check for damping here -- it's done in BulletSim and SceneObjectPart.
// Updates only for individual prims and for the root object of a linkset. // Updates only for individual prims and for the root object of a linkset.
if (Linkset.IsRoot(this)) if (Linkset.IsRoot(this))
{ {
// Assign to the local variables so the normal set action does not happen // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet
// TODO: handle physics introduced by Bullet with computed vehicle physics.
if (_vehicle.IsActive)
{
entprop.RotationalVelocity = OMV.Vector3.Zero;
}
// Assign directly to the local variables so the normal set action does not happen
_position = entprop.Position; _position = entprop.Position;
_orientation = entprop.Rotation; _orientation = entprop.Rotation;
_velocity = entprop.Velocity; _velocity = entprop.Velocity;
_acceleration = entprop.Acceleration; _acceleration = entprop.Acceleration;
_rotationalVelocity = entprop.RotationalVelocity; _rotationalVelocity = entprop.RotationalVelocity;
// The sanity check can change the velocity and/or position.
if (PositionSanityCheck(true))
{
entprop.Position = _position;
entprop.Velocity = _velocity;
}
// remember the current and last set values // remember the current and last set values
LastEntityProperties = CurrentEntityProperties; LastEntityProperties = CurrentEntityProperties;
CurrentEntityProperties = entprop; CurrentEntityProperties = entprop;
PositionSanityCheck(true);
OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation;
DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}", DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity); LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);

View File

@ -39,23 +39,10 @@ using log4net;
using OpenMetaverse; using OpenMetaverse;
// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
// Test sculpties (verified that they don't work)
// Compute physics FPS reasonably
// Based on material, set density and friction // Based on material, set density and friction
// Don't use constraints in linksets of non-physical objects. Means having to move children manually.
// Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly?
// In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground)
// At the moment, physical and phantom causes object to drop through the terrain
// Physical phantom objects and related typing (collision options )
// Check out llVolumeDetect. Must do something for that.
// Use collision masks for collision with terrain and phantom objects
// More efficient memory usage when passing hull information from BSPrim to BulletSim // More efficient memory usage when passing hull information from BSPrim to BulletSim
// Should prim.link() and prim.delink() membership checking happen at taint time?
// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once.
// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect // Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
// Implement LockAngularMotion // Implement LockAngularMotion
// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
// Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet.
// Add PID movement operations. What does ScenePresence.MoveToTarget do? // Add PID movement operations. What does ScenePresence.MoveToTarget do?
// Check terrain size. 128 or 127? // Check terrain size. 128 or 127?
// Raycast // Raycast
@ -234,6 +221,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
if (m_physicsLoggingEnabled) if (m_physicsLoggingEnabled)
{ {
PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages.
} }
else else
{ {
@ -308,6 +296,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// Do any replacements in the parameters // Do any replacements in the parameters
m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
} }
// The material characteristics.
BSMaterials.InitializeFromDefaults(Params);
if (pConfig != null)
{
BSMaterials.InitializefromParameters(pConfig);
}
} }
} }
@ -520,9 +515,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
collidersCount = 0; collidersCount = 0;
} }
// Don't have to use the pointers passed back since we know it is the same pinned memory we passed in // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in.
// Get a value for 'now' so all the collision and update routines don't have to get their own // Get a value for 'now' so all the collision and update routines don't have to get their own.
SimulationNowTime = Util.EnvironmentTickCount(); SimulationNowTime = Util.EnvironmentTickCount();
// If there were collisions, process them by sending the event to the prim. // If there were collisions, process them by sending the event to the prim.
@ -568,6 +563,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
ObjectsWithCollisions.Remove(po); ObjectsWithCollisions.Remove(po);
ObjectsWithNoMoreCollisions.Clear(); ObjectsWithNoMoreCollisions.Clear();
} }
// Done with collisions.
// If any of the objects had updated properties, tell the object it has been changed by the physics engine // If any of the objects had updated properties, tell the object it has been changed by the physics engine
if (updatedEntityCount > 0) if (updatedEntityCount > 0)
@ -591,9 +587,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// The physics engine returns the number of milliseconds it simulated this call. // The physics engine returns the number of milliseconds it simulated this call.
// These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
// We multiply by 55 to give a recognizable running rate (55 or less). // Multiply by 55 to give a nominal frame rate of 55.
return numSubSteps * m_fixedTimeStep * 1000 * 55; return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f;
// return timeStep * 1000 * 55;
} }
// Something has collided // Something has collided
@ -683,7 +678,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
#region Taints #region Taints
// Calls to the PhysicsActors can't directly call into the physics engine // Calls to the PhysicsActors can't directly call into the physics engine
// because it might be busy. We delay changes to a known time. // because it might be busy. We delay changes to a known time.
// We rely on C#'s closure to save and restore the context for the delegate. // We rely on C#'s closure to save and restore the context for the delegate.
public void TaintedObject(String ident, TaintCallback callback) public void TaintedObject(String ident, TaintCallback callback)
{ {
@ -712,7 +707,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// here just before the physics engine is called to step the simulation. // here just before the physics engine is called to step the simulation.
public void ProcessTaints() public void ProcessTaints()
{ {
InTaintTime = true; InTaintTime = true; // Only used for debugging so locking is not necessary.
ProcessRegularTaints(); ProcessRegularTaints();
ProcessPostTaintTaints(); ProcessPostTaintTaints();
InTaintTime = false; InTaintTime = false;
@ -758,6 +753,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
DetailLog("{0},BSScene.ProcessTaints,leftTaintsOnList,numNotProcessed={1}", DetailLogZero, _taintOperations.Count); DetailLog("{0},BSScene.ProcessTaints,leftTaintsOnList,numNotProcessed={1}", DetailLogZero, _taintOperations.Count);
} }
*/ */
// swizzle a new list into the list location so we can process what's there // swizzle a new list into the list location so we can process what's there
List<TaintCallbackEntry> oldList; List<TaintCallbackEntry> oldList;
lock (_taintLock) lock (_taintLock)
@ -787,8 +783,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// will replace any previous operation by the same object. // will replace any previous operation by the same object.
public void PostTaintObject(String ident, uint ID, TaintCallback callback) public void PostTaintObject(String ident, uint ID, TaintCallback callback)
{ {
if (!m_initialized) return;
string uniqueIdent = ident + "-" + ID.ToString(); string uniqueIdent = ident + "-" + ID.ToString();
lock (_taintLock) lock (_taintLock)
{ {
@ -864,13 +858,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
} }
} }
// Only used for debugging. Does not change state of anything so locking is not necessary.
public bool AssertInTaintTime(string whereFrom) public bool AssertInTaintTime(string whereFrom)
{ {
if (!InTaintTime) if (!InTaintTime)
{ {
DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
Util.PrintCallStack(); Util.PrintCallStack(); // Prints the stack into the DEBUG log file.
} }
return InTaintTime; return InTaintTime;
} }
@ -1069,7 +1064,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
(s,p,l,v) => { s.PID_P = v; } ), (s,p,l,v) => { s.PID_P = v; } ),
new ParameterDefn("DefaultFriction", "Friction factor used on new objects", new ParameterDefn("DefaultFriction", "Friction factor used on new objects",
0.5f, 0.2f,
(s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].defaultFriction; }, (s) => { return s.m_params[0].defaultFriction; },
(s,p,l,v) => { s.m_params[0].defaultFriction = v; } ), (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ),
@ -1084,7 +1079,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
(s) => { return s.m_params[0].defaultRestitution; }, (s) => { return s.m_params[0].defaultRestitution; },
(s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ), (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ),
new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)", new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)",
0f, 0.04f,
(s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].collisionMargin; }, (s) => { return s.m_params[0].collisionMargin; },
(s,p,l,v) => { s.m_params[0].collisionMargin = v; } ), (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ),
@ -1145,8 +1140,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ), (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ),
new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)",
(float)BSTerrainPhys.TerrainImplementation.Mesh,
(s,cf,p,v) => { s.m_params[0].terrainImplementation = cf.GetFloat(p,v); },
(s) => { return s.m_params[0].terrainImplementation; },
(s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ),
new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
0.5f, 0.3f,
(s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].terrainFriction; }, (s) => { return s.m_params[0].terrainFriction; },
(s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ), (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ),
@ -1160,13 +1160,19 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
(s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].terrainRestitution; }, (s) => { return s.m_params[0].terrainRestitution; },
(s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ), (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ),
new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" ,
0.04f,
(s,cf,p,v) => { s.m_params[0].terrainCollisionMargin = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].terrainCollisionMargin; },
(s,p,l,v) => { s.m_params[0].terrainCollisionMargin = v; /* TODO: set on real terrain */ } ),
new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
0.2f, 0.2f,
(s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].avatarFriction; }, (s) => { return s.m_params[0].avatarFriction; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ),
new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.", new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
10f, 10.0f,
(s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].avatarStandingFriction; }, (s) => { return s.m_params[0].avatarStandingFriction; },
(s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ), (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ),
@ -1180,11 +1186,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
(s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].avatarRestitution; }, (s) => { return s.m_params[0].avatarRestitution; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ),
new ParameterDefn("AvatarCapsuleRadius", "Radius of space around an avatar", new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule",
0.37f, 0.6f,
(s,cf,p,v) => { s.m_params[0].avatarCapsuleRadius = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].avatarCapsuleWidth = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].avatarCapsuleRadius; }, (s) => { return s.m_params[0].avatarCapsuleWidth; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleRadius, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleWidth, p, l, v); } ),
new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule",
0.45f,
(s,cf,p,v) => { s.m_params[0].avatarCapsuleDepth = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].avatarCapsuleDepth; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleDepth, p, l, v); } ),
new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar",
1.5f, 1.5f,
(s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); },
@ -1196,6 +1207,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
(s) => { return s.m_params[0].avatarContactProcessingThreshold; }, (s) => { return s.m_params[0].avatarContactProcessingThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ),
new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)",
0.95f,
(s,cf,p,v) => { s.m_params[0].vehicleAngularDamping = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].vehicleAngularDamping; },
(s,p,l,v) => { s.m_params[0].vehicleAngularDamping = v; } ),
new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
0f, 0f,

View File

@ -178,7 +178,7 @@ public sealed class BSShapeCollection : IDisposable
bool ret = false; bool ret = false;
switch (shape.type) switch (shape.type)
{ {
case ShapeData.PhysicsShapeType.SHAPE_MESH: case BSPhysicsShapeType.SHAPE_MESH:
MeshDesc meshDesc; MeshDesc meshDesc;
if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
{ {
@ -201,7 +201,7 @@ public sealed class BSShapeCollection : IDisposable
meshDesc.lastReferenced = System.DateTime.Now; meshDesc.lastReferenced = System.DateTime.Now;
Meshes[shape.shapeKey] = meshDesc; Meshes[shape.shapeKey] = meshDesc;
break; break;
case ShapeData.PhysicsShapeType.SHAPE_HULL: case BSPhysicsShapeType.SHAPE_HULL:
HullDesc hullDesc; HullDesc hullDesc;
if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
{ {
@ -224,7 +224,7 @@ public sealed class BSShapeCollection : IDisposable
hullDesc.lastReferenced = System.DateTime.Now; hullDesc.lastReferenced = System.DateTime.Now;
Hulls[shape.shapeKey] = hullDesc; Hulls[shape.shapeKey] = hullDesc;
break; break;
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: case BSPhysicsShapeType.SHAPE_UNKNOWN:
break; break;
default: default:
// Native shapes are not tracked and they don't go into any list // Native shapes are not tracked and they don't go into any list
@ -255,16 +255,16 @@ public sealed class BSShapeCollection : IDisposable
{ {
switch (shape.type) switch (shape.type)
{ {
case ShapeData.PhysicsShapeType.SHAPE_HULL: case BSPhysicsShapeType.SHAPE_HULL:
DereferenceHull(shape, shapeCallback); DereferenceHull(shape, shapeCallback);
break; break;
case ShapeData.PhysicsShapeType.SHAPE_MESH: case BSPhysicsShapeType.SHAPE_MESH:
DereferenceMesh(shape, shapeCallback); DereferenceMesh(shape, shapeCallback);
break; break;
case ShapeData.PhysicsShapeType.SHAPE_COMPOUND: case BSPhysicsShapeType.SHAPE_COMPOUND:
DereferenceCompound(shape, shapeCallback); DereferenceCompound(shape, shapeCallback);
break; break;
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: case BSPhysicsShapeType.SHAPE_UNKNOWN:
break; break;
default: default:
break; break;
@ -352,28 +352,28 @@ public sealed class BSShapeCollection : IDisposable
BulletShape shapeInfo = new BulletShape(cShape); BulletShape shapeInfo = new BulletShape(cShape);
if (TryGetMeshByPtr(cShape, out meshDesc)) if (TryGetMeshByPtr(cShape, out meshDesc))
{ {
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_MESH; shapeInfo.type = BSPhysicsShapeType.SHAPE_MESH;
shapeInfo.shapeKey = meshDesc.shapeKey; shapeInfo.shapeKey = meshDesc.shapeKey;
} }
else else
{ {
if (TryGetHullByPtr(cShape, out hullDesc)) if (TryGetHullByPtr(cShape, out hullDesc))
{ {
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_HULL; shapeInfo.type = BSPhysicsShapeType.SHAPE_HULL;
shapeInfo.shapeKey = hullDesc.shapeKey; shapeInfo.shapeKey = hullDesc.shapeKey;
} }
else else
{ {
if (BulletSimAPI.IsCompound2(cShape)) if (BulletSimAPI.IsCompound2(cShape))
{ {
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_COMPOUND; shapeInfo.type = BSPhysicsShapeType.SHAPE_COMPOUND;
} }
else else
{ {
if (BulletSimAPI.IsNativeShape2(cShape)) if (BulletSimAPI.IsNativeShape2(cShape))
{ {
shapeInfo.isNativeShape = true; shapeInfo.isNativeShape = true;
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter) shapeInfo.type = BSPhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter)
} }
} }
} }
@ -381,7 +381,7 @@ public sealed class BSShapeCollection : IDisposable
DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo); DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
if (shapeInfo.type != ShapeData.PhysicsShapeType.SHAPE_UNKNOWN) if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN)
{ {
DereferenceShape(shapeInfo, true, null); DereferenceShape(shapeInfo, true, null);
} }
@ -405,11 +405,11 @@ public sealed class BSShapeCollection : IDisposable
bool ret = false; bool ret = false;
bool haveShape = false; bool haveShape = false;
if (!haveShape && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_AVATAR) if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
{ {
// an avatar capsule is close to a native shape (it is not shared) // an avatar capsule is close to a native shape (it is not shared)
ret = GetReferenceToNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_AVATAR, ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE,
ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback); FixedShapeKey.KEY_CAPSULE, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape); DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
ret = true; ret = true;
haveShape = true; haveShape = true;
@ -417,7 +417,7 @@ public sealed class BSShapeCollection : IDisposable
// Compound shapes are handled special as they are rebuilt from scratch. // Compound shapes are handled special as they are rebuilt from scratch.
// This isn't too great a hardship since most of the child shapes will already been created. // This isn't too great a hardship since most of the child shapes will already been created.
if (!haveShape && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_COMPOUND) if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
{ {
ret = GetReferenceToCompoundShape(prim, shapeCallback); ret = GetReferenceToCompoundShape(prim, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape); DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
@ -460,11 +460,11 @@ public sealed class BSShapeCollection : IDisposable
haveShape = true; haveShape = true;
if (forceRebuild if (forceRebuild
|| prim.Scale != prim.Size || prim.Scale != prim.Size
|| prim.PhysShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE
) )
{ {
ret = GetReferenceToNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_SPHERE, ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE,
ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback); FixedShapeKey.KEY_SPHERE, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
prim.LocalID, forceRebuild, prim.PhysShape); prim.LocalID, forceRebuild, prim.PhysShape);
} }
@ -474,11 +474,11 @@ public sealed class BSShapeCollection : IDisposable
haveShape = true; haveShape = true;
if (forceRebuild if (forceRebuild
|| prim.Scale != prim.Size || prim.Scale != prim.Size
|| prim.PhysShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX
) )
{ {
ret = GetReferenceToNativeShape( prim, ShapeData.PhysicsShapeType.SHAPE_BOX, ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX,
ShapeData.FixedShapeKey.KEY_BOX, shapeCallback); FixedShapeKey.KEY_BOX, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
prim.LocalID, forceRebuild, prim.PhysShape); prim.LocalID, forceRebuild, prim.PhysShape);
} }
@ -519,15 +519,12 @@ public sealed class BSShapeCollection : IDisposable
// Creates a native shape and assignes it to prim.BSShape. // Creates a native shape and assignes it to prim.BSShape.
// "Native" shapes are never shared. they are created here and destroyed in DereferenceShape(). // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
private bool GetReferenceToNativeShape(BSPhysObject prim, private bool GetReferenceToNativeShape(BSPhysObject prim,
ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey, BSPhysicsShapeType shapeType, FixedShapeKey shapeKey,
ShapeDestructionCallback shapeCallback) ShapeDestructionCallback shapeCallback)
{ {
// release any previous shape // release any previous shape
DereferenceShape(prim.PhysShape, true, shapeCallback); DereferenceShape(prim.PhysShape, true, shapeCallback);
// Bullet native objects are scaled by the Bullet engine so pass the size in
prim.Scale = prim.Size;
BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey); BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey);
// Don't need to do a 'ReferenceShape()' here because native shapes are not shared. // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
@ -538,8 +535,8 @@ public sealed class BSShapeCollection : IDisposable
return true; return true;
} }
private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, ShapeData.PhysicsShapeType shapeType, private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, BSPhysicsShapeType shapeType,
ShapeData.FixedShapeKey shapeKey) FixedShapeKey shapeKey)
{ {
BulletShape newShape; BulletShape newShape;
// Need to make sure the passed shape information is for the native type. // Need to make sure the passed shape information is for the native type.
@ -547,12 +544,13 @@ public sealed class BSShapeCollection : IDisposable
nativeShapeData.Type = shapeType; nativeShapeData.Type = shapeType;
nativeShapeData.ID = prim.LocalID; nativeShapeData.ID = prim.LocalID;
nativeShapeData.Scale = prim.Scale; nativeShapeData.Scale = prim.Scale;
nativeShapeData.Size = prim.Scale; nativeShapeData.Size = prim.Scale; // unneeded, I think.
nativeShapeData.MeshKey = (ulong)shapeKey; nativeShapeData.MeshKey = (ulong)shapeKey;
nativeShapeData.HullKey = (ulong)shapeKey; nativeShapeData.HullKey = (ulong)shapeKey;
if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR) if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
{ {
// The proper scale has been calculated in the prim.
newShape = new BulletShape( newShape = new BulletShape(
BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale) BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
, shapeType); , shapeType);
@ -560,6 +558,9 @@ public sealed class BSShapeCollection : IDisposable
} }
else else
{ {
// Native shapes are scaled in Bullet so set the scaling to the size
prim.Scale = prim.Size;
nativeShapeData.Scale = prim.Scale;
newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType); newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType);
} }
if (newShape.ptr == IntPtr.Zero) if (newShape.ptr == IntPtr.Zero)
@ -585,7 +586,7 @@ public sealed class BSShapeCollection : IDisposable
System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
// if this new shape is the same as last time, don't recreate the mesh // if this new shape is the same as last time, don't recreate the mesh
if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH) if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH)
return false; return false;
DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}", DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
@ -643,7 +644,7 @@ public sealed class BSShapeCollection : IDisposable
indices.GetLength(0), indices, vertices.Count, verticesAsFloats); indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
} }
} }
BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH); BulletShape newShape = new BulletShape(meshPtr, BSPhysicsShapeType.SHAPE_MESH);
newShape.shapeKey = newMeshKey; newShape.shapeKey = newMeshKey;
return newShape; return newShape;
@ -659,7 +660,7 @@ public sealed class BSShapeCollection : IDisposable
System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
// if the hull hasn't changed, don't rebuild it // if the hull hasn't changed, don't rebuild it
if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL) if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL)
return false; return false;
DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}", DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
@ -780,7 +781,7 @@ public sealed class BSShapeCollection : IDisposable
} }
} }
BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); BulletShape newShape = new BulletShape(hullPtr, BSPhysicsShapeType.SHAPE_HULL);
newShape.shapeKey = newHullKey; newShape.shapeKey = newHullKey;
return newShape; // 'true' means a new shape has been added to this prim return newShape; // 'true' means a new shape has been added to this prim
@ -803,7 +804,7 @@ public sealed class BSShapeCollection : IDisposable
// DereferenceShape(prim.PhysShape, true, shapeCallback); // DereferenceShape(prim.PhysShape, true, shapeCallback);
BulletShape cShape = new BulletShape( BulletShape cShape = new BulletShape(
BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), ShapeData.PhysicsShapeType.SHAPE_COMPOUND); BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), BSPhysicsShapeType.SHAPE_COMPOUND);
// Create the shape for the root prim and add it to the compound shape. Cannot be a native shape. // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
CreateGeomMeshOrHull(prim, shapeCallback); CreateGeomMeshOrHull(prim, shapeCallback);
@ -894,7 +895,7 @@ public sealed class BSShapeCollection : IDisposable
// While we figure out the real problem, stick a simple native shape on the object. // While we figure out the real problem, stick a simple native shape on the object.
BulletShape fillinShape = BulletShape fillinShape =
BuildPhysicalNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_BOX, ShapeData.FixedShapeKey.KEY_BOX); BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
return fillinShape; return fillinShape;
} }
@ -940,7 +941,7 @@ public sealed class BSShapeCollection : IDisposable
else else
{ {
bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
prim.LocalID, prim.ForcePosition, prim.ForceOrientation); prim.LocalID, prim.RawPosition, prim.RawOrientation);
DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
} }
aBody = new BulletBody(prim.LocalID, bodyPtr); aBody = new BulletBody(prim.LocalID, bodyPtr);

View File

@ -1,213 +1,208 @@
/* /*
* Copyright (c) Contributors, http://opensimulator.org/ * Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders. * See CONTRIBUTORS.TXT for a full list of copyright holders.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyrightD * * Redistributions in binary form must reproduce the above copyrightD
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the * * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * 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 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public abstract class BSShape public abstract class BSShape
{ {
public IntPtr ptr { get; set; } public IntPtr ptr { get; set; }
public ShapeData.PhysicsShapeType type { get; set; } public BSPhysicsShapeType type { get; set; }
public System.UInt64 key { get; set; } public System.UInt64 key { get; set; }
public int referenceCount { get; set; } public int referenceCount { get; set; }
public DateTime lastReferenced { get; set; } public DateTime lastReferenced { get; set; }
protected void Initialize() public BSShape()
{ {
ptr = IntPtr.Zero; ptr = IntPtr.Zero;
type = ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; type = BSPhysicsShapeType.SHAPE_UNKNOWN;
key = 0; key = 0;
referenceCount = 0; referenceCount = 0;
lastReferenced = DateTime.Now; lastReferenced = DateTime.Now;
} }
// Get a reference to a physical shape. Create if it doesn't exist // Get a reference to a physical shape. Create if it doesn't exist
public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
{ {
BSShape ret = null; BSShape ret = null;
if (prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_AVATAR) if (prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
{ {
// an avatar capsule is close to a native shape (it is not shared) // an avatar capsule is close to a native shape (it is not shared)
ret = BSShapeNative.GetReference(physicsScene, prim, ShapeData.PhysicsShapeType.SHAPE_AVATAR, ret = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_CAPSULE,
ShapeData.FixedShapeKey.KEY_CAPSULE); FixedShapeKey.KEY_CAPSULE);
physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret); physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret);
} }
// Compound shapes are handled special as they are rebuilt from scratch. // Compound shapes are handled special as they are rebuilt from scratch.
// This isn't too great a hardship since most of the child shapes will already been created. // This isn't too great a hardship since most of the child shapes will already been created.
if (ret == null && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_COMPOUND) if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
{ {
// Getting a reference to a compound shape gets you the compound shape with the root prim shape added // Getting a reference to a compound shape gets you the compound shape with the root prim shape added
ret = BSShapeCompound.GetReference(prim); ret = BSShapeCompound.GetReference(prim);
physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret); physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret);
} }
if (ret == null) if (ret == null)
ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim); ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim);
return ret; return ret;
} }
public static BSShape GetShapeReferenceNonSpecial(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) public static BSShape GetShapeReferenceNonSpecial(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
{ {
return null; return null;
} }
public static BSShape GetShapeReferenceNonNative(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) public static BSShape GetShapeReferenceNonNative(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
{ {
return null; return null;
} }
// Release the use of a physical shape. // Release the use of a physical shape.
public abstract void Dereference(BSScene physicsScene); public abstract void Dereference(BSScene physicsScene);
// All shapes have a static call to get a reference to the physical shape // All shapes have a static call to get a reference to the physical shape
// protected abstract static BSShape GetReference(); // protected abstract static BSShape GetReference();
public override string ToString() public override string ToString()
{ {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
buff.Append("<p="); buff.Append("<p=");
buff.Append(ptr.ToString("X")); buff.Append(ptr.ToString("X"));
buff.Append(",s="); buff.Append(",s=");
buff.Append(type.ToString()); buff.Append(type.ToString());
buff.Append(",k="); buff.Append(",k=");
buff.Append(key.ToString("X")); buff.Append(key.ToString("X"));
buff.Append(",c="); buff.Append(",c=");
buff.Append(referenceCount.ToString()); buff.Append(referenceCount.ToString());
buff.Append(">"); buff.Append(">");
return buff.ToString(); return buff.ToString();
} }
} }
public class BSShapeNull : BSShape public class BSShapeNull : BSShape
{ {
public BSShapeNull() public BSShapeNull() : base()
{ {
base.Initialize(); }
} public static BSShape GetReference() { return new BSShapeNull(); }
public static BSShape GetReference() { return new BSShapeNull(); } public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ }
public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ } }
}
public class BSShapeNative : BSShape
public class BSShapeNative : BSShape {
{ private static string LogHeader = "[BULLETSIM SHAPE NATIVE]";
private static string LogHeader = "[BULLETSIM SHAPE NATIVE]"; public BSShapeNative() : base()
public BSShapeNative() {
{ }
base.Initialize(); public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim,
} BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim, {
ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey) // Native shapes are not shared and are always built anew.
{ return new BSShapeNative(physicsScene, prim, shapeType, shapeKey);
// Native shapes are not shared and are always built anew. }
return new BSShapeNative(physicsScene, prim, shapeType, shapeKey);
} private BSShapeNative(BSScene physicsScene, BSPhysObject prim,
BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
private BSShapeNative(BSScene physicsScene, BSPhysObject prim, {
ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey) ShapeData nativeShapeData = new ShapeData();
{ nativeShapeData.Type = shapeType;
ShapeData nativeShapeData = new ShapeData(); nativeShapeData.ID = prim.LocalID;
nativeShapeData.Type = shapeType; nativeShapeData.Scale = prim.Scale;
nativeShapeData.ID = prim.LocalID; nativeShapeData.Size = prim.Scale;
nativeShapeData.Scale = prim.Scale; nativeShapeData.MeshKey = (ulong)shapeKey;
nativeShapeData.Size = prim.Scale; nativeShapeData.HullKey = (ulong)shapeKey;
nativeShapeData.MeshKey = (ulong)shapeKey;
nativeShapeData.HullKey = (ulong)shapeKey;
if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
{
if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR) ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale);
{ physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale); }
physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); else
} {
else ptr = BulletSimAPI.BuildNativeShape2(physicsScene.World.ptr, nativeShapeData);
{ }
ptr = BulletSimAPI.BuildNativeShape2(physicsScene.World.ptr, nativeShapeData); if (ptr == IntPtr.Zero)
} {
if (ptr == IntPtr.Zero) physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
{ LogHeader, prim.LocalID, shapeType);
physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", }
LogHeader, prim.LocalID, shapeType); type = shapeType;
} key = (UInt64)shapeKey;
type = shapeType; }
key = (UInt64)shapeKey; // Make this reference to the physical shape go away since native shapes are not shared.
} public override void Dereference(BSScene physicsScene)
// Make this reference to the physical shape go away since native shapes are not shared. {
public override void Dereference(BSScene physicsScene) // Native shapes are not tracked and are released immediately
{ physicsScene.DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this);
// Native shapes are not tracked and are released immediately BulletSimAPI.DeleteCollisionShape2(physicsScene.World.ptr, ptr);
physicsScene.DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this); ptr = IntPtr.Zero;
BulletSimAPI.DeleteCollisionShape2(physicsScene.World.ptr, ptr); // Garbage collection will free up this instance.
ptr = IntPtr.Zero; }
// Garbage collection will free up this instance. }
}
} public class BSShapeMesh : BSShape
{
public class BSShapeMesh : BSShape private static string LogHeader = "[BULLETSIM SHAPE MESH]";
{ private static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>();
private static string LogHeader = "[BULLETSIM SHAPE MESH]";
private static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>(); public BSShapeMesh() : base()
{
public BSShapeMesh() }
{ public static BSShape GetReference() { return new BSShapeNull(); }
base.Initialize(); public override void Dereference(BSScene physicsScene) { }
} }
public static BSShape GetReference() { return new BSShapeNull(); }
public override void Dereference(BSScene physicsScene) { } public class BSShapeHull : BSShape
} {
private static string LogHeader = "[BULLETSIM SHAPE HULL]";
public class BSShapeHull : BSShape private static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>();
{
private static string LogHeader = "[BULLETSIM SHAPE HULL]"; public BSShapeHull() : base()
private static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>(); {
}
public BSShapeHull() public static BSShape GetReference() { return new BSShapeNull(); }
{ public override void Dereference(BSScene physicsScene) { }
base.Initialize(); }
}
public static BSShape GetReference() { return new BSShapeNull(); } public class BSShapeCompound : BSShape
public override void Dereference(BSScene physicsScene) { } {
} private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]";
public BSShapeCompound() : base()
public class BSShapeCompound : BSShape {
{ }
private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]"; public static BSShape GetReference(BSPhysObject prim)
public BSShapeCompound() {
{ return new BSShapeNull();
base.Initialize(); }
} public override void Dereference(BSScene physicsScene) { }
public static BSShape GetReference(BSPhysObject prim) }
{ }
return new BSShapeNull();
}
public override void Dereference(BSScene physicsScene) { }
}
}

View File

@ -0,0 +1,170 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyrightD
* 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.Collections.Generic;
using System.Text;
using OpenSim.Framework;
using OpenSim.Region.Framework;
using OpenSim.Region.CoreModules;
using OpenSim.Region.Physics.Manager;
using Nini.Config;
using log4net;
using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public sealed class BSTerrainHeightmap : BSTerrainPhys
{
static string LogHeader = "[BULLETSIM TERRAIN HEIGHTMAP]";
BulletHeightMapInfo m_mapInfo = null;
// Constructor to build a default, flat heightmap terrain.
public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
: base(physicsScene, regionBase, id)
{
Vector3 minTerrainCoords = new Vector3(0f, 0f, BSTerrainManager.HEIGHT_INITIALIZATION - BSTerrainManager.HEIGHT_EQUAL_FUDGE);
Vector3 maxTerrainCoords = new Vector3(regionSize.X, regionSize.Y, BSTerrainManager.HEIGHT_INITIALIZATION);
int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y;
float[] initialMap = new float[totalHeights];
for (int ii = 0; ii < totalHeights; ii++)
{
initialMap[ii] = BSTerrainManager.HEIGHT_INITIALIZATION;
}
m_mapInfo = new BulletHeightMapInfo(id, initialMap, IntPtr.Zero);
m_mapInfo.minCoords = minTerrainCoords;
m_mapInfo.maxCoords = maxTerrainCoords;
m_mapInfo.terrainRegionBase = TerrainBase;
// Don't have to free any previous since we just got here.
BuildHeightmapTerrain();
}
// This minCoords and maxCoords passed in give the size of the terrain (min and max Z
// are the high and low points of the heightmap).
public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
Vector3 minCoords, Vector3 maxCoords)
: base(physicsScene, regionBase, id)
{
m_mapInfo = new BulletHeightMapInfo(id, initialMap, IntPtr.Zero);
m_mapInfo.minCoords = minCoords;
m_mapInfo.maxCoords = maxCoords;
m_mapInfo.minZ = minCoords.Z;
m_mapInfo.maxZ = maxCoords.Z;
m_mapInfo.terrainRegionBase = TerrainBase;
// Don't have to free any previous since we just got here.
BuildHeightmapTerrain();
}
public override void Dispose()
{
ReleaseHeightMapTerrain();
}
// Using the information in m_mapInfo, create the physical representation of the heightmap.
private void BuildHeightmapTerrain()
{
m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID,
m_mapInfo.minCoords, m_mapInfo.maxCoords,
m_mapInfo.heightMap, PhysicsScene.Params.terrainCollisionMargin);
// Create the terrain shape from the mapInfo
m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr),
BSPhysicsShapeType.SHAPE_TERRAIN);
// The terrain object initial position is at the center of the object
Vector3 centerPos;
centerPos.X = m_mapInfo.minCoords.X + (m_mapInfo.sizeX / 2f);
centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f);
centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f);
m_mapInfo.terrainBody = new BulletBody(m_mapInfo.ID,
BulletSimAPI.CreateBodyWithDefaultMotionState2(m_mapInfo.terrainShape.ptr,
m_mapInfo.ID, centerPos, Quaternion.Identity));
// Set current terrain attributes
BulletSimAPI.SetFriction2(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainFriction);
BulletSimAPI.SetHitFraction2(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
BulletSimAPI.SetRestitution2(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
BulletSimAPI.SetCollisionFlags2(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
// Return the new terrain to the world of physical objects
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
// redo its bounding box now that it is in the world
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
BulletSimAPI.SetCollisionFilterMask2(m_mapInfo.terrainBody.ptr,
(uint)CollisionFilterGroups.TerrainFilter,
(uint)CollisionFilterGroups.TerrainMask);
// Make it so the terrain will not move or be considered for movement.
BulletSimAPI.ForceActivationState2(m_mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
return;
}
// If there is information in m_mapInfo pointing to physical structures, release same.
private void ReleaseHeightMapTerrain()
{
if (m_mapInfo != null)
{
if (m_mapInfo.terrainBody.ptr != IntPtr.Zero)
{
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
// Frees both the body and the shape.
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
BulletSimAPI.ReleaseHeightMapInfo2(m_mapInfo.Ptr);
}
}
m_mapInfo = null;
}
// The passed position is relative to the base of the region.
public override float GetHeightAtXYZ(Vector3 pos)
{
float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
int mapIndex = (int)pos.Y * (int)m_mapInfo.sizeY + (int)pos.X;
try
{
ret = m_mapInfo.heightMap[mapIndex];
}
catch
{
// Sometimes they give us wonky values of X and Y. Give a warning and return something.
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
LogHeader, m_mapInfo.terrainRegionBase, pos);
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
}
return ret;
}
}
}

View File

@ -40,6 +40,32 @@ using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
// The physical implementation of the terrain is wrapped in this class.
public abstract class BSTerrainPhys : IDisposable
{
public enum TerrainImplementation
{
Heightmap = 0,
Mesh = 1
}
public BSScene PhysicsScene { get; private set; }
// Base of the region in world coordinates. Coordinates inside the region are relative to this.
public Vector3 TerrainBase { get; private set; }
public uint ID { get; private set; }
public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id)
{
PhysicsScene = physicsScene;
TerrainBase = regionBase;
ID = id;
}
public abstract void Dispose();
public abstract float GetHeightAtXYZ(Vector3 pos);
}
// ==========================================================================================
public sealed class BSTerrainManager public sealed class BSTerrainManager
{ {
static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
@ -54,8 +80,6 @@ public sealed class BSTerrainManager
// amount to make sure that a bounding box is built for the terrain. // amount to make sure that a bounding box is built for the terrain.
public const float HEIGHT_EQUAL_FUDGE = 0.2f; public const float HEIGHT_EQUAL_FUDGE = 0.2f;
public const float TERRAIN_COLLISION_MARGIN = 0.0f;
// Until the whole simulator is changed to pass us the region size, we rely on constants. // Until the whole simulator is changed to pass us the region size, we rely on constants.
public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
@ -67,11 +91,10 @@ public sealed class BSTerrainManager
// If doing mega-regions, if we're region zero we will be managing multiple // If doing mega-regions, if we're region zero we will be managing multiple
// region terrains since region zero does the physics for the whole mega-region. // region terrains since region zero does the physics for the whole mega-region.
private Dictionary<Vector2, BulletHeightMapInfo> m_heightMaps; private Dictionary<Vector3, BSTerrainPhys> m_terrains;
// True of the terrain has been modified. // Flags used to know when to recalculate the height.
// Used to force recalculation of terrain height after terrain has been modified private bool m_terrainModified = false;
private bool m_terrainModified;
// If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount. // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount.
// This is incremented before assigning to new region so it is the last ID allocated. // This is incremented before assigning to new region so it is the last ID allocated.
@ -89,8 +112,7 @@ public sealed class BSTerrainManager
public BSTerrainManager(BSScene physicsScene) public BSTerrainManager(BSScene physicsScene)
{ {
PhysicsScene = physicsScene; PhysicsScene = physicsScene;
m_heightMaps = new Dictionary<Vector2,BulletHeightMapInfo>(); m_terrains = new Dictionary<Vector3,BSTerrainPhys>();
m_terrainModified = false;
// Assume one region of default size // Assume one region of default size
m_worldOffset = Vector3.Zero; m_worldOffset = Vector3.Zero;
@ -99,17 +121,15 @@ public sealed class BSTerrainManager
} }
// Create the initial instance of terrain and the underlying ground plane. // Create the initial instance of terrain and the underlying ground plane.
// The objects are allocated in the unmanaged space and the pointers are tracked
// by the managed code.
// The terrains and the groundPlane are not added to the list of PhysObjects.
// This is called from the initialization routine so we presume it is // This is called from the initialization routine so we presume it is
// safe to call Bullet in real time. We hope no one is moving prims around yet. // safe to call Bullet in real time. We hope no one is moving prims around yet.
public void CreateInitialGroundPlaneAndTerrain() public void CreateInitialGroundPlaneAndTerrain()
{ {
// The ground plane is here to catch things that are trying to drop to negative infinity // The ground plane is here to catch things that are trying to drop to negative infinity
BulletShape groundPlaneShape = new BulletShape( BulletShape groundPlaneShape = new BulletShape(
BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN), BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f,
ShapeData.PhysicsShapeType.SHAPE_GROUNDPLANE); PhysicsScene.Params.terrainCollisionMargin),
BSPhysicsShapeType.SHAPE_GROUNDPLANE);
m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
Vector3.Zero, Quaternion.Identity)); Vector3.Zero, Quaternion.Identity));
@ -121,15 +141,9 @@ public sealed class BSTerrainManager
BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr, BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr,
(uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask); (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask);
Vector3 minTerrainCoords = new Vector3(0f, 0f, HEIGHT_INITIALIZATION - HEIGHT_EQUAL_FUDGE); // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
Vector3 maxTerrainCoords = new Vector3(DefaultRegionSize.X, DefaultRegionSize.Y, HEIGHT_INITIALIZATION); BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y; m_terrains.Add(Vector3.Zero, initialTerrain);
float[] initialMap = new float[totalHeights];
for (int ii = 0; ii < totalHeights; ii++)
{
initialMap[ii] = HEIGHT_INITIALIZATION;
}
UpdateOrCreateTerrain(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords, true);
} }
// Release all the terrain structures we might have allocated // Release all the terrain structures we might have allocated
@ -150,21 +164,22 @@ public sealed class BSTerrainManager
// Release all the terrain we have allocated // Release all the terrain we have allocated
public void ReleaseTerrain() public void ReleaseTerrain()
{ {
foreach (KeyValuePair<Vector2, BulletHeightMapInfo> kvp in m_heightMaps) lock (m_terrains)
{ {
if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr)) foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains)
{ {
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr); kvp.Value.Dispose();
BulletSimAPI.ReleaseHeightMapInfo2(kvp.Value.Ptr);
} }
m_terrains.Clear();
} }
m_heightMaps.Clear();
} }
// The simulator wants to set a new heightmap for the terrain. // The simulator wants to set a new heightmap for the terrain.
public void SetTerrain(float[] heightMap) { public void SetTerrain(float[] heightMap) {
float[] localHeightMap = heightMap; float[] localHeightMap = heightMap;
PhysicsScene.TaintedObject("TerrainManager.SetTerrain", delegate() // If there are multiple requests for changes to the same terrain between ticks,
// only do that last one.
PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate()
{ {
if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null) if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null)
{ {
@ -176,8 +191,9 @@ public sealed class BSTerrainManager
{ {
DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
BSScene.DetailLogZero, m_worldOffset, m_worldMax); BSScene.DetailLogZero, m_worldOffset, m_worldMax);
((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID, ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain(
localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true); BSScene.CHILDTERRAIN_ID, localHeightMap,
m_worldOffset, m_worldOffset + DefaultRegionSize, true);
} }
} }
else else
@ -185,7 +201,7 @@ public sealed class BSTerrainManager
// If not doing the mega-prim thing, just change the terrain // If not doing the mega-prim thing, just change the terrain
DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero); DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
UpdateOrCreateTerrain(BSScene.TERRAIN_ID, localHeightMap, UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap,
m_worldOffset, m_worldOffset + DefaultRegionSize, true); m_worldOffset, m_worldOffset + DefaultRegionSize, true);
} }
}); });
@ -195,56 +211,64 @@ public sealed class BSTerrainManager
// based on the passed information. The 'id' should be either the terrain id or // based on the passed information. The 'id' should be either the terrain id or
// BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used. // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
// The latter feature is for creating child terrains for mega-regions. // The latter feature is for creating child terrains for mega-regions.
// If called with a mapInfo in m_heightMaps but the terrain has no body yet (mapInfo.terrainBody.Ptr == 0)
// then a new body and shape is created and the mapInfo is filled.
// This call is used for doing the initial terrain creation.
// If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new
// terrain shape is created and added to the body. // terrain shape is created and added to the body.
// This call is most often used to update the heightMap and parameters of the terrain. // This call is most often used to update the heightMap and parameters of the terrain.
// (The above does suggest that some simplification/refactoring is in order.) // (The above does suggest that some simplification/refactoring is in order.)
private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) // Called during taint-time.
private void UpdateTerrain(uint id, float[] heightMap,
Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
{ {
DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},inTaintTime={3}", DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime); BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime);
// Find high and low points of passed heightmap.
// The min and max passed in is usually the area objects can be in (maximum
// object height, for instance). The terrain wants the bounding box for the
// terrain so replace passed min and max Z with the actual terrain min/max Z.
float minZ = float.MaxValue; float minZ = float.MaxValue;
float maxZ = float.MinValue; float maxZ = float.MinValue;
Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y); foreach (float height in heightMap)
int heightMapSize = heightMap.Length;
for (int ii = 0; ii < heightMapSize; ii++)
{ {
float height = heightMap[ii];
if (height < minZ) minZ = height; if (height < minZ) minZ = height;
if (height > maxZ) maxZ = height; if (height > maxZ) maxZ = height;
} }
if (minZ == maxZ)
// The shape of the terrain is from its base to its extents. {
// If min and max are the same, reduce min a little bit so a good bounding box is created.
minZ -= BSTerrainManager.HEIGHT_EQUAL_FUDGE;
}
minCoords.Z = minZ; minCoords.Z = minZ;
maxCoords.Z = maxZ; maxCoords.Z = maxZ;
BulletHeightMapInfo mapInfo; Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f);
if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo))
lock (m_terrains)
{ {
// If this is terrain we know about, it's easy to update BSTerrainPhys terrainPhys;
if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
mapInfo.heightMap = heightMap;
mapInfo.minCoords = minCoords;
mapInfo.maxCoords = maxCoords;
mapInfo.minZ = minZ;
mapInfo.maxZ = maxZ;
mapInfo.sizeX = maxCoords.X - minCoords.X;
mapInfo.sizeY = maxCoords.Y - minCoords.Y;
DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,call,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateOrCreateTerrain:UpdateExisting", delegate()
{ {
if (MegaRegionParentPhysicsScene != null) // There is already a terrain in this spot. Free the old and build the new.
DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
// Remove old terrain from the collection
m_terrains.Remove(terrainRegionBase);
// Release any physical memory it may be using.
terrainPhys.Dispose();
if (MegaRegionParentPhysicsScene == null)
{
BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
m_terrains.Add(terrainRegionBase, newTerrainPhys);
m_terrainModified = true;
}
else
{ {
// It's possible that Combine() was called after this code was queued. // It's possible that Combine() was called after this code was queued.
// If we are a child of combined regions, we don't create any terrain for us. // If we are a child of combined regions, we don't create any terrain for us.
DetailLog("{0},UpdateOrCreateTerrain:AmACombineChild,taint", BSScene.DetailLogZero); DetailLog("{0},BSTerrainManager.UpdateTerrain:AmACombineChild,taint", BSScene.DetailLogZero);
// Get rid of any terrain that may have been allocated for us. // Get rid of any terrain that may have been allocated for us.
ReleaseGroundPlaneAndTerrain(); ReleaseGroundPlaneAndTerrain();
@ -252,135 +276,56 @@ public sealed class BSTerrainManager
// I hate doing this, but just bail // I hate doing this, but just bail
return; return;
} }
}
if (mapInfo.terrainBody.ptr != IntPtr.Zero) else
{
// Updating an existing terrain.
DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,taint,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
// Remove from the dynamics world because we're going to mangle this object
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
// Get rid of the old terrain
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
BulletSimAPI.ReleaseHeightMapInfo2(mapInfo.Ptr);
mapInfo.Ptr = IntPtr.Zero;
/*
// NOTE: This routine is half here because I can't get the terrain shape replacement
// to work. In the short term, the above three lines completely delete the old
// terrain and the code below recreates one from scratch.
// Hopefully the Bullet community will help me out on this one.
// First, release the old collision shape (there is only one terrain)
BulletSimAPI.DeleteCollisionShape2(m_physicsScene.World.Ptr, mapInfo.terrainShape.Ptr);
// Fill the existing height map info with the new location and size information
BulletSimAPI.FillHeightMapInfo2(m_physicsScene.World.Ptr, mapInfo.Ptr, mapInfo.ID,
mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
// Create a terrain shape based on the new info
mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr));
// Stuff the shape into the existing terrain body
BulletSimAPI.SetBodyShape2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr, mapInfo.terrainShape.Ptr);
*/
}
// else
{
// Creating a new terrain.
DetailLog("{0},UpdateOrCreateTerrain:CreateNewTerrain,taint,baseX={1},baseY={2},minZ={3},maxZ={4}",
BSScene.DetailLogZero, mapInfo.minCoords.X, mapInfo.minCoords.Y, minZ, maxZ);
mapInfo.ID = id;
mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID,
mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
// Create the terrain shape from the mapInfo
mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr),
ShapeData.PhysicsShapeType.SHAPE_TERRAIN);
// The terrain object initial position is at the center of the object
Vector3 centerPos;
centerPos.X = minCoords.X + (mapInfo.sizeX / 2f);
centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f);
centerPos.Z = minZ + ((maxZ - minZ) / 2f);
mapInfo.terrainBody = new BulletBody(mapInfo.ID,
BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr,
id, centerPos, Quaternion.Identity));
}
// Make sure the entry is in the heightmap table
m_heightMaps[terrainRegionBase] = mapInfo;
// Set current terrain attributes
BulletSimAPI.SetFriction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainFriction);
BulletSimAPI.SetHitFraction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
// Return the new terrain to the world of physical objects
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
// redo its bounding box now that it is in the world
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
BulletSimAPI.SetCollisionFilterMask2(mapInfo.terrainBody.ptr,
(uint)CollisionFilterGroups.TerrainFilter,
(uint)CollisionFilterGroups.TerrainMask);
// Make sure the new shape is processed.
// BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true);
// BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.ISLAND_SLEEPING);
BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
m_terrainModified = true;
});
}
else
{
// We don't know about this terrain so either we are creating a new terrain or
// our mega-prim child is giving us a new terrain to add to the phys world
// if this is a child terrain, calculate a unique terrain id
uint newTerrainID = id;
if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
newTerrainID = ++m_terrainCount;
float[] heightMapX = heightMap;
Vector3 minCoordsX = minCoords;
Vector3 maxCoordsX = maxCoords;
DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}",
BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
// Code that must happen at taint-time
PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateOrCreateTerrain:NewTerrain", delegate()
{ {
DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,taint,baseX={1},baseY={2}", BSScene.DetailLogZero, minCoords.X, minCoords.Y); // We don't know about this terrain so either we are creating a new terrain or
// Create a new mapInfo that will be filled with the new info // our mega-prim child is giving us a new terrain to add to the phys world
mapInfo = new BulletHeightMapInfo(id, heightMapX,
BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, newTerrainID, // if this is a child terrain, calculate a unique terrain id
minCoordsX, maxCoordsX, heightMapX, TERRAIN_COLLISION_MARGIN)); uint newTerrainID = id;
// Put the unfilled heightmap info into the collection of same if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
m_heightMaps.Add(terrainRegionBase, mapInfo); newTerrainID = ++m_terrainCount;
// Build the terrain
UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true); DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}",
BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
m_terrains.Add(terrainRegionBase, newTerrainPhys);
m_terrainModified = true; m_terrainModified = true;
}); }
} }
} }
// Someday we will have complex terrain with caves and tunnels // TODO: redo terrain implementation selection to allow other base types than heightMap.
public float GetTerrainHeightAtXYZ(Vector3 loc) private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
{ {
// For the moment, it's flat and convex PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}",
return GetTerrainHeightAtXY(loc.X, loc.Y); LogHeader, PhysicsScene.RegionName, terrainRegionBase,
(BSTerrainPhys.TerrainImplementation)PhysicsScene.Params.terrainImplementation);
BSTerrainPhys newTerrainPhys = null;
switch ((int)PhysicsScene.Params.terrainImplementation)
{
case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id,
heightMap, minCoords, maxCoords);
break;
case (int)BSTerrainPhys.TerrainImplementation.Mesh:
newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id,
heightMap, minCoords, maxCoords);
break;
default:
PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}",
LogHeader,
(int)PhysicsScene.Params.terrainImplementation,
PhysicsScene.Params.terrainImplementation,
PhysicsScene.RegionName, terrainRegionBase);
break;
}
return newTerrainPhys;
} }
// Given an X and Y, find the height of the terrain. // Given an X and Y, find the height of the terrain.
// Since we could be handling multiple terrains for a mega-region, // Since we could be handling multiple terrains for a mega-region,
// the base of the region is calcuated assuming all regions are // the base of the region is calcuated assuming all regions are
@ -390,12 +335,15 @@ public sealed class BSTerrainManager
private float lastHeightTX = 999999f; private float lastHeightTX = 999999f;
private float lastHeightTY = 999999f; private float lastHeightTY = 999999f;
private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT; private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
private float GetTerrainHeightAtXY(float tX, float tY) public float GetTerrainHeightAtXYZ(Vector3 loc)
{ {
float tX = loc.X;
float tY = loc.Y;
// You'd be surprized at the number of times this routine is called // You'd be surprized at the number of times this routine is called
// with the same parameters as last time. // with the same parameters as last time.
if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY) if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY)
return lastHeight; return lastHeight;
m_terrainModified = false;
lastHeightTX = tX; lastHeightTX = tX;
lastHeightTY = tY; lastHeightTY = tY;
@ -403,34 +351,21 @@ public sealed class BSTerrainManager
int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y; int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
Vector2 terrainBaseXY = new Vector2(offsetX, offsetY); Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
BulletHeightMapInfo mapInfo; lock (m_terrains)
if (m_heightMaps.TryGetValue(terrainBaseXY, out mapInfo))
{ {
float regionX = tX - offsetX; BSTerrainPhys physTerrain;
float regionY = tY - offsetY; if (m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain))
int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX;
try
{ {
ret = mapInfo.heightMap[mapIndex]; ret = physTerrain.GetHeightAtXYZ(loc - terrainBaseXYZ);
} }
catch else
{ {
// Sometimes they give us wonky values of X and Y. Give a warning and return something. PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, x={2}, y={3}", LogHeader, PhysicsScene.RegionName, tX, tY);
LogHeader, terrainBaseXY, regionX, regionY);
ret = HEIGHT_GETHEIGHT_RET;
} }
// DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXY,bX={1},baseY={2},szX={3},szY={4},regX={5},regY={6},index={7},ht={8}",
// BSScene.DetailLogZero, offsetX, offsetY, mapInfo.sizeX, mapInfo.sizeY, regionX, regionY, mapIndex, ret);
} }
else
{
PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
LogHeader, PhysicsScene.RegionName, tX, tY);
}
m_terrainModified = false;
lastHeight = ret; lastHeight = ret;
return ret; return ret;
} }
@ -466,7 +401,7 @@ public sealed class BSTerrainManager
// Unhook all the combining that I know about. // Unhook all the combining that I know about.
public void UnCombine(PhysicsScene pScene) public void UnCombine(PhysicsScene pScene)
{ {
// Just like ODE, for the moment a NOP // Just like ODE, we don't do anything yet.
DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero); DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero);
} }

View File

@ -0,0 +1,262 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyrightD
* 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.Collections.Generic;
using System.Text;
using OpenSim.Framework;
using OpenSim.Region.Framework;
using OpenSim.Region.CoreModules;
using OpenSim.Region.Physics.Manager;
using Nini.Config;
using log4net;
using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public sealed class BSTerrainMesh : BSTerrainPhys
{
static string LogHeader = "[BULLETSIM TERRAIN MESH]";
private float[] m_savedHeightMap;
int m_sizeX;
int m_sizeY;
BulletShape m_terrainShape;
BulletBody m_terrainBody;
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
: base(physicsScene, regionBase, id)
{
}
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id /* parameters for making mesh */)
: base(physicsScene, regionBase, id)
{
}
// Create terrain mesh from a heightmap.
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
Vector3 minCoords, Vector3 maxCoords)
: base(physicsScene, regionBase, id)
{
int indicesCount;
int[] indices;
int verticesCount;
float[] vertices;
m_savedHeightMap = initialMap;
m_sizeX = (int)(maxCoords.X - minCoords.X);
m_sizeY = (int)(maxCoords.Y - minCoords.Y);
if (!BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, initialMap,
m_sizeX, m_sizeY,
(float)m_sizeX, (float)m_sizeY,
Vector3.Zero, 1.0f,
out indicesCount, out indices, out verticesCount, out vertices))
{
// DISASTER!!
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", ID);
PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase);
// Something is very messed up and a crash is in our future.
return;
}
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,indices={1},indSz={2},vertices={3},vertSz={4}",
ID, indicesCount, indices.Length, verticesCount, vertices.Length);
m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
indicesCount, indices, verticesCount, vertices),
BSPhysicsShapeType.SHAPE_MESH);
if (m_terrainShape.ptr == IntPtr.Zero)
{
// DISASTER!!
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID);
physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
// Something is very messed up and a crash is in our future.
return;
}
Vector3 pos = regionBase;
Quaternion rot = Quaternion.Identity;
m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot));
if (m_terrainBody.ptr == IntPtr.Zero)
{
// DISASTER!!
physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
// Something is very messed up and a crash is in our future.
return;
}
// Set current terrain attributes
BulletSimAPI.SetFriction2(m_terrainBody.ptr, PhysicsScene.Params.terrainFriction);
BulletSimAPI.SetHitFraction2(m_terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
BulletSimAPI.SetRestitution2(m_terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
BulletSimAPI.SetCollisionFlags2(m_terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
// Static objects are not very massive.
BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero);
// Put the new terrain to the world of physical objects
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
// Redo its bounding box now that it is in the world
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr);
BulletSimAPI.SetCollisionFilterMask2(m_terrainBody.ptr,
(uint)CollisionFilterGroups.TerrainFilter,
(uint)CollisionFilterGroups.TerrainMask);
// Make it so the terrain will not move or be considered for movement.
BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
}
public override void Dispose()
{
if (m_terrainBody.ptr != IntPtr.Zero)
{
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
// Frees both the body and the shape.
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_terrainBody.ptr);
}
}
public override float GetHeightAtXYZ(Vector3 pos)
{
// For the moment use the saved heightmap to get the terrain height.
// TODO: raycast downward to find the true terrain below the position.
float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
int mapIndex = (int)pos.Y * m_sizeY + (int)pos.X;
try
{
ret = m_savedHeightMap[mapIndex];
}
catch
{
// Sometimes they give us wonky values of X and Y. Give a warning and return something.
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
LogHeader, TerrainBase, pos);
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
}
return ret;
}
// Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
// Return 'true' if successfully created.
public static bool ConvertHeightmapToMesh(
BSScene physicsScene,
float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap
float extentX, float extentY, // zero based range for output vertices
Vector3 extentBase, // base to be added to all vertices
float magnification, // number of vertices to create between heightMap coords
out int indicesCountO, out int[] indicesO,
out int verticesCountO, out float[] verticesO)
{
bool ret = false;
int indicesCount = 0;
int verticesCount = 0;
int[] indices = new int[0];
float[] vertices = new float[0];
// Simple mesh creation which assumes magnification == 1.
// TODO: do a more general solution that scales, adds new vertices and smoothes the result.
// Create an array of vertices that is sizeX+1 by sizeY+1 (note the loop
// from zero to <= sizeX). The triangle indices are then generated as two triangles
// per heightmap point. There are sizeX by sizeY of these squares. The extra row and
// column of vertices are used to complete the triangles of the last row and column
// of the heightmap.
try
{
// One vertice per heightmap value plus the vertices off the top and bottom edge.
int totalVertices = (sizeX + 1) * (sizeY + 1);
vertices = new float[totalVertices * 3];
int totalIndices = sizeX * sizeY * 6;
indices = new int[totalIndices];
float magX = (float)sizeX / extentX;
float magY = (float)sizeY / extentY;
physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}",
BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY);
float minHeight = float.MaxValue;
// Note that sizeX+1 vertices are created since there is land between this and the next region.
for (int yy = 0; yy <= sizeY; yy++)
{
for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times
{
int offset = yy * sizeX + xx;
// Extend the height with the height from the last row or column
if (yy == sizeY) offset -= sizeX;
if (xx == sizeX) offset -= 1;
float height = heightMap[offset];
minHeight = Math.Min(minHeight, height);
vertices[verticesCount + 0] = (float)xx * magX + extentBase.X;
vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y;
vertices[verticesCount + 2] = height + extentBase.Z;
verticesCount += 3;
}
}
verticesCount = verticesCount / 3;
for (int yy = 0; yy < sizeY; yy++)
{
for (int xx = 0; xx < sizeX; xx++)
{
int offset = yy * (sizeX + 1) + xx;
// Each vertices is presumed to be the upper left corner of a box of two triangles
indices[indicesCount + 0] = offset;
indices[indicesCount + 1] = offset + 1;
indices[indicesCount + 2] = offset + sizeX + 1; // accounting for the extra column
indices[indicesCount + 3] = offset + 1;
indices[indicesCount + 4] = offset + sizeX + 2;
indices[indicesCount + 5] = offset + sizeX + 1;
indicesCount += 6;
}
}
ret = true;
}
catch (Exception e)
{
physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}",
LogHeader, physicsScene.RegionName, extentBase, e);
}
indicesCountO = indicesCount;
indicesO = indices;
verticesCountO = verticesCount;
verticesO = vertices;
return ret;
}
}
}

View File

@ -88,11 +88,11 @@ public struct BulletShape
public BulletShape(IntPtr xx) public BulletShape(IntPtr xx)
{ {
ptr = xx; ptr = xx;
type=ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; type=BSPhysicsShapeType.SHAPE_UNKNOWN;
shapeKey = 0; shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
isNativeShape = false; isNativeShape = false;
} }
public BulletShape(IntPtr xx, ShapeData.PhysicsShapeType typ) public BulletShape(IntPtr xx, BSPhysicsShapeType typ)
{ {
ptr = xx; ptr = xx;
type = typ; type = typ;
@ -100,7 +100,7 @@ public struct BulletShape
isNativeShape = false; isNativeShape = false;
} }
public IntPtr ptr; public IntPtr ptr;
public ShapeData.PhysicsShapeType type; public BSPhysicsShapeType type;
public System.UInt64 shapeKey; public System.UInt64 shapeKey;
public bool isNativeShape; public bool isNativeShape;
public override string ToString() public override string ToString()
@ -152,7 +152,7 @@ public class BulletHeightMapInfo
ID = id; ID = id;
Ptr = xx; Ptr = xx;
heightMap = hm; heightMap = hm;
terrainRegionBase = new Vector2(0f, 0f); terrainRegionBase = Vector3.Zero;
minCoords = new Vector3(100f, 100f, 25f); minCoords = new Vector3(100f, 100f, 25f);
maxCoords = new Vector3(101f, 101f, 26f); maxCoords = new Vector3(101f, 101f, 26f);
minZ = maxZ = 0f; minZ = maxZ = 0f;
@ -161,7 +161,7 @@ public class BulletHeightMapInfo
public uint ID; public uint ID;
public IntPtr Ptr; public IntPtr Ptr;
public float[] heightMap; public float[] heightMap;
public Vector2 terrainRegionBase; public Vector3 terrainRegionBase;
public Vector3 minCoords; public Vector3 minCoords;
public Vector3 maxCoords; public Vector3 maxCoords;
public float sizeX, sizeY; public float sizeX, sizeY;
@ -178,26 +178,39 @@ public struct ConvexHull
int VertexCount; int VertexCount;
Vector3[] Vertices; Vector3[] Vertices;
} }
public enum BSPhysicsShapeType
{
SHAPE_UNKNOWN = 0,
SHAPE_CAPSULE = 1,
SHAPE_BOX = 2,
SHAPE_CONE = 3,
SHAPE_CYLINDER = 4,
SHAPE_SPHERE = 5,
SHAPE_MESH = 6,
SHAPE_HULL = 7,
// following defined by BulletSim
SHAPE_GROUNDPLANE = 20,
SHAPE_TERRAIN = 21,
SHAPE_COMPOUND = 22,
SHAPE_HEIGHTMAP = 23,
};
// The native shapes have predefined shape hash keys
public enum FixedShapeKey : ulong
{
KEY_NONE = 0,
KEY_BOX = 1,
KEY_SPHERE = 2,
KEY_CONE = 3,
KEY_CYLINDER = 4,
KEY_CAPSULE = 5,
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct ShapeData public struct ShapeData
{ {
public enum PhysicsShapeType
{
SHAPE_UNKNOWN = 0,
SHAPE_AVATAR = 1,
SHAPE_BOX = 2,
SHAPE_CONE = 3,
SHAPE_CYLINDER = 4,
SHAPE_SPHERE = 5,
SHAPE_MESH = 6,
SHAPE_HULL = 7,
// following defined by BulletSim
SHAPE_GROUNDPLANE = 20,
SHAPE_TERRAIN = 21,
SHAPE_COMPOUND = 22,
};
public uint ID; public uint ID;
public PhysicsShapeType Type; public BSPhysicsShapeType Type;
public Vector3 Position; public Vector3 Position;
public Quaternion Rotation; public Quaternion Rotation;
public Vector3 Velocity; public Vector3 Velocity;
@ -216,16 +229,6 @@ public struct ShapeData
// note that bools are passed as floats since bool size changes by language and architecture // note that bools are passed as floats since bool size changes by language and architecture
public const float numericTrue = 1f; public const float numericTrue = 1f;
public const float numericFalse = 0f; public const float numericFalse = 0f;
// The native shapes have predefined shape hash keys
public enum FixedShapeKey : ulong
{
KEY_BOX = 1,
KEY_SPHERE = 2,
KEY_CONE = 3,
KEY_CYLINDER = 4,
KEY_CAPSULE = 5,
}
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct SweepHit public struct SweepHit
@ -280,17 +283,23 @@ public struct ConfigurationParameters
public float ccdSweptSphereRadius; public float ccdSweptSphereRadius;
public float contactProcessingThreshold; public float contactProcessingThreshold;
public float terrainImplementation;
public float terrainFriction; public float terrainFriction;
public float terrainHitFraction; public float terrainHitFraction;
public float terrainRestitution; public float terrainRestitution;
public float terrainCollisionMargin;
public float avatarFriction; public float avatarFriction;
public float avatarStandingFriction; public float avatarStandingFriction;
public float avatarDensity; public float avatarDensity;
public float avatarRestitution; public float avatarRestitution;
public float avatarCapsuleRadius; public float avatarCapsuleWidth;
public float avatarCapsuleDepth;
public float avatarCapsuleHeight; public float avatarCapsuleHeight;
public float avatarContactProcessingThreshold; public float avatarContactProcessingThreshold;
public float vehicleAngularDamping;
public float maxPersistantManifoldPoolSize; public float maxPersistantManifoldPoolSize;
public float maxCollisionAlgorithmPoolSize; public float maxCollisionAlgorithmPoolSize;
public float shouldDisableContactPoolDynamicAllocation; public float shouldDisableContactPoolDynamicAllocation;
@ -348,7 +357,7 @@ public enum CollisionFlags : uint
CF_CHARACTER_OBJECT = 1 << 4, CF_CHARACTER_OBJECT = 1 << 4,
CF_DISABLE_VISUALIZE_OBJECT = 1 << 5, CF_DISABLE_VISUALIZE_OBJECT = 1 << 5,
CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6, CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6,
// Following used by BulletSim to control collisions // Following used by BulletSim to control collisions and updates
BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10,
BS_FLOATS_ON_WATER = 1 << 11, BS_FLOATS_ON_WATER = 1 << 11,
BS_NONE = 0, BS_NONE = 0,
@ -388,13 +397,13 @@ public enum CollisionFilterGroups : uint
ObjectFilter = BSolidFilter, ObjectFilter = BSolidFilter,
ObjectMask = BAllFilter, ObjectMask = BAllFilter,
StaticObjectFilter = BStaticFilter, StaticObjectFilter = BStaticFilter,
StaticObjectMask = BAllFilter, StaticObjectMask = BAllFilter & ~BStaticFilter, // static objects don't collide with each other
LinksetFilter = BLinksetFilter, LinksetFilter = BLinksetFilter,
LinksetMask = BAllFilter & ~BLinksetFilter, LinksetMask = BAllFilter & ~BLinksetFilter, // linkset objects don't collide with each other
VolumeDetectFilter = BSensorTrigger, VolumeDetectFilter = BSensorTrigger,
VolumeDetectMask = ~BSensorTrigger, VolumeDetectMask = ~BSensorTrigger,
TerrainFilter = BTerrainFilter, TerrainFilter = BTerrainFilter,
TerrainMask = BAllFilter & ~BStaticFilter, TerrainMask = BAllFilter & ~BStaticFilter, // static objects on the ground don't collide
GroundPlaneFilter = BGroundPlaneFilter, GroundPlaneFilter = BGroundPlaneFilter,
GroundPlaneMask = BAllFilter GroundPlaneMask = BAllFilter
@ -429,140 +438,6 @@ static class BulletSimAPI {
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg); public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetVersion();
/* Remove the linkage to the old api methods
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern uint Initialize(Vector3 maxPosition, IntPtr parms,
int maxCollisions, IntPtr collisionArray,
int maxUpdates, IntPtr updateArray,
DebugLogCallback logRoutine);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void CreateInitialGroundPlaneAndTerrain(uint worldID);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetHeightmap(uint worldID, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void Shutdown(uint worldID);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool UpdateParameter(uint worldID, uint localID,
[MarshalAs(UnmanagedType.LPStr)]string paramCode, float value);
// ===============================================================================
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern int PhysicsStep(uint worldID, float timeStep, int maxSubSteps, float fixedTimeStep,
out int updatedEntityCount,
out IntPtr updatedEntitiesPtr,
out int collidersCount,
out IntPtr collidersPtr);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool CreateHull(uint worldID, System.UInt64 meshKey,
int hullCount, [MarshalAs(UnmanagedType.LPArray)] float[] hulls
);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool CreateMesh(uint worldID, System.UInt64 meshKey,
int indexCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices,
int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices
);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool DestroyHull(uint worldID, System.UInt64 meshKey);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool DestroyMesh(uint worldID, System.UInt64 meshKey);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool CreateObject(uint worldID, ShapeData shapeData);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetObjectPosition(uint WorldID, uint id);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Quaternion GetObjectOrientation(uint WorldID, uint id);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectTranslation(uint worldID, uint id, Vector3 position, Quaternion rotation);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectVelocity(uint worldID, uint id, Vector3 velocity);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectAngularVelocity(uint worldID, uint id, Vector3 angularVelocity);
// Set the current force acting on the object
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectForce(uint worldID, uint id, Vector3 force);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectScaleMass(uint worldID, uint id, Vector3 scale, float mass, bool isDynamic);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectCollidable(uint worldID, uint id, bool phantom);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectDynamic(uint worldID, uint id, bool isDynamic, float mass);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectGhost(uint worldID, uint id, bool ghostly);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectProperties(uint worldID, uint id, bool isStatic, bool isSolid, bool genCollisions, float mass);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectBuoyancy(uint worldID, uint id, float buoyancy);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool HasObject(uint worldID, uint id);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool DestroyObject(uint worldID, uint id);
// ===============================================================================
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern SweepHit ConvexSweepTest(uint worldID, uint id, Vector3 to, float extraMargin);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern RaycastHit RayTest(uint worldID, uint id, Vector3 from, Vector3 to);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 RecoverFromPenetration(uint worldID, uint id);
// ===============================================================================
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpBulletStatistics();
*/
// Log a debug message
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetDebugLogCallback(DebugLogCallback callback);
// ===============================================================================
// ===============================================================================
// ===============================================================================
// A new version of the API that enables moving all the logic out of the C++ code and into
// the C# code. This will make modifications easier for the next person.
// This interface passes the actual pointers to the objects in the unmanaged
// address space. All the management (calls for creation/destruction/lookup)
// is done in the C# code.
// The names have a "2" tacked on. This will be removed as the C# code gets rebuilt
// and the old code is removed.
// Functions use while converting from API1 to API2. Can be removed when totally converted.
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr GetSimHandle2(uint worldID);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr GetBodyHandleWorldID2(uint worldID, uint id);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr GetBodyHandle2(IntPtr world, uint id);
// =============================================================================== // ===============================================================================
// Initialization and simulation // Initialization and simulation
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
@ -610,6 +485,9 @@ public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData)
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsNativeShape2(IntPtr shape); public static extern bool IsNativeShape2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetShapeCollisionMargin(IntPtr shape, float margin);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale); public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale);

View File

@ -32,13 +32,14 @@ using OpenMetaverse;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Region.Physics.Manager; using OpenSim.Region.Physics.Manager;
using OpenSim.Region.Physics.OdePlugin; using OpenSim.Region.Physics.OdePlugin;
using OpenSim.Tests.Common;
using log4net; using log4net;
using System.Reflection; using System.Reflection;
namespace OpenSim.Region.Physics.OdePlugin.Tests namespace OpenSim.Region.Physics.OdePlugin.Tests
{ {
[TestFixture] [TestFixture]
public class ODETestClass public class ODETestClass : OpenSimTestCase
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

Some files were not shown because too many files have changed in this diff Show More