Merge branch 'master' of ssh://opensimulator.org/var/git/opensim
commit
daa4745fb7
|
@ -1369,6 +1369,8 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||||
/// <description>profile url</description></item>
|
/// <description>profile url</description></item>
|
||||||
/// <item><term>noassets</term>
|
/// <item><term>noassets</term>
|
||||||
/// <description>true if no assets should be saved</description></item>
|
/// <description>true if no assets should be saved</description></item>
|
||||||
|
/// <item><term>all</term>
|
||||||
|
/// <description>true to save all the regions in the simulator</description></item>
|
||||||
/// <item><term>perm</term>
|
/// <item><term>perm</term>
|
||||||
/// <description>C and/or T</description></item>
|
/// <description>C and/or T</description></item>
|
||||||
/// </list>
|
/// </list>
|
||||||
|
@ -1425,6 +1427,11 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||||
options["checkPermissions"] = (string)requestData["perm"];
|
options["checkPermissions"] = (string)requestData["perm"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((string)requestData["all"] == "true")
|
||||||
|
{
|
||||||
|
options["all"] = (string)requestData["all"];
|
||||||
|
}
|
||||||
|
|
||||||
IRegionArchiverModule archiver = scene.RequestModuleInterface<IRegionArchiverModule>();
|
IRegionArchiverModule archiver = scene.RequestModuleInterface<IRegionArchiverModule>();
|
||||||
|
|
||||||
if (archiver != null)
|
if (archiver != null)
|
||||||
|
|
|
@ -52,6 +52,11 @@ namespace OpenSim.Framework.Serialization
|
||||||
/// </value>
|
/// </value>
|
||||||
public const string INVENTORY_PATH = "inventory/";
|
public const string INVENTORY_PATH = "inventory/";
|
||||||
|
|
||||||
|
/// <value>
|
||||||
|
/// Path for regions in a multi-region archive
|
||||||
|
/// </value>
|
||||||
|
public const string REGIONS_PATH = "regions/";
|
||||||
|
|
||||||
/// <value>
|
/// <value>
|
||||||
/// Path for the prims file
|
/// Path for the prims file
|
||||||
/// </value>
|
/// </value>
|
||||||
|
|
|
@ -292,7 +292,7 @@ namespace OpenSim
|
||||||
|
|
||||||
m_console.Commands.AddCommand("Archiving", false, "save oar",
|
m_console.Commands.AddCommand("Archiving", false, "save oar",
|
||||||
//"save oar [-v|--version=<N>] [-p|--profile=<url>] [<OAR path>]",
|
//"save oar [-v|--version=<N>] [-p|--profile=<url>] [<OAR path>]",
|
||||||
"save oar [-h|--home=<url>] [--noassets] [--publish] [--perm=<permissions>] [<OAR path>]",
|
"save oar [-h|--home=<url>] [--noassets] [--publish] [--perm=<permissions>] [--all] [<OAR path>]",
|
||||||
"Save a region's data to an OAR archive.",
|
"Save a region's data to an OAR archive.",
|
||||||
// "-v|--version=<N> generates scene objects as per older versions of the serialization (e.g. -v=0)" + Environment.NewLine
|
// "-v|--version=<N> generates scene objects as per older versions of the serialization (e.g. -v=0)" + Environment.NewLine
|
||||||
"-h|--home=<url> adds the url of the profile service to the saved user information.\n"
|
"-h|--home=<url> adds the url of the profile service to the saved user information.\n"
|
||||||
|
@ -302,6 +302,7 @@ namespace OpenSim
|
||||||
+ " this is useful if you're making oars generally available that might be reloaded to the same grid from which you published\n"
|
+ " this is useful if you're making oars generally available that might be reloaded to the same grid from which you published\n"
|
||||||
+ "--perm=<permissions> stops objects with insufficient permissions from being saved to the OAR.\n"
|
+ "--perm=<permissions> stops objects with insufficient permissions from being saved to the OAR.\n"
|
||||||
+ " <permissions> can contain one or more of these characters: \"C\" = Copy, \"T\" = Transfer\n"
|
+ " <permissions> can contain one or more of these characters: \"C\" = Copy, \"T\" = Transfer\n"
|
||||||
|
+ "--all saves all the regions in the simulator, instead of just the current region.\n"
|
||||||
+ "The OAR path must be a filesystem path."
|
+ "The OAR path must be a filesystem path."
|
||||||
+ " If this is not given then the oar is saved to region.oar in the current directory.",
|
+ " If this is not given then the oar is saved to region.oar in the current directory.",
|
||||||
SaveOar);
|
SaveOar);
|
||||||
|
|
|
@ -76,7 +76,7 @@ namespace OpenSim.Region.ClientStack
|
||||||
|
|
||||||
protected override void StartupSpecific()
|
protected override void StartupSpecific()
|
||||||
{
|
{
|
||||||
SceneManager = new SceneManager();
|
SceneManager = SceneManager.Instance;
|
||||||
m_clientStackManager = CreateClientStackManager();
|
m_clientStackManager = CreateClientStackManager();
|
||||||
|
|
||||||
Initialize();
|
Initialize();
|
||||||
|
|
|
@ -107,8 +107,6 @@ namespace OpenSim.Region.CoreModules.Asset
|
||||||
private IAssetService m_AssetService;
|
private IAssetService m_AssetService;
|
||||||
private List<Scene> m_Scenes = new List<Scene>();
|
private List<Scene> m_Scenes = new List<Scene>();
|
||||||
|
|
||||||
private bool m_DeepScanBeforePurge;
|
|
||||||
|
|
||||||
public FlotsamAssetCache()
|
public FlotsamAssetCache()
|
||||||
{
|
{
|
||||||
m_InvalidChars.AddRange(Path.GetInvalidPathChars());
|
m_InvalidChars.AddRange(Path.GetInvalidPathChars());
|
||||||
|
@ -170,8 +168,6 @@ namespace OpenSim.Region.CoreModules.Asset
|
||||||
m_CacheDirectoryTierLen = assetConfig.GetInt("CacheDirectoryTierLength", m_CacheDirectoryTierLen);
|
m_CacheDirectoryTierLen = assetConfig.GetInt("CacheDirectoryTierLength", m_CacheDirectoryTierLen);
|
||||||
|
|
||||||
m_CacheWarnAt = assetConfig.GetInt("CacheWarnAt", m_CacheWarnAt);
|
m_CacheWarnAt = assetConfig.GetInt("CacheWarnAt", m_CacheWarnAt);
|
||||||
|
|
||||||
m_DeepScanBeforePurge = assetConfig.GetBoolean("DeepScanBeforePurge", m_DeepScanBeforePurge);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory);
|
m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory);
|
||||||
|
@ -506,13 +502,10 @@ namespace OpenSim.Region.CoreModules.Asset
|
||||||
// Purge all files last accessed prior to this point
|
// Purge all files last accessed prior to this point
|
||||||
DateTime purgeLine = DateTime.Now - m_FileExpiration;
|
DateTime purgeLine = DateTime.Now - m_FileExpiration;
|
||||||
|
|
||||||
// An optional deep scan at this point will ensure assets present in scenes,
|
// An asset cache may contain local non-temporary assets that are not in the asset service. Therefore,
|
||||||
// or referenced by objects in the scene, but not recently accessed
|
// before cleaning up expired files we must scan the objects in the scene to make sure that we retain
|
||||||
// are not purged.
|
// such local assets if they have not been recently accessed.
|
||||||
if (m_DeepScanBeforePurge)
|
TouchAllSceneAssets(false);
|
||||||
{
|
|
||||||
CacheScenes();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
|
foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
|
||||||
{
|
{
|
||||||
|
@ -705,11 +698,14 @@ namespace OpenSim.Region.CoreModules.Asset
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Iterates through all Scenes, doing a deep scan through assets
|
/// Iterates through all Scenes, doing a deep scan through assets
|
||||||
/// to cache all assets present in the scene or referenced by assets
|
/// to update the access time of all assets present in the scene or referenced by assets
|
||||||
/// in the scene
|
/// in the scene.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <param name="storeUncached">
|
||||||
private int CacheScenes()
|
/// If true, then assets scanned which are not found in cache are added to the cache.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>Number of distinct asset references found in the scene.</returns>
|
||||||
|
private int TouchAllSceneAssets(bool storeUncached)
|
||||||
{
|
{
|
||||||
UuidGatherer gatherer = new UuidGatherer(m_AssetService);
|
UuidGatherer gatherer = new UuidGatherer(m_AssetService);
|
||||||
|
|
||||||
|
@ -732,7 +728,7 @@ namespace OpenSim.Region.CoreModules.Asset
|
||||||
{
|
{
|
||||||
File.SetLastAccessTime(filename, DateTime.Now);
|
File.SetLastAccessTime(filename, DateTime.Now);
|
||||||
}
|
}
|
||||||
else
|
else if (storeUncached)
|
||||||
{
|
{
|
||||||
m_AssetService.Get(assetID.ToString());
|
m_AssetService.Get(assetID.ToString());
|
||||||
}
|
}
|
||||||
|
@ -860,13 +856,14 @@ namespace OpenSim.Region.CoreModules.Asset
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case "assets":
|
case "assets":
|
||||||
m_log.Info("[FLOTSAM ASSET CACHE]: Caching all assets, in all scenes.");
|
m_log.Info("[FLOTSAM ASSET CACHE]: Ensuring assets are cached for all scenes.");
|
||||||
|
|
||||||
Util.FireAndForget(delegate {
|
Util.FireAndForget(delegate {
|
||||||
int assetsCached = CacheScenes();
|
int assetReferenceTotal = TouchAllSceneAssets(true);
|
||||||
m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Completed Scene Caching, {0} assets found.", assetsCached);
|
m_log.InfoFormat(
|
||||||
|
"[FLOTSAM ASSET CACHE]: Completed check with {0} assets.",
|
||||||
|
assetReferenceTotal);
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -204,8 +204,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
|
||||||
public byte[] GetData(string id)
|
public byte[] GetData(string id)
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("[LOCAL ASSET SERVICES CONNECTOR]: Requesting data for asset {0}", id);
|
// m_log.DebugFormat("[LOCAL ASSET SERVICES CONNECTOR]: Requesting data for asset {0}", id);
|
||||||
|
|
||||||
AssetBase asset = m_Cache.Get(id);
|
AssetBase asset = null;
|
||||||
|
|
||||||
|
if (m_Cache != null)
|
||||||
|
asset = m_Cache.Get(id);
|
||||||
|
|
||||||
if (asset != null)
|
if (asset != null)
|
||||||
return asset.Data;
|
return asset.Data;
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* 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.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
using log4net.Config;
|
||||||
|
using Nini.Config;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Region.Framework.Scenes;
|
||||||
|
using OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset;
|
||||||
|
using OpenSim.Tests.Common;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class AssetConnectorsTests : OpenSimTestCase
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestAddAsset()
|
||||||
|
{
|
||||||
|
TestHelpers.InMethod();
|
||||||
|
// TestHelpers.EnableLogging();
|
||||||
|
|
||||||
|
IConfigSource config = new IniConfigSource();
|
||||||
|
config.AddConfig("Modules");
|
||||||
|
config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
|
||||||
|
config.AddConfig("AssetService");
|
||||||
|
config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
|
||||||
|
config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
|
||||||
|
|
||||||
|
LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
|
||||||
|
lasc.Initialise(config);
|
||||||
|
|
||||||
|
AssetBase a1 = AssetHelpers.CreateNotecardAsset();
|
||||||
|
lasc.Store(a1);
|
||||||
|
|
||||||
|
AssetBase retreivedA1 = lasc.Get(a1.ID);
|
||||||
|
Assert.That(retreivedA1.ID, Is.EqualTo(a1.ID));
|
||||||
|
Assert.That(retreivedA1.Metadata.ID, Is.EqualTo(a1.Metadata.ID));
|
||||||
|
Assert.That(retreivedA1.Data.Length, Is.EqualTo(a1.Data.Length));
|
||||||
|
|
||||||
|
AssetMetadata retrievedA1Metadata = lasc.GetMetadata(a1.ID);
|
||||||
|
Assert.That(retrievedA1Metadata.ID, Is.EqualTo(a1.ID));
|
||||||
|
|
||||||
|
byte[] retrievedA1Data = lasc.GetData(a1.ID);
|
||||||
|
Assert.That(retrievedA1Data.Length, Is.EqualTo(a1.Data.Length));
|
||||||
|
|
||||||
|
// TODO: Add cache and check that this does receive a copy of the asset
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddTemporaryAsset()
|
||||||
|
{
|
||||||
|
TestHelpers.InMethod();
|
||||||
|
// TestHelpers.EnableLogging();
|
||||||
|
|
||||||
|
IConfigSource config = new IniConfigSource();
|
||||||
|
config.AddConfig("Modules");
|
||||||
|
config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
|
||||||
|
config.AddConfig("AssetService");
|
||||||
|
config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
|
||||||
|
config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
|
||||||
|
|
||||||
|
LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
|
||||||
|
lasc.Initialise(config);
|
||||||
|
|
||||||
|
AssetBase a1 = AssetHelpers.CreateNotecardAsset();
|
||||||
|
a1.Temporary = true;
|
||||||
|
|
||||||
|
lasc.Store(a1);
|
||||||
|
|
||||||
|
Assert.That(lasc.Get(a1.ID), Is.Null);
|
||||||
|
Assert.That(lasc.GetData(a1.ID), Is.Null);
|
||||||
|
Assert.That(lasc.GetMetadata(a1.ID), Is.Null);
|
||||||
|
|
||||||
|
// TODO: Add cache and check that this does receive a copy of the asset
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddLocalAsset()
|
||||||
|
{
|
||||||
|
TestHelpers.InMethod();
|
||||||
|
// TestHelpers.EnableLogging();
|
||||||
|
|
||||||
|
IConfigSource config = new IniConfigSource();
|
||||||
|
config.AddConfig("Modules");
|
||||||
|
config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
|
||||||
|
config.AddConfig("AssetService");
|
||||||
|
config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
|
||||||
|
config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
|
||||||
|
|
||||||
|
LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
|
||||||
|
lasc.Initialise(config);
|
||||||
|
|
||||||
|
AssetBase a1 = AssetHelpers.CreateNotecardAsset();
|
||||||
|
a1.Local = true;
|
||||||
|
|
||||||
|
lasc.Store(a1);
|
||||||
|
|
||||||
|
Assert.That(lasc.Get(a1.ID), Is.Null);
|
||||||
|
Assert.That(lasc.GetData(a1.ID), Is.Null);
|
||||||
|
Assert.That(lasc.GetMetadata(a1.ID), Is.Null);
|
||||||
|
|
||||||
|
// TODO: Add cache and check that this does receive a copy of the asset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,6 +43,7 @@ using OpenSim.Region.Framework.Interfaces;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Region.Framework.Scenes.Serialization;
|
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.World.Archiver
|
namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
{
|
{
|
||||||
|
@ -52,7 +53,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
public class ArchiveReadRequest
|
public class ArchiveReadRequest
|
||||||
{
|
{
|
||||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains data used while dearchiving a single scene.
|
||||||
|
/// </summary>
|
||||||
|
private class DearchiveContext
|
||||||
|
{
|
||||||
|
public Scene Scene { get; set; }
|
||||||
|
|
||||||
|
public List<string> SerialisedSceneObjects { get; set; }
|
||||||
|
|
||||||
|
public List<string> SerialisedParcels { get; set; }
|
||||||
|
|
||||||
|
public List<SceneObjectGroup> SceneObjects { get; set; }
|
||||||
|
|
||||||
|
public DearchiveContext(Scene scene)
|
||||||
|
{
|
||||||
|
Scene = scene;
|
||||||
|
SerialisedSceneObjects = new List<string>();
|
||||||
|
SerialisedParcels = new List<string>();
|
||||||
|
SceneObjects = new List<SceneObjectGroup>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum major version of OAR that we can read. Minor versions shouldn't need a max number since version
|
/// The maximum major version of OAR that we can read. Minor versions shouldn't need a max number since version
|
||||||
/// bumps here should be compatible.
|
/// bumps here should be compatible.
|
||||||
|
@ -62,9 +86,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Has the control file been loaded for this archive?
|
/// Has the control file been loaded for this archive?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ControlFileLoaded { get; private set; }
|
public bool ControlFileLoaded { get; private set; }
|
||||||
|
|
||||||
protected Scene m_scene;
|
protected string m_loadPath;
|
||||||
|
protected Scene m_rootScene;
|
||||||
protected Stream m_loadStream;
|
protected Stream m_loadStream;
|
||||||
protected Guid m_requestId;
|
protected Guid m_requestId;
|
||||||
protected string m_errorMessage;
|
protected string m_errorMessage;
|
||||||
|
@ -91,7 +116,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
{
|
{
|
||||||
if (m_UserMan == null)
|
if (m_UserMan == null)
|
||||||
{
|
{
|
||||||
m_UserMan = m_scene.RequestModuleInterface<IUserManagement>();
|
m_UserMan = m_rootScene.RequestModuleInterface<IUserManagement>();
|
||||||
}
|
}
|
||||||
return m_UserMan;
|
return m_UserMan;
|
||||||
}
|
}
|
||||||
|
@ -104,10 +129,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
|
|
||||||
private IGroupsModule m_groupsModule;
|
private IGroupsModule m_groupsModule;
|
||||||
|
|
||||||
|
private IAssetService m_assetService = null;
|
||||||
|
|
||||||
|
|
||||||
public ArchiveReadRequest(Scene scene, string loadPath, bool merge, bool skipAssets, Guid requestId)
|
public ArchiveReadRequest(Scene scene, string loadPath, bool merge, bool skipAssets, Guid requestId)
|
||||||
{
|
{
|
||||||
m_scene = scene;
|
m_rootScene = scene;
|
||||||
|
|
||||||
|
m_loadPath = loadPath;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
m_loadStream = new GZipStream(ArchiveHelpers.GetStream(loadPath), CompressionMode.Decompress);
|
m_loadStream = new GZipStream(ArchiveHelpers.GetStream(loadPath), CompressionMode.Decompress);
|
||||||
|
@ -128,12 +157,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
// Zero can never be a valid user id
|
// Zero can never be a valid user id
|
||||||
m_validUserUuids[UUID.Zero] = false;
|
m_validUserUuids[UUID.Zero] = false;
|
||||||
|
|
||||||
m_groupsModule = m_scene.RequestModuleInterface<IGroupsModule>();
|
m_groupsModule = m_rootScene.RequestModuleInterface<IGroupsModule>();
|
||||||
|
m_assetService = m_rootScene.AssetService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArchiveReadRequest(Scene scene, Stream loadStream, bool merge, bool skipAssets, Guid requestId)
|
public ArchiveReadRequest(Scene scene, Stream loadStream, bool merge, bool skipAssets, Guid requestId)
|
||||||
{
|
{
|
||||||
m_scene = scene;
|
m_rootScene = scene;
|
||||||
|
m_loadPath = null;
|
||||||
m_loadStream = loadStream;
|
m_loadStream = loadStream;
|
||||||
m_merge = merge;
|
m_merge = merge;
|
||||||
m_skipAssets = skipAssets;
|
m_skipAssets = skipAssets;
|
||||||
|
@ -142,33 +173,34 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
// Zero can never be a valid user id
|
// Zero can never be a valid user id
|
||||||
m_validUserUuids[UUID.Zero] = false;
|
m_validUserUuids[UUID.Zero] = false;
|
||||||
|
|
||||||
m_groupsModule = m_scene.RequestModuleInterface<IGroupsModule>();
|
m_groupsModule = m_rootScene.RequestModuleInterface<IGroupsModule>();
|
||||||
|
m_assetService = m_rootScene.AssetService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dearchive the region embodied in this request.
|
/// Dearchive the region embodied in this request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void DearchiveRegion()
|
public void DearchiveRegion()
|
||||||
{
|
|
||||||
// The same code can handle dearchiving 0.1 and 0.2 OpenSim Archive versions
|
|
||||||
DearchiveRegion0DotStar();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DearchiveRegion0DotStar()
|
|
||||||
{
|
{
|
||||||
int successfulAssetRestores = 0;
|
int successfulAssetRestores = 0;
|
||||||
int failedAssetRestores = 0;
|
int failedAssetRestores = 0;
|
||||||
List<string> serialisedSceneObjects = new List<string>();
|
|
||||||
List<string> serialisedParcels = new List<string>();
|
|
||||||
string filePath = "NONE";
|
|
||||||
|
|
||||||
TarArchiveReader archive = new TarArchiveReader(m_loadStream);
|
DearchiveScenesInfo dearchivedScenes;
|
||||||
|
|
||||||
|
// We dearchive all the scenes at once, because the files in the TAR archive might be mixed.
|
||||||
|
// Therefore, we have to keep track of the dearchive context of all the scenes.
|
||||||
|
Dictionary<UUID, DearchiveContext> sceneContexts = new Dictionary<UUID, DearchiveContext>();
|
||||||
|
|
||||||
|
string fullPath = "NONE";
|
||||||
|
TarArchiveReader archive = null;
|
||||||
byte[] data;
|
byte[] data;
|
||||||
TarArchiveReader.TarEntryType entryType;
|
TarArchiveReader.TarEntryType entryType;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
|
FindAndLoadControlFile(out archive, out dearchivedScenes);
|
||||||
|
|
||||||
|
while ((data = archive.ReadEntry(out fullPath, out entryType)) != null)
|
||||||
{
|
{
|
||||||
//m_log.DebugFormat(
|
//m_log.DebugFormat(
|
||||||
// "[ARCHIVER]: Successfully read {0} ({1} bytes)", filePath, data.Length);
|
// "[ARCHIVER]: Successfully read {0} ({1} bytes)", filePath, data.Length);
|
||||||
|
@ -176,9 +208,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType)
|
if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
// Find the scene that this file belongs to
|
||||||
|
|
||||||
|
Scene scene;
|
||||||
|
string filePath;
|
||||||
|
if (!dearchivedScenes.GetRegionFromPath(fullPath, out scene, out filePath))
|
||||||
|
continue; // this file belongs to a region that we're not loading
|
||||||
|
|
||||||
|
DearchiveContext sceneContext = null;
|
||||||
|
if (scene != null)
|
||||||
|
{
|
||||||
|
if (!sceneContexts.TryGetValue(scene.RegionInfo.RegionID, out sceneContext))
|
||||||
|
{
|
||||||
|
sceneContext = new DearchiveContext(scene);
|
||||||
|
sceneContexts.Add(scene.RegionInfo.RegionID, sceneContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Process the file
|
||||||
|
|
||||||
if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
|
if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
|
||||||
{
|
{
|
||||||
serialisedSceneObjects.Add(Encoding.UTF8.GetString(data));
|
sceneContext.SerialisedSceneObjects.Add(Encoding.UTF8.GetString(data));
|
||||||
}
|
}
|
||||||
else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH) && !m_skipAssets)
|
else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH) && !m_skipAssets)
|
||||||
{
|
{
|
||||||
|
@ -192,19 +245,19 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
}
|
}
|
||||||
else if (!m_merge && filePath.StartsWith(ArchiveConstants.TERRAINS_PATH))
|
else if (!m_merge && filePath.StartsWith(ArchiveConstants.TERRAINS_PATH))
|
||||||
{
|
{
|
||||||
LoadTerrain(filePath, data);
|
LoadTerrain(scene, filePath, data);
|
||||||
}
|
}
|
||||||
else if (!m_merge && filePath.StartsWith(ArchiveConstants.SETTINGS_PATH))
|
else if (!m_merge && filePath.StartsWith(ArchiveConstants.SETTINGS_PATH))
|
||||||
{
|
{
|
||||||
LoadRegionSettings(filePath, data);
|
LoadRegionSettings(scene, filePath, data, dearchivedScenes);
|
||||||
}
|
}
|
||||||
else if (!m_merge && filePath.StartsWith(ArchiveConstants.LANDDATA_PATH))
|
else if (!m_merge && filePath.StartsWith(ArchiveConstants.LANDDATA_PATH))
|
||||||
{
|
{
|
||||||
serialisedParcels.Add(Encoding.UTF8.GetString(data));
|
sceneContext.SerialisedParcels.Add(Encoding.UTF8.GetString(data));
|
||||||
}
|
}
|
||||||
else if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
|
else if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
|
||||||
{
|
{
|
||||||
LoadControlFile(filePath, data);
|
// Ignore, because we already read the control file
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,15 +265,16 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
m_log.ErrorFormat(
|
m_log.Error(
|
||||||
"[ARCHIVER]: Aborting load with error in archive file {0}. {1}", filePath, e);
|
String.Format("[ARCHIVER]: Aborting load with error in archive file {0} ", fullPath), e);
|
||||||
m_errorMessage += e.ToString();
|
m_errorMessage += e.ToString();
|
||||||
m_scene.EventManager.TriggerOarFileLoaded(m_requestId, m_errorMessage);
|
m_rootScene.EventManager.TriggerOarFileLoaded(m_requestId, new List<UUID>(), m_errorMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
archive.Close();
|
if (archive != null)
|
||||||
|
archive.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_skipAssets)
|
if (!m_skipAssets)
|
||||||
|
@ -234,32 +288,143 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_merge)
|
foreach (DearchiveContext sceneContext in sceneContexts.Values)
|
||||||
{
|
{
|
||||||
m_log.Info("[ARCHIVER]: Clearing all existing scene objects");
|
m_log.InfoFormat("[ARCHIVER:] Loading region {0}", sceneContext.Scene.RegionInfo.RegionName);
|
||||||
m_scene.DeleteAllSceneObjects();
|
|
||||||
|
if (!m_merge)
|
||||||
|
{
|
||||||
|
m_log.Info("[ARCHIVER]: Clearing all existing scene objects");
|
||||||
|
sceneContext.Scene.DeleteAllSceneObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LoadParcels(sceneContext.Scene, sceneContext.SerialisedParcels);
|
||||||
|
LoadObjects(sceneContext.Scene, sceneContext.SerialisedSceneObjects, sceneContext.SceneObjects);
|
||||||
|
|
||||||
|
// Inform any interested parties that the region has changed. We waited until now so that all
|
||||||
|
// of the region's objects will be loaded when we send this notification.
|
||||||
|
IEstateModule estateModule = sceneContext.Scene.RequestModuleInterface<IEstateModule>();
|
||||||
|
if (estateModule != null)
|
||||||
|
estateModule.TriggerRegionInfoChange();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
m_log.Error("[ARCHIVER]: Error loading parcels or objects ", e);
|
||||||
|
m_errorMessage += e.ToString();
|
||||||
|
m_rootScene.EventManager.TriggerOarFileLoaded(m_requestId, new List<UUID>(), m_errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadParcels(serialisedParcels);
|
// Start the scripts. We delayed this because we want the OAR to finish loading ASAP, so
|
||||||
LoadObjects(serialisedSceneObjects);
|
// that users can enter the scene. If we allow the scripts to start in the loop above
|
||||||
|
// then they significantly increase the time until the OAR finishes loading.
|
||||||
|
Util.FireAndForget(delegate(object o)
|
||||||
|
{
|
||||||
|
Thread.Sleep(15000);
|
||||||
|
m_log.Info("Starting scripts in scene objects");
|
||||||
|
|
||||||
|
foreach (DearchiveContext sceneContext in sceneContexts.Values)
|
||||||
|
{
|
||||||
|
foreach (SceneObjectGroup sceneObject in sceneContext.SceneObjects)
|
||||||
|
{
|
||||||
|
sceneObject.CreateScriptInstances(0, false, sceneContext.Scene.DefaultScriptEngine, 0); // StateSource.RegionStart
|
||||||
|
sceneObject.ResumeScripts();
|
||||||
|
}
|
||||||
|
|
||||||
|
sceneContext.SceneObjects.Clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
m_log.InfoFormat("[ARCHIVER]: Successfully loaded archive");
|
m_log.InfoFormat("[ARCHIVER]: Successfully loaded archive");
|
||||||
|
|
||||||
m_scene.EventManager.TriggerOarFileLoaded(m_requestId, m_errorMessage);
|
m_rootScene.EventManager.TriggerOarFileLoaded(m_requestId, dearchivedScenes.GetLoadedScenes(), m_errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches through the files in the archive for the control file, and reads it.
|
||||||
|
/// We must read the control file first, in order to know which regions are available.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// In most cases the control file *is* first, since that's how we create archives. However,
|
||||||
|
/// it's possible that someone rewrote the archive externally so we can't rely on this fact.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="archive"></param>
|
||||||
|
/// <param name="dearchivedScenes"></param>
|
||||||
|
private void FindAndLoadControlFile(out TarArchiveReader archive, out DearchiveScenesInfo dearchivedScenes)
|
||||||
|
{
|
||||||
|
archive = new TarArchiveReader(m_loadStream);
|
||||||
|
dearchivedScenes = new DearchiveScenesInfo();
|
||||||
|
|
||||||
|
string filePath;
|
||||||
|
byte[] data;
|
||||||
|
TarArchiveReader.TarEntryType entryType;
|
||||||
|
bool firstFile = true;
|
||||||
|
|
||||||
|
while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
|
||||||
|
{
|
||||||
|
if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
|
||||||
|
{
|
||||||
|
LoadControlFile(filePath, data, dearchivedScenes);
|
||||||
|
|
||||||
|
// Find which scenes are available in the simulator
|
||||||
|
ArchiveScenesGroup simulatorScenes = new ArchiveScenesGroup();
|
||||||
|
SceneManager.Instance.ForEachScene(delegate(Scene scene2)
|
||||||
|
{
|
||||||
|
simulatorScenes.AddScene(scene2);
|
||||||
|
});
|
||||||
|
simulatorScenes.CalcSceneLocations();
|
||||||
|
dearchivedScenes.SetSimulatorScenes(m_rootScene, simulatorScenes);
|
||||||
|
|
||||||
|
// If the control file wasn't the first file then reset the read pointer
|
||||||
|
if (!firstFile)
|
||||||
|
{
|
||||||
|
m_log.Warn("Control file wasn't the first file in the archive");
|
||||||
|
if (m_loadStream.CanSeek)
|
||||||
|
{
|
||||||
|
m_loadStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
else if (m_loadPath != null)
|
||||||
|
{
|
||||||
|
archive.Close();
|
||||||
|
archive = null;
|
||||||
|
m_loadStream.Close();
|
||||||
|
m_loadStream = null;
|
||||||
|
m_loadStream = new GZipStream(ArchiveHelpers.GetStream(m_loadPath), CompressionMode.Decompress);
|
||||||
|
archive = new TarArchiveReader(m_loadStream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// There isn't currently a scenario where this happens, but it's best to add a check just in case
|
||||||
|
throw new Exception("Error reading archive: control file wasn't the first file, and the input stream doesn't allow seeking");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstFile = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("Control file not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Load serialized scene objects.
|
/// Load serialized scene objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="serialisedSceneObjects"></param>
|
protected void LoadObjects(Scene scene, List<string> serialisedSceneObjects, List<SceneObjectGroup> sceneObjects)
|
||||||
protected void LoadObjects(List<string> serialisedSceneObjects)
|
|
||||||
{
|
{
|
||||||
// Reload serialized prims
|
// Reload serialized prims
|
||||||
m_log.InfoFormat("[ARCHIVER]: Loading {0} scene objects. Please wait.", serialisedSceneObjects.Count);
|
m_log.InfoFormat("[ARCHIVER]: Loading {0} scene objects. Please wait.", serialisedSceneObjects.Count);
|
||||||
|
|
||||||
UUID oldTelehubUUID = m_scene.RegionInfo.RegionSettings.TelehubObject;
|
UUID oldTelehubUUID = scene.RegionInfo.RegionSettings.TelehubObject;
|
||||||
|
|
||||||
IRegionSerialiserModule serialiser = m_scene.RequestModuleInterface<IRegionSerialiserModule>();
|
IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>();
|
||||||
int sceneObjectsLoadedCount = 0;
|
int sceneObjectsLoadedCount = 0;
|
||||||
|
|
||||||
foreach (string serialisedSceneObject in serialisedSceneObjects)
|
foreach (string serialisedSceneObject in serialisedSceneObjects)
|
||||||
|
@ -280,7 +445,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
|
|
||||||
SceneObjectGroup sceneObject = serialiser.DeserializeGroupFromXml2(serialisedSceneObject);
|
SceneObjectGroup sceneObject = serialiser.DeserializeGroupFromXml2(serialisedSceneObject);
|
||||||
|
|
||||||
bool isTelehub = (sceneObject.UUID == oldTelehubUUID);
|
bool isTelehub = (sceneObject.UUID == oldTelehubUUID) && (oldTelehubUUID != UUID.Zero);
|
||||||
|
|
||||||
// For now, give all incoming scene objects new uuids. This will allow scenes to be cloned
|
// For now, give all incoming scene objects new uuids. This will allow scenes to be cloned
|
||||||
// on the same region server and multiple examples a single object archive to be imported
|
// on the same region server and multiple examples a single object archive to be imported
|
||||||
|
@ -290,8 +455,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
if (isTelehub)
|
if (isTelehub)
|
||||||
{
|
{
|
||||||
// Change the Telehub Object to the new UUID
|
// Change the Telehub Object to the new UUID
|
||||||
m_scene.RegionInfo.RegionSettings.TelehubObject = sceneObject.UUID;
|
scene.RegionInfo.RegionSettings.TelehubObject = sceneObject.UUID;
|
||||||
m_scene.RegionInfo.RegionSettings.Save();
|
scene.RegionInfo.RegionSettings.Save();
|
||||||
oldTelehubUUID = UUID.Zero;
|
oldTelehubUUID = UUID.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,17 +466,17 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
{
|
{
|
||||||
if (part.CreatorData == null || part.CreatorData == string.Empty)
|
if (part.CreatorData == null || part.CreatorData == string.Empty)
|
||||||
{
|
{
|
||||||
if (!ResolveUserUuid(part.CreatorID))
|
if (!ResolveUserUuid(scene, part.CreatorID))
|
||||||
part.CreatorID = m_scene.RegionInfo.EstateSettings.EstateOwner;
|
part.CreatorID = scene.RegionInfo.EstateSettings.EstateOwner;
|
||||||
}
|
}
|
||||||
if (UserManager != null)
|
if (UserManager != null)
|
||||||
UserManager.AddUser(part.CreatorID, part.CreatorData);
|
UserManager.AddUser(part.CreatorID, part.CreatorData);
|
||||||
|
|
||||||
if (!ResolveUserUuid(part.OwnerID))
|
if (!ResolveUserUuid(scene, part.OwnerID))
|
||||||
part.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
|
part.OwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
|
||||||
|
|
||||||
if (!ResolveUserUuid(part.LastOwnerID))
|
if (!ResolveUserUuid(scene, part.LastOwnerID))
|
||||||
part.LastOwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
|
part.LastOwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
|
||||||
|
|
||||||
if (!ResolveGroupUuid(part.GroupID))
|
if (!ResolveGroupUuid(part.GroupID))
|
||||||
part.GroupID = UUID.Zero;
|
part.GroupID = UUID.Zero;
|
||||||
|
@ -328,15 +493,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
TaskInventoryDictionary inv = part.TaskInventory;
|
TaskInventoryDictionary inv = part.TaskInventory;
|
||||||
foreach (KeyValuePair<UUID, TaskInventoryItem> kvp in inv)
|
foreach (KeyValuePair<UUID, TaskInventoryItem> kvp in inv)
|
||||||
{
|
{
|
||||||
if (!ResolveUserUuid(kvp.Value.OwnerID))
|
if (!ResolveUserUuid(scene, kvp.Value.OwnerID))
|
||||||
{
|
{
|
||||||
kvp.Value.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
|
kvp.Value.OwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kvp.Value.CreatorData == null || kvp.Value.CreatorData == string.Empty)
|
if (kvp.Value.CreatorData == null || kvp.Value.CreatorData == string.Empty)
|
||||||
{
|
{
|
||||||
if (!ResolveUserUuid(kvp.Value.CreatorID))
|
if (!ResolveUserUuid(scene, kvp.Value.CreatorID))
|
||||||
kvp.Value.CreatorID = m_scene.RegionInfo.EstateSettings.EstateOwner;
|
kvp.Value.CreatorID = scene.RegionInfo.EstateSettings.EstateOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UserManager != null)
|
if (UserManager != null)
|
||||||
|
@ -348,10 +513,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_scene.AddRestoredSceneObject(sceneObject, true, false))
|
if (scene.AddRestoredSceneObject(sceneObject, true, false))
|
||||||
{
|
{
|
||||||
sceneObjectsLoadedCount++;
|
sceneObjectsLoadedCount++;
|
||||||
sceneObject.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, 0);
|
sceneObject.CreateScriptInstances(0, false, scene.DefaultScriptEngine, 0);
|
||||||
sceneObject.ResumeScripts();
|
sceneObject.ResumeScripts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,16 +531,17 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
if (oldTelehubUUID != UUID.Zero)
|
if (oldTelehubUUID != UUID.Zero)
|
||||||
{
|
{
|
||||||
m_log.WarnFormat("Telehub object not found: {0}", oldTelehubUUID);
|
m_log.WarnFormat("Telehub object not found: {0}", oldTelehubUUID);
|
||||||
m_scene.RegionInfo.RegionSettings.TelehubObject = UUID.Zero;
|
scene.RegionInfo.RegionSettings.TelehubObject = UUID.Zero;
|
||||||
m_scene.RegionInfo.RegionSettings.ClearSpawnPoints();
|
scene.RegionInfo.RegionSettings.ClearSpawnPoints();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Load serialized parcels.
|
/// Load serialized parcels.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="scene"></param>
|
||||||
/// <param name="serialisedParcels"></param>
|
/// <param name="serialisedParcels"></param>
|
||||||
protected void LoadParcels(List<string> serialisedParcels)
|
protected void LoadParcels(Scene scene, List<string> serialisedParcels)
|
||||||
{
|
{
|
||||||
// Reload serialized parcels
|
// Reload serialized parcels
|
||||||
m_log.InfoFormat("[ARCHIVER]: Loading {0} parcels. Please wait.", serialisedParcels.Count);
|
m_log.InfoFormat("[ARCHIVER]: Loading {0} parcels. Please wait.", serialisedParcels.Count);
|
||||||
|
@ -386,8 +552,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
|
|
||||||
// Validate User and Group UUID's
|
// Validate User and Group UUID's
|
||||||
|
|
||||||
if (!ResolveUserUuid(parcel.OwnerID))
|
if (!ResolveUserUuid(scene, parcel.OwnerID))
|
||||||
parcel.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
|
parcel.OwnerID = m_rootScene.RegionInfo.EstateSettings.EstateOwner;
|
||||||
|
|
||||||
if (!ResolveGroupUuid(parcel.GroupID))
|
if (!ResolveGroupUuid(parcel.GroupID))
|
||||||
{
|
{
|
||||||
|
@ -398,7 +564,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
List<LandAccessEntry> accessList = new List<LandAccessEntry>();
|
List<LandAccessEntry> accessList = new List<LandAccessEntry>();
|
||||||
foreach (LandAccessEntry entry in parcel.ParcelAccessList)
|
foreach (LandAccessEntry entry in parcel.ParcelAccessList)
|
||||||
{
|
{
|
||||||
if (ResolveUserUuid(entry.AgentID))
|
if (ResolveUserUuid(scene, entry.AgentID))
|
||||||
accessList.Add(entry);
|
accessList.Add(entry);
|
||||||
// else, drop this access rule
|
// else, drop this access rule
|
||||||
}
|
}
|
||||||
|
@ -414,23 +580,24 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
if (!m_merge)
|
if (!m_merge)
|
||||||
{
|
{
|
||||||
bool setupDefaultParcel = (landData.Count == 0);
|
bool setupDefaultParcel = (landData.Count == 0);
|
||||||
m_scene.LandChannel.Clear(setupDefaultParcel);
|
scene.LandChannel.Clear(setupDefaultParcel);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_scene.EventManager.TriggerIncomingLandDataFromStorage(landData);
|
scene.EventManager.TriggerIncomingLandDataFromStorage(landData);
|
||||||
m_log.InfoFormat("[ARCHIVER]: Restored {0} parcels.", landData.Count);
|
m_log.InfoFormat("[ARCHIVER]: Restored {0} parcels.", landData.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Look up the given user id to check whether it's one that is valid for this grid.
|
/// Look up the given user id to check whether it's one that is valid for this grid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="scene"></param>
|
||||||
/// <param name="uuid"></param>
|
/// <param name="uuid"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private bool ResolveUserUuid(UUID uuid)
|
private bool ResolveUserUuid(Scene scene, UUID uuid)
|
||||||
{
|
{
|
||||||
if (!m_validUserUuids.ContainsKey(uuid))
|
if (!m_validUserUuids.ContainsKey(uuid))
|
||||||
{
|
{
|
||||||
UserAccount account = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid);
|
UserAccount account = scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, uuid);
|
||||||
m_validUserUuids.Add(uuid, account != null);
|
m_validUserUuids.Add(uuid, account != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,7 +652,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
string extension = filename.Substring(i);
|
string extension = filename.Substring(i);
|
||||||
string uuid = filename.Remove(filename.Length - extension.Length);
|
string uuid = filename.Remove(filename.Length - extension.Length);
|
||||||
|
|
||||||
if (m_scene.AssetService.GetMetadata(uuid) != null)
|
if (m_assetService.GetMetadata(uuid) != null)
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("[ARCHIVER]: found existing asset {0}",uuid);
|
// m_log.DebugFormat("[ARCHIVER]: found existing asset {0}",uuid);
|
||||||
return true;
|
return true;
|
||||||
|
@ -505,7 +672,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
|
|
||||||
// We're relying on the asset service to do the sensible thing and not store the asset if it already
|
// We're relying on the asset service to do the sensible thing and not store the asset if it already
|
||||||
// exists.
|
// exists.
|
||||||
m_scene.AssetService.Store(asset);
|
m_assetService.Store(asset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create layers on decode for image assets. This is likely to significantly increase the time to load archives so
|
* Create layers on decode for image assets. This is likely to significantly increase the time to load archives so
|
||||||
|
@ -533,12 +700,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Load region settings data
|
/// Load region settings data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="scene"></param>
|
||||||
/// <param name="settingsPath"></param>
|
/// <param name="settingsPath"></param>
|
||||||
/// <param name="data"></param>
|
/// <param name="data"></param>
|
||||||
|
/// <param name="dearchivedScenes"></param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// true if settings were loaded successfully, false otherwise
|
/// true if settings were loaded successfully, false otherwise
|
||||||
/// </returns>
|
/// </returns>
|
||||||
private bool LoadRegionSettings(string settingsPath, byte[] data)
|
private bool LoadRegionSettings(Scene scene, string settingsPath, byte[] data, DearchiveScenesInfo dearchivedScenes)
|
||||||
{
|
{
|
||||||
RegionSettings loadedRegionSettings;
|
RegionSettings loadedRegionSettings;
|
||||||
|
|
||||||
|
@ -554,7 +723,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegionSettings currentRegionSettings = m_scene.RegionInfo.RegionSettings;
|
RegionSettings currentRegionSettings = scene.RegionInfo.RegionSettings;
|
||||||
|
|
||||||
currentRegionSettings.AgentLimit = loadedRegionSettings.AgentLimit;
|
currentRegionSettings.AgentLimit = loadedRegionSettings.AgentLimit;
|
||||||
currentRegionSettings.AllowDamage = loadedRegionSettings.AllowDamage;
|
currentRegionSettings.AllowDamage = loadedRegionSettings.AllowDamage;
|
||||||
|
@ -591,12 +760,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
foreach (SpawnPoint sp in loadedRegionSettings.SpawnPoints())
|
foreach (SpawnPoint sp in loadedRegionSettings.SpawnPoints())
|
||||||
currentRegionSettings.AddSpawnPoint(sp);
|
currentRegionSettings.AddSpawnPoint(sp);
|
||||||
|
|
||||||
|
currentRegionSettings.LoadedCreationDateTime = dearchivedScenes.LoadedCreationDateTime;
|
||||||
|
currentRegionSettings.LoadedCreationID = dearchivedScenes.GetOriginalRegionID(scene.RegionInfo.RegionID).ToString();
|
||||||
|
|
||||||
currentRegionSettings.Save();
|
currentRegionSettings.Save();
|
||||||
|
|
||||||
m_scene.TriggerEstateSunUpdate();
|
scene.TriggerEstateSunUpdate();
|
||||||
|
|
||||||
IEstateModule estateModule = m_scene.RequestModuleInterface<IEstateModule>();
|
IEstateModule estateModule = scene.RequestModuleInterface<IEstateModule>();
|
||||||
|
|
||||||
if (estateModule != null)
|
if (estateModule != null)
|
||||||
estateModule.sendRegionHandshakeToAll();
|
estateModule.sendRegionHandshakeToAll();
|
||||||
|
|
||||||
|
@ -606,14 +777,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Load terrain data
|
/// Load terrain data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="scene"></param>
|
||||||
/// <param name="terrainPath"></param>
|
/// <param name="terrainPath"></param>
|
||||||
/// <param name="data"></param>
|
/// <param name="data"></param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// true if terrain was resolved successfully, false otherwise.
|
/// true if terrain was resolved successfully, false otherwise.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
private bool LoadTerrain(string terrainPath, byte[] data)
|
private bool LoadTerrain(Scene scene, string terrainPath, byte[] data)
|
||||||
{
|
{
|
||||||
ITerrainModule terrainModule = m_scene.RequestModuleInterface<ITerrainModule>();
|
ITerrainModule terrainModule = scene.RequestModuleInterface<ITerrainModule>();
|
||||||
|
|
||||||
MemoryStream ms = new MemoryStream(data);
|
MemoryStream ms = new MemoryStream(data);
|
||||||
terrainModule.LoadFromStream(terrainPath, ms);
|
terrainModule.LoadFromStream(terrainPath, ms);
|
||||||
|
@ -629,17 +801,18 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path"></param>
|
/// <param name="path"></param>
|
||||||
/// <param name="data"></param>
|
/// <param name="data"></param>
|
||||||
public void LoadControlFile(string path, byte[] data)
|
/// <param name="dearchivedScenes"></param>
|
||||||
|
public DearchiveScenesInfo LoadControlFile(string path, byte[] data, DearchiveScenesInfo dearchivedScenes)
|
||||||
{
|
{
|
||||||
XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
|
XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
|
||||||
XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
|
XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
|
||||||
XmlTextReader xtr = new XmlTextReader(Encoding.ASCII.GetString(data), XmlNodeType.Document, context);
|
XmlTextReader xtr = new XmlTextReader(Encoding.ASCII.GetString(data), XmlNodeType.Document, context);
|
||||||
|
|
||||||
RegionSettings currentRegionSettings = m_scene.RegionInfo.RegionSettings;
|
// Loaded metadata will be empty if no information exists in the archive
|
||||||
|
dearchivedScenes.LoadedCreationDateTime = 0;
|
||||||
|
dearchivedScenes.DefaultOriginalID = "";
|
||||||
|
|
||||||
// Loaded metadata will empty if no information exists in the archive
|
bool multiRegion = false;
|
||||||
currentRegionSettings.LoadedCreationDateTime = 0;
|
|
||||||
currentRegionSettings.LoadedCreationID = "";
|
|
||||||
|
|
||||||
while (xtr.Read())
|
while (xtr.Read())
|
||||||
{
|
{
|
||||||
|
@ -665,18 +838,44 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
{
|
{
|
||||||
int value;
|
int value;
|
||||||
if (Int32.TryParse(xtr.ReadElementContentAsString(), out value))
|
if (Int32.TryParse(xtr.ReadElementContentAsString(), out value))
|
||||||
currentRegionSettings.LoadedCreationDateTime = value;
|
dearchivedScenes.LoadedCreationDateTime = value;
|
||||||
}
|
}
|
||||||
else if (xtr.Name.ToString() == "id")
|
else if (xtr.Name.ToString() == "row")
|
||||||
{
|
{
|
||||||
currentRegionSettings.LoadedCreationID = xtr.ReadElementContentAsString();
|
multiRegion = true;
|
||||||
|
dearchivedScenes.StartRow();
|
||||||
|
}
|
||||||
|
else if (xtr.Name.ToString() == "region")
|
||||||
|
{
|
||||||
|
dearchivedScenes.StartRegion();
|
||||||
|
}
|
||||||
|
else if (xtr.Name.ToString() == "id")
|
||||||
|
{
|
||||||
|
string id = xtr.ReadElementContentAsString();
|
||||||
|
dearchivedScenes.DefaultOriginalID = id;
|
||||||
|
if (multiRegion)
|
||||||
|
dearchivedScenes.SetRegionOriginalID(id);
|
||||||
|
}
|
||||||
|
else if (xtr.Name.ToString() == "dir")
|
||||||
|
{
|
||||||
|
dearchivedScenes.SetRegionDirectory(xtr.ReadElementContentAsString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentRegionSettings.Save();
|
dearchivedScenes.MultiRegionFormat = multiRegion;
|
||||||
|
if (!multiRegion)
|
||||||
|
{
|
||||||
|
// Add the single scene
|
||||||
|
dearchivedScenes.StartRow();
|
||||||
|
dearchivedScenes.StartRegion();
|
||||||
|
dearchivedScenes.SetRegionOriginalID(dearchivedScenes.DefaultOriginalID);
|
||||||
|
dearchivedScenes.SetRegionDirectory("");
|
||||||
|
}
|
||||||
|
|
||||||
ControlFileLoaded = true;
|
ControlFileLoaded = true;
|
||||||
|
|
||||||
|
return dearchivedScenes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
/*
|
||||||
|
* 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.Text;
|
||||||
|
using OpenSim.Region.Framework.Scenes;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A group of regions arranged in a rectangle, possibly with holes.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The regions usually (but not necessarily) belong to an archive file, in which case we
|
||||||
|
/// store additional information used to create the archive (e.g., each region's
|
||||||
|
/// directory within the archive).
|
||||||
|
/// </remarks>
|
||||||
|
public class ArchiveScenesGroup
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// All the regions. The outer dictionary contains rows (key: Y coordinate).
|
||||||
|
/// The inner dictionaries contain each row's regions (key: X coordinate).
|
||||||
|
/// </summary>
|
||||||
|
public SortedDictionary<uint, SortedDictionary<uint, Scene>> Regions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The subdirectory where each region is stored in the archive.
|
||||||
|
/// </summary>
|
||||||
|
protected Dictionary<UUID, string> m_regionDirs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The grid coordinates of the regions' bounding box.
|
||||||
|
/// </summary>
|
||||||
|
public Rectangle Rect { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public ArchiveScenesGroup()
|
||||||
|
{
|
||||||
|
Regions = new SortedDictionary<uint, SortedDictionary<uint, Scene>>();
|
||||||
|
m_regionDirs = new Dictionary<UUID, string>();
|
||||||
|
Rect = new Rectangle(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddScene(Scene scene)
|
||||||
|
{
|
||||||
|
uint x = scene.RegionInfo.RegionLocX;
|
||||||
|
uint y = scene.RegionInfo.RegionLocY;
|
||||||
|
|
||||||
|
SortedDictionary<uint, Scene> row;
|
||||||
|
if (!Regions.TryGetValue(y, out row))
|
||||||
|
{
|
||||||
|
row = new SortedDictionary<uint, Scene>();
|
||||||
|
Regions[y] = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
row[x] = scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after all the scenes have been added. Performs calculations that require
|
||||||
|
/// knowledge of all the scenes.
|
||||||
|
/// </summary>
|
||||||
|
public void CalcSceneLocations()
|
||||||
|
{
|
||||||
|
if (Regions.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find the bounding rectangle
|
||||||
|
|
||||||
|
uint firstY = Regions.First().Key;
|
||||||
|
uint lastY = Regions.Last().Key;
|
||||||
|
|
||||||
|
uint? firstX = null;
|
||||||
|
uint? lastX = null;
|
||||||
|
|
||||||
|
foreach (SortedDictionary<uint, Scene> row in Regions.Values)
|
||||||
|
{
|
||||||
|
uint curFirstX = row.First().Key;
|
||||||
|
uint curLastX = row.Last().Key;
|
||||||
|
|
||||||
|
firstX = (firstX == null) ? curFirstX : (firstX < curFirstX) ? firstX : curFirstX;
|
||||||
|
lastX = (lastX == null) ? curLastX : (lastX > curLastX) ? lastX : curLastX;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect = new Rectangle((int)firstX, (int)firstY, (int)(lastY - firstY + 1), (int)(lastX - firstX + 1));
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate the subdirectory in which each region will be stored in the archive
|
||||||
|
|
||||||
|
m_regionDirs.Clear();
|
||||||
|
ForEachScene(delegate(Scene scene)
|
||||||
|
{
|
||||||
|
// We add the region's coordinates to ensure uniqueness even if multiple regions have the same name
|
||||||
|
string path = string.Format("{0}_{1}_{2}",
|
||||||
|
scene.RegionInfo.RegionLocX - Rect.X + 1,
|
||||||
|
scene.RegionInfo.RegionLocY - Rect.Y + 1,
|
||||||
|
scene.RegionInfo.RegionName.Replace(' ', '_'));
|
||||||
|
m_regionDirs[scene.RegionInfo.RegionID] = path;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the subdirectory where the region is stored.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="regionID"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string GetRegionDir(UUID regionID)
|
||||||
|
{
|
||||||
|
return m_regionDirs[regionID];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs an action on all the scenes in this order: rows from South to North,
|
||||||
|
/// and within each row West to East.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
public void ForEachScene(Action<Scene> action)
|
||||||
|
{
|
||||||
|
foreach (SortedDictionary<uint, Scene> row in Regions.Values)
|
||||||
|
{
|
||||||
|
foreach (Scene scene in row.Values)
|
||||||
|
{
|
||||||
|
action(scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the scene at position 'location'.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">A location in the grid</param>
|
||||||
|
/// <param name="scene">The scene at this location</param>
|
||||||
|
/// <returns>Whether the scene was found</returns>
|
||||||
|
public bool TryGetScene(Point location, out Scene scene)
|
||||||
|
{
|
||||||
|
SortedDictionary<uint, Scene> row;
|
||||||
|
if (Regions.TryGetValue((uint)location.Y, out row))
|
||||||
|
{
|
||||||
|
if (row.TryGetValue((uint)location.X, out scene))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
scene = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,634 @@
|
||||||
|
/*
|
||||||
|
* 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.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Xml;
|
||||||
|
using log4net;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Framework.Serialization;
|
||||||
|
using OpenSim.Region.CoreModules.World.Terrain;
|
||||||
|
using OpenSim.Region.Framework.Interfaces;
|
||||||
|
using OpenSim.Region.Framework.Scenes;
|
||||||
|
using Ionic.Zlib;
|
||||||
|
using GZipStream = Ionic.Zlib.GZipStream;
|
||||||
|
using CompressionMode = Ionic.Zlib.CompressionMode;
|
||||||
|
using OpenSim.Framework.Serialization.External;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prepare to write out an archive.
|
||||||
|
/// </summary>
|
||||||
|
public class ArchiveWriteRequest
|
||||||
|
{
|
||||||
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum major version of OAR that we can write.
|
||||||
|
/// </summary>
|
||||||
|
public static int MIN_MAJOR_VERSION = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum major version of OAR that we can write.
|
||||||
|
/// </summary>
|
||||||
|
public static int MAX_MAJOR_VERSION = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether we're saving a multi-region archive.
|
||||||
|
/// </summary>
|
||||||
|
public bool MultiRegionFormat { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determine whether this archive will save assets. Default is true.
|
||||||
|
/// </summary>
|
||||||
|
public bool SaveAssets { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines which objects will be included in the archive, according to their permissions.
|
||||||
|
/// Default is null, meaning no permission checks.
|
||||||
|
/// </summary>
|
||||||
|
public string CheckPermissions { get; set; }
|
||||||
|
|
||||||
|
protected Scene m_rootScene;
|
||||||
|
protected Stream m_saveStream;
|
||||||
|
protected TarArchiveWriter m_archiveWriter;
|
||||||
|
protected Guid m_requestId;
|
||||||
|
protected Dictionary<string, object> m_options;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">Calling module</param>
|
||||||
|
/// <param name="savePath">The path to which to save data.</param>
|
||||||
|
/// <param name="requestId">The id associated with this request</param>
|
||||||
|
/// <exception cref="System.IO.IOException">
|
||||||
|
/// If there was a problem opening a stream for the file specified by the savePath
|
||||||
|
/// </exception>
|
||||||
|
public ArchiveWriteRequest(Scene scene, string savePath, Guid requestId) : this(scene, requestId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress, CompressionLevel.BestCompression);
|
||||||
|
}
|
||||||
|
catch (EntryPointNotFoundException e)
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat(
|
||||||
|
"[ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
|
||||||
|
+ "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
|
||||||
|
m_log.ErrorFormat("{0} {1}", e.Message, e.StackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scene">The root scene to archive</param>
|
||||||
|
/// <param name="saveStream">The stream to which to save data.</param>
|
||||||
|
/// <param name="requestId">The id associated with this request</param>
|
||||||
|
public ArchiveWriteRequest(Scene scene, Stream saveStream, Guid requestId) : this(scene, requestId)
|
||||||
|
{
|
||||||
|
m_saveStream = saveStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ArchiveWriteRequest(Scene scene, Guid requestId)
|
||||||
|
{
|
||||||
|
m_rootScene = scene;
|
||||||
|
m_requestId = requestId;
|
||||||
|
m_archiveWriter = null;
|
||||||
|
|
||||||
|
MultiRegionFormat = false;
|
||||||
|
SaveAssets = true;
|
||||||
|
CheckPermissions = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Archive the region requested.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception>
|
||||||
|
public void ArchiveRegion(Dictionary<string, object> options)
|
||||||
|
{
|
||||||
|
m_options = options;
|
||||||
|
|
||||||
|
if (options.ContainsKey("all") && (bool)options["all"])
|
||||||
|
MultiRegionFormat = true;
|
||||||
|
|
||||||
|
if (options.ContainsKey("noassets") && (bool)options["noassets"])
|
||||||
|
SaveAssets = false;
|
||||||
|
|
||||||
|
Object temp;
|
||||||
|
if (options.TryGetValue("checkPermissions", out temp))
|
||||||
|
CheckPermissions = (string)temp;
|
||||||
|
|
||||||
|
|
||||||
|
// Find the regions to archive
|
||||||
|
ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();
|
||||||
|
if (MultiRegionFormat)
|
||||||
|
{
|
||||||
|
m_log.InfoFormat("[ARCHIVER]: Saving {0} regions", SceneManager.Instance.Scenes.Count);
|
||||||
|
SceneManager.Instance.ForEachScene(delegate(Scene scene)
|
||||||
|
{
|
||||||
|
scenesGroup.AddScene(scene);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scenesGroup.AddScene(m_rootScene);
|
||||||
|
}
|
||||||
|
scenesGroup.CalcSceneLocations();
|
||||||
|
|
||||||
|
|
||||||
|
m_archiveWriter = new TarArchiveWriter(m_saveStream);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Write out control file. It should be first so that it will be found ASAP when loading the file.
|
||||||
|
m_archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(scenesGroup));
|
||||||
|
m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");
|
||||||
|
|
||||||
|
// Archive the regions
|
||||||
|
|
||||||
|
Dictionary<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>();
|
||||||
|
|
||||||
|
scenesGroup.ForEachScene(delegate(Scene scene)
|
||||||
|
{
|
||||||
|
string regionDir = MultiRegionFormat ? scenesGroup.GetRegionDir(scene.RegionInfo.RegionID) : "";
|
||||||
|
ArchiveOneRegion(scene, regionDir, assetUuids);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Archive the assets
|
||||||
|
|
||||||
|
if (SaveAssets)
|
||||||
|
{
|
||||||
|
m_log.DebugFormat("[ARCHIVER]: Saving {0} assets", assetUuids.Count);
|
||||||
|
|
||||||
|
// Asynchronously request all the assets required to perform this archive operation
|
||||||
|
AssetsRequest ar
|
||||||
|
= new AssetsRequest(
|
||||||
|
new AssetsArchiver(m_archiveWriter), assetUuids,
|
||||||
|
m_rootScene.AssetService, m_rootScene.UserAccountService,
|
||||||
|
m_rootScene.RegionInfo.ScopeID, options, ReceivedAllAssets);
|
||||||
|
|
||||||
|
Util.FireAndForget(o => ar.Execute());
|
||||||
|
|
||||||
|
// CloseArchive() will be called from ReceivedAllAssets()
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified");
|
||||||
|
CloseArchive(string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
CloseArchive(e.Message);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ArchiveOneRegion(Scene scene, string regionDir, Dictionary<UUID, AssetType> assetUuids)
|
||||||
|
{
|
||||||
|
m_log.InfoFormat("[ARCHIVER]: Writing region {0}", scene.RegionInfo.RegionName);
|
||||||
|
|
||||||
|
EntityBase[] entities = scene.GetEntities();
|
||||||
|
List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>();
|
||||||
|
|
||||||
|
int numObjectsSkippedPermissions = 0;
|
||||||
|
|
||||||
|
// Filter entities so that we only have scene objects.
|
||||||
|
// FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods
|
||||||
|
// end up having to do this
|
||||||
|
IPermissionsModule permissionsModule = scene.RequestModuleInterface<IPermissionsModule>();
|
||||||
|
foreach (EntityBase entity in entities)
|
||||||
|
{
|
||||||
|
if (entity is SceneObjectGroup)
|
||||||
|
{
|
||||||
|
SceneObjectGroup sceneObject = (SceneObjectGroup)entity;
|
||||||
|
|
||||||
|
if (!sceneObject.IsDeleted && !sceneObject.IsAttachment)
|
||||||
|
{
|
||||||
|
if (!CanUserArchiveObject(scene.RegionInfo.EstateSettings.EstateOwner, sceneObject, CheckPermissions, permissionsModule))
|
||||||
|
{
|
||||||
|
// The user isn't allowed to copy/transfer this object, so it will not be included in the OAR.
|
||||||
|
++numObjectsSkippedPermissions;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sceneObjects.Add(sceneObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SaveAssets)
|
||||||
|
{
|
||||||
|
UuidGatherer assetGatherer = new UuidGatherer(scene.AssetService);
|
||||||
|
int prevAssets = assetUuids.Count;
|
||||||
|
|
||||||
|
foreach (SceneObjectGroup sceneObject in sceneObjects)
|
||||||
|
{
|
||||||
|
assetGatherer.GatherAssetUuids(sceneObject, assetUuids);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_log.DebugFormat(
|
||||||
|
"[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets",
|
||||||
|
sceneObjects.Count, assetUuids.Count - prevAssets);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numObjectsSkippedPermissions > 0)
|
||||||
|
{
|
||||||
|
m_log.DebugFormat(
|
||||||
|
"[ARCHIVER]: {0} scene objects skipped due to lack of permissions",
|
||||||
|
numObjectsSkippedPermissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that we also request terrain texture assets
|
||||||
|
RegionSettings regionSettings = scene.RegionInfo.RegionSettings;
|
||||||
|
|
||||||
|
if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1)
|
||||||
|
assetUuids[regionSettings.TerrainTexture1] = AssetType.Texture;
|
||||||
|
|
||||||
|
if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2)
|
||||||
|
assetUuids[regionSettings.TerrainTexture2] = AssetType.Texture;
|
||||||
|
|
||||||
|
if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3)
|
||||||
|
assetUuids[regionSettings.TerrainTexture3] = AssetType.Texture;
|
||||||
|
|
||||||
|
if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4)
|
||||||
|
assetUuids[regionSettings.TerrainTexture4] = AssetType.Texture;
|
||||||
|
|
||||||
|
Save(scene, sceneObjects, regionDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the user has permission to export an object group to an OAR.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The user</param>
|
||||||
|
/// <param name="objGroup">The object group</param>
|
||||||
|
/// <param name="checkPermissions">Which permissions to check: "C" = Copy, "T" = Transfer</param>
|
||||||
|
/// <param name="permissionsModule">The scene's permissions module</param>
|
||||||
|
/// <returns>Whether the user is allowed to export the object to an OAR</returns>
|
||||||
|
private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions, IPermissionsModule permissionsModule)
|
||||||
|
{
|
||||||
|
if (checkPermissions == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (permissionsModule == null)
|
||||||
|
return true; // this shouldn't happen
|
||||||
|
|
||||||
|
// Check whether the user is permitted to export all of the parts in the SOG. If any
|
||||||
|
// part can't be exported then the entire SOG can't be exported.
|
||||||
|
|
||||||
|
bool permitted = true;
|
||||||
|
//int primNumber = 1;
|
||||||
|
|
||||||
|
foreach (SceneObjectPart obj in objGroup.Parts)
|
||||||
|
{
|
||||||
|
uint perm;
|
||||||
|
PermissionClass permissionClass = permissionsModule.GetPermissionClass(user, obj);
|
||||||
|
switch (permissionClass)
|
||||||
|
{
|
||||||
|
case PermissionClass.Owner:
|
||||||
|
perm = obj.BaseMask;
|
||||||
|
break;
|
||||||
|
case PermissionClass.Group:
|
||||||
|
perm = obj.GroupMask | obj.EveryoneMask;
|
||||||
|
break;
|
||||||
|
case PermissionClass.Everyone:
|
||||||
|
default:
|
||||||
|
perm = obj.EveryoneMask;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canCopy = (perm & (uint)PermissionMask.Copy) != 0;
|
||||||
|
bool canTransfer = (perm & (uint)PermissionMask.Transfer) != 0;
|
||||||
|
|
||||||
|
// Special case: if Everyone can copy the object then this implies it can also be
|
||||||
|
// Transferred.
|
||||||
|
// However, if the user is the Owner then we don't check EveryoneMask, because it seems that the mask
|
||||||
|
// always (incorrectly) includes the Copy bit set in this case. But that's a mistake: the viewer
|
||||||
|
// does NOT show that the object has Everyone-Copy permissions, and doesn't allow it to be copied.
|
||||||
|
if (permissionClass != PermissionClass.Owner)
|
||||||
|
canTransfer |= (obj.EveryoneMask & (uint)PermissionMask.Copy) != 0;
|
||||||
|
|
||||||
|
bool partPermitted = true;
|
||||||
|
if (checkPermissions.Contains("C") && !canCopy)
|
||||||
|
partPermitted = false;
|
||||||
|
if (checkPermissions.Contains("T") && !canTransfer)
|
||||||
|
partPermitted = false;
|
||||||
|
|
||||||
|
// If the user is the Creator of the object then it can always be included in the OAR
|
||||||
|
bool creator = (obj.CreatorID.Guid == user.Guid);
|
||||||
|
if (creator)
|
||||||
|
partPermitted = true;
|
||||||
|
|
||||||
|
//string name = (objGroup.PrimCount == 1) ? objGroup.Name : string.Format("{0} ({1}/{2})", obj.Name, primNumber, objGroup.PrimCount);
|
||||||
|
//m_log.DebugFormat("[ARCHIVER]: Object permissions: {0}: Base={1:X4}, Owner={2:X4}, Everyone={3:X4}, permissionClass={4}, checkPermissions={5}, canCopy={6}, canTransfer={7}, creator={8}, permitted={9}",
|
||||||
|
// name, obj.BaseMask, obj.OwnerMask, obj.EveryoneMask,
|
||||||
|
// permissionClass, checkPermissions, canCopy, canTransfer, creator, partPermitted);
|
||||||
|
|
||||||
|
if (!partPermitted)
|
||||||
|
{
|
||||||
|
permitted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//++primNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
return permitted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create the control file.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string CreateControlFile(ArchiveScenesGroup scenesGroup)
|
||||||
|
{
|
||||||
|
int majorVersion;
|
||||||
|
int minorVersion;
|
||||||
|
|
||||||
|
if (MultiRegionFormat)
|
||||||
|
{
|
||||||
|
majorVersion = MAX_MAJOR_VERSION;
|
||||||
|
minorVersion = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// To support older versions of OpenSim, we continue to create single-region OARs
|
||||||
|
// using the old file format. In the future this format will be discontinued.
|
||||||
|
majorVersion = 0;
|
||||||
|
minorVersion = 8;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// if (m_options.ContainsKey("version"))
|
||||||
|
// {
|
||||||
|
// string[] parts = m_options["version"].ToString().Split('.');
|
||||||
|
// if (parts.Length >= 1)
|
||||||
|
// {
|
||||||
|
// majorVersion = Int32.Parse(parts[0]);
|
||||||
|
//
|
||||||
|
// if (parts.Length >= 2)
|
||||||
|
// minorVersion = Int32.Parse(parts[1]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (majorVersion < MIN_MAJOR_VERSION || majorVersion > MAX_MAJOR_VERSION)
|
||||||
|
// {
|
||||||
|
// throw new Exception(
|
||||||
|
// string.Format(
|
||||||
|
// "OAR version number for save must be between {0} and {1}",
|
||||||
|
// MIN_MAJOR_VERSION, MAX_MAJOR_VERSION));
|
||||||
|
// }
|
||||||
|
// else if (majorVersion == MAX_MAJOR_VERSION)
|
||||||
|
// {
|
||||||
|
// // Force 1.0
|
||||||
|
// minorVersion = 0;
|
||||||
|
// }
|
||||||
|
// else if (majorVersion == MIN_MAJOR_VERSION)
|
||||||
|
// {
|
||||||
|
// // Force 0.4
|
||||||
|
// minorVersion = 4;
|
||||||
|
// }
|
||||||
|
|
||||||
|
m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion);
|
||||||
|
if (majorVersion == 1)
|
||||||
|
{
|
||||||
|
m_log.WarnFormat("[ARCHIVER]: Please be aware that version 1.0 OARs are not compatible with OpenSim versions prior to 0.7.4. Do not use the --all option if you want to produce a compatible OAR");
|
||||||
|
}
|
||||||
|
|
||||||
|
String s;
|
||||||
|
|
||||||
|
using (StringWriter sw = new StringWriter())
|
||||||
|
{
|
||||||
|
using (XmlTextWriter xtw = new XmlTextWriter(sw))
|
||||||
|
{
|
||||||
|
xtw.Formatting = Formatting.Indented;
|
||||||
|
xtw.WriteStartDocument();
|
||||||
|
xtw.WriteStartElement("archive");
|
||||||
|
xtw.WriteAttributeString("major_version", majorVersion.ToString());
|
||||||
|
xtw.WriteAttributeString("minor_version", minorVersion.ToString());
|
||||||
|
|
||||||
|
xtw.WriteStartElement("creation_info");
|
||||||
|
DateTime now = DateTime.UtcNow;
|
||||||
|
TimeSpan t = now - new DateTime(1970, 1, 1);
|
||||||
|
xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString());
|
||||||
|
if (!MultiRegionFormat)
|
||||||
|
xtw.WriteElementString("id", m_rootScene.RegionInfo.RegionID.ToString());
|
||||||
|
xtw.WriteEndElement();
|
||||||
|
|
||||||
|
xtw.WriteElementString("assets_included", SaveAssets.ToString());
|
||||||
|
|
||||||
|
if (MultiRegionFormat)
|
||||||
|
{
|
||||||
|
WriteRegionsManifest(scenesGroup, xtw);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xtw.WriteStartElement("region_info");
|
||||||
|
WriteRegionInfo(m_rootScene, xtw);
|
||||||
|
xtw.WriteEndElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
xtw.WriteEndElement();
|
||||||
|
|
||||||
|
xtw.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
s = sw.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the list of regions included in a multi-region OAR.
|
||||||
|
/// </summary>
|
||||||
|
private static void WriteRegionsManifest(ArchiveScenesGroup scenesGroup, XmlTextWriter xtw)
|
||||||
|
{
|
||||||
|
xtw.WriteStartElement("regions");
|
||||||
|
|
||||||
|
// Write the regions in order: rows from South to North, then regions from West to East.
|
||||||
|
// The list of regions can have "holes"; we write empty elements in their position.
|
||||||
|
|
||||||
|
for (uint y = (uint)scenesGroup.Rect.Top; y < scenesGroup.Rect.Bottom; ++y)
|
||||||
|
{
|
||||||
|
SortedDictionary<uint, Scene> row;
|
||||||
|
if (scenesGroup.Regions.TryGetValue(y, out row))
|
||||||
|
{
|
||||||
|
xtw.WriteStartElement("row");
|
||||||
|
|
||||||
|
for (uint x = (uint)scenesGroup.Rect.Left; x < scenesGroup.Rect.Right; ++x)
|
||||||
|
{
|
||||||
|
Scene scene;
|
||||||
|
if (row.TryGetValue(x, out scene))
|
||||||
|
{
|
||||||
|
xtw.WriteStartElement("region");
|
||||||
|
xtw.WriteElementString("id", scene.RegionInfo.RegionID.ToString());
|
||||||
|
xtw.WriteElementString("dir", scenesGroup.GetRegionDir(scene.RegionInfo.RegionID));
|
||||||
|
WriteRegionInfo(scene, xtw);
|
||||||
|
xtw.WriteEndElement();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Write a placeholder for a missing region
|
||||||
|
xtw.WriteElementString("region", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xtw.WriteEndElement();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Write a placeholder for a missing row
|
||||||
|
xtw.WriteElementString("row", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xtw.WriteEndElement(); // "regions"
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void WriteRegionInfo(Scene scene, XmlTextWriter xtw)
|
||||||
|
{
|
||||||
|
bool isMegaregion;
|
||||||
|
Vector2 size;
|
||||||
|
|
||||||
|
IRegionCombinerModule rcMod = scene.RequestModuleInterface<IRegionCombinerModule>();
|
||||||
|
|
||||||
|
if (rcMod != null)
|
||||||
|
isMegaregion = rcMod.IsRootForMegaregion(scene.RegionInfo.RegionID);
|
||||||
|
else
|
||||||
|
isMegaregion = false;
|
||||||
|
|
||||||
|
if (isMegaregion)
|
||||||
|
size = rcMod.GetSizeOfMegaregion(scene.RegionInfo.RegionID);
|
||||||
|
else
|
||||||
|
size = new Vector2((float)Constants.RegionSize, (float)Constants.RegionSize);
|
||||||
|
|
||||||
|
xtw.WriteElementString("is_megaregion", isMegaregion.ToString());
|
||||||
|
xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void Save(Scene scene, List<SceneObjectGroup> sceneObjects, string regionDir)
|
||||||
|
{
|
||||||
|
if (regionDir != string.Empty)
|
||||||
|
regionDir = ArchiveConstants.REGIONS_PATH + regionDir + "/";
|
||||||
|
|
||||||
|
m_log.InfoFormat("[ARCHIVER]: Adding region settings to archive.");
|
||||||
|
|
||||||
|
// Write out region settings
|
||||||
|
string settingsPath = String.Format("{0}{1}{2}.xml",
|
||||||
|
regionDir, ArchiveConstants.SETTINGS_PATH, scene.RegionInfo.RegionName);
|
||||||
|
m_archiveWriter.WriteFile(settingsPath, RegionSettingsSerializer.Serialize(scene.RegionInfo.RegionSettings));
|
||||||
|
|
||||||
|
m_log.InfoFormat("[ARCHIVER]: Adding parcel settings to archive.");
|
||||||
|
|
||||||
|
// Write out land data (aka parcel) settings
|
||||||
|
List<ILandObject> landObjects = scene.LandChannel.AllParcels();
|
||||||
|
foreach (ILandObject lo in landObjects)
|
||||||
|
{
|
||||||
|
LandData landData = lo.LandData;
|
||||||
|
string landDataPath = String.Format("{0}{1}{2}.xml",
|
||||||
|
regionDir, ArchiveConstants.LANDDATA_PATH, landData.GlobalID.ToString());
|
||||||
|
m_archiveWriter.WriteFile(landDataPath, LandDataSerializer.Serialize(landData, m_options));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_log.InfoFormat("[ARCHIVER]: Adding terrain information to archive.");
|
||||||
|
|
||||||
|
// Write out terrain
|
||||||
|
string terrainPath = String.Format("{0}{1}{2}.r32",
|
||||||
|
regionDir, ArchiveConstants.TERRAINS_PATH, scene.RegionInfo.RegionName);
|
||||||
|
|
||||||
|
MemoryStream ms = new MemoryStream();
|
||||||
|
scene.RequestModuleInterface<ITerrainModule>().SaveToStream(terrainPath, ms);
|
||||||
|
m_archiveWriter.WriteFile(terrainPath, ms.ToArray());
|
||||||
|
ms.Close();
|
||||||
|
|
||||||
|
m_log.InfoFormat("[ARCHIVER]: Adding scene objects to archive.");
|
||||||
|
|
||||||
|
// Write out scene object metadata
|
||||||
|
IRegionSerialiserModule serializer = scene.RequestModuleInterface<IRegionSerialiserModule>();
|
||||||
|
foreach (SceneObjectGroup sceneObject in sceneObjects)
|
||||||
|
{
|
||||||
|
//m_log.DebugFormat("[ARCHIVER]: Saving {0} {1}, {2}", entity.Name, entity.UUID, entity.GetType());
|
||||||
|
|
||||||
|
string serializedObject = serializer.SerializeGroupToXml2(sceneObject, m_options);
|
||||||
|
string objectPath = string.Format("{0}{1}", regionDir, ArchiveHelpers.CreateObjectPath(sceneObject));
|
||||||
|
m_archiveWriter.WriteFile(objectPath, serializedObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ReceivedAllAssets(
|
||||||
|
ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids)
|
||||||
|
{
|
||||||
|
foreach (UUID uuid in assetsNotFoundUuids)
|
||||||
|
{
|
||||||
|
m_log.DebugFormat("[ARCHIVER]: Could not find asset {0}", uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// m_log.InfoFormat(
|
||||||
|
// "[ARCHIVER]: Received {0} of {1} assets requested",
|
||||||
|
// assetsFoundUuids.Count, assetsFoundUuids.Count + assetsNotFoundUuids.Count);
|
||||||
|
|
||||||
|
CloseArchive(String.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes the archive and notifies that we're done.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="errorMessage">The error that occurred, or empty for success</param>
|
||||||
|
protected void CloseArchive(string errorMessage)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (m_archiveWriter != null)
|
||||||
|
m_archiveWriter.Close();
|
||||||
|
m_saveStream.Close();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
m_log.Error(string.Format("[ARCHIVER]: Error closing archive: {0} ", e.Message), e);
|
||||||
|
if (errorMessage == string.Empty)
|
||||||
|
errorMessage = e.Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_log.InfoFormat("[ARCHIVER]: Finished writing out OAR for {0}", m_rootScene.RegionInfo.RegionName);
|
||||||
|
|
||||||
|
m_rootScene.EventManager.TriggerOarFileSaved(m_requestId, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,153 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Xml;
|
|
||||||
using log4net;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Framework.Serialization;
|
|
||||||
using OpenSim.Framework.Serialization.External;
|
|
||||||
using OpenSim.Region.CoreModules.World.Terrain;
|
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
|
||||||
using OpenSim.Region.Framework.Scenes;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.World.Archiver
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Method called when all the necessary assets for an archive request have been received.
|
|
||||||
/// </summary>
|
|
||||||
public delegate void AssetsRequestCallback(
|
|
||||||
ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Execute the write of an archive once we have received all the necessary data
|
|
||||||
/// </summary>
|
|
||||||
public class ArchiveWriteRequestExecution
|
|
||||||
{
|
|
||||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
||||||
|
|
||||||
protected ITerrainModule m_terrainModule;
|
|
||||||
protected IRegionSerialiserModule m_serialiser;
|
|
||||||
protected List<SceneObjectGroup> m_sceneObjects;
|
|
||||||
protected Scene m_scene;
|
|
||||||
protected TarArchiveWriter m_archiveWriter;
|
|
||||||
protected Guid m_requestId;
|
|
||||||
protected Dictionary<string, object> m_options;
|
|
||||||
|
|
||||||
public ArchiveWriteRequestExecution(
|
|
||||||
List<SceneObjectGroup> sceneObjects,
|
|
||||||
ITerrainModule terrainModule,
|
|
||||||
IRegionSerialiserModule serialiser,
|
|
||||||
Scene scene,
|
|
||||||
TarArchiveWriter archiveWriter,
|
|
||||||
Guid requestId,
|
|
||||||
Dictionary<string, object> options)
|
|
||||||
{
|
|
||||||
m_sceneObjects = sceneObjects;
|
|
||||||
m_terrainModule = terrainModule;
|
|
||||||
m_serialiser = serialiser;
|
|
||||||
m_scene = scene;
|
|
||||||
m_archiveWriter = archiveWriter;
|
|
||||||
m_requestId = requestId;
|
|
||||||
m_options = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected internal void ReceivedAllAssets(
|
|
||||||
ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Save(assetsFoundUuids, assetsNotFoundUuids);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
m_archiveWriter.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_log.InfoFormat("[ARCHIVER]: Finished writing out OAR for {0}", m_scene.RegionInfo.RegionName);
|
|
||||||
|
|
||||||
m_scene.EventManager.TriggerOarFileSaved(m_requestId, String.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected internal void Save(ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids)
|
|
||||||
{
|
|
||||||
foreach (UUID uuid in assetsNotFoundUuids)
|
|
||||||
{
|
|
||||||
m_log.DebugFormat("[ARCHIVER]: Could not find asset {0}", uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// m_log.InfoFormat(
|
|
||||||
// "[ARCHIVER]: Received {0} of {1} assets requested",
|
|
||||||
// assetsFoundUuids.Count, assetsFoundUuids.Count + assetsNotFoundUuids.Count);
|
|
||||||
|
|
||||||
m_log.InfoFormat("[ARCHIVER]: Adding region settings to archive.");
|
|
||||||
|
|
||||||
// Write out region settings
|
|
||||||
string settingsPath
|
|
||||||
= String.Format("{0}{1}.xml", ArchiveConstants.SETTINGS_PATH, m_scene.RegionInfo.RegionName);
|
|
||||||
m_archiveWriter.WriteFile(settingsPath, RegionSettingsSerializer.Serialize(m_scene.RegionInfo.RegionSettings));
|
|
||||||
|
|
||||||
m_log.InfoFormat("[ARCHIVER]: Adding parcel settings to archive.");
|
|
||||||
|
|
||||||
// Write out land data (aka parcel) settings
|
|
||||||
List<ILandObject>landObjects = m_scene.LandChannel.AllParcels();
|
|
||||||
foreach (ILandObject lo in landObjects)
|
|
||||||
{
|
|
||||||
LandData landData = lo.LandData;
|
|
||||||
string landDataPath = String.Format("{0}{1}.xml", ArchiveConstants.LANDDATA_PATH,
|
|
||||||
landData.GlobalID.ToString());
|
|
||||||
m_archiveWriter.WriteFile(landDataPath, LandDataSerializer.Serialize(landData, m_options));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_log.InfoFormat("[ARCHIVER]: Adding terrain information to archive.");
|
|
||||||
|
|
||||||
// Write out terrain
|
|
||||||
string terrainPath
|
|
||||||
= String.Format("{0}{1}.r32", ArchiveConstants.TERRAINS_PATH, m_scene.RegionInfo.RegionName);
|
|
||||||
|
|
||||||
MemoryStream ms = new MemoryStream();
|
|
||||||
m_terrainModule.SaveToStream(terrainPath, ms);
|
|
||||||
m_archiveWriter.WriteFile(terrainPath, ms.ToArray());
|
|
||||||
ms.Close();
|
|
||||||
|
|
||||||
m_log.InfoFormat("[ARCHIVER]: Adding scene objects to archive.");
|
|
||||||
|
|
||||||
// Write out scene object metadata
|
|
||||||
foreach (SceneObjectGroup sceneObject in m_sceneObjects)
|
|
||||||
{
|
|
||||||
//m_log.DebugFormat("[ARCHIVER]: Saving {0} {1}, {2}", entity.Name, entity.UUID, entity.GetType());
|
|
||||||
|
|
||||||
string serializedObject = m_serialiser.SerializeGroupToXml2(sceneObject, m_options);
|
|
||||||
m_archiveWriter.WriteFile(ArchiveHelpers.CreateObjectPath(sceneObject), serializedObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,438 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.IO;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Xml;
|
|
||||||
using log4net;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Framework.Serialization;
|
|
||||||
using OpenSim.Region.CoreModules.World.Terrain;
|
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
|
||||||
using OpenSim.Region.Framework.Scenes;
|
|
||||||
using Ionic.Zlib;
|
|
||||||
using GZipStream = Ionic.Zlib.GZipStream;
|
|
||||||
using CompressionMode = Ionic.Zlib.CompressionMode;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.World.Archiver
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Prepare to write out an archive.
|
|
||||||
/// </summary>
|
|
||||||
public class ArchiveWriteRequestPreparation
|
|
||||||
{
|
|
||||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The minimum major version of OAR that we can write.
|
|
||||||
/// </summary>
|
|
||||||
public static int MIN_MAJOR_VERSION = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The maximum major version of OAR that we can write.
|
|
||||||
/// </summary>
|
|
||||||
public static int MAX_MAJOR_VERSION = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determine whether this archive will save assets. Default is true.
|
|
||||||
/// </summary>
|
|
||||||
public bool SaveAssets { get; set; }
|
|
||||||
|
|
||||||
protected ArchiverModule m_module;
|
|
||||||
protected Scene m_scene;
|
|
||||||
protected Stream m_saveStream;
|
|
||||||
protected Guid m_requestId;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="module">Calling module</param>
|
|
||||||
/// <param name="savePath">The path to which to save data.</param>
|
|
||||||
/// <param name="requestId">The id associated with this request</param>
|
|
||||||
/// <exception cref="System.IO.IOException">
|
|
||||||
/// If there was a problem opening a stream for the file specified by the savePath
|
|
||||||
/// </exception>
|
|
||||||
public ArchiveWriteRequestPreparation(ArchiverModule module, string savePath, Guid requestId) : this(module, requestId)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress, CompressionLevel.BestCompression);
|
|
||||||
}
|
|
||||||
catch (EntryPointNotFoundException e)
|
|
||||||
{
|
|
||||||
m_log.ErrorFormat(
|
|
||||||
"[ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
|
|
||||||
+ "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
|
|
||||||
m_log.ErrorFormat("{0} {1}", e.Message, e.StackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="module">Calling module</param>
|
|
||||||
/// <param name="saveStream">The stream to which to save data.</param>
|
|
||||||
/// <param name="requestId">The id associated with this request</param>
|
|
||||||
public ArchiveWriteRequestPreparation(ArchiverModule module, Stream saveStream, Guid requestId) : this(module, requestId)
|
|
||||||
{
|
|
||||||
m_saveStream = saveStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ArchiveWriteRequestPreparation(ArchiverModule module, Guid requestId)
|
|
||||||
{
|
|
||||||
m_module = module;
|
|
||||||
|
|
||||||
// FIXME: This is only here for regression test purposes since they do not supply a module. Need to fix
|
|
||||||
// this.
|
|
||||||
if (m_module != null)
|
|
||||||
m_scene = m_module.Scene;
|
|
||||||
|
|
||||||
m_requestId = requestId;
|
|
||||||
|
|
||||||
SaveAssets = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Archive the region requested.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception>
|
|
||||||
public void ArchiveRegion(Dictionary<string, object> options)
|
|
||||||
{
|
|
||||||
if (options.ContainsKey("noassets") && (bool)options["noassets"])
|
|
||||||
SaveAssets = false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Dictionary<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>();
|
|
||||||
|
|
||||||
EntityBase[] entities = m_scene.GetEntities();
|
|
||||||
List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>();
|
|
||||||
|
|
||||||
string checkPermissions = null;
|
|
||||||
int numObjectsSkippedPermissions = 0;
|
|
||||||
Object temp;
|
|
||||||
if (options.TryGetValue("checkPermissions", out temp))
|
|
||||||
checkPermissions = (string)temp;
|
|
||||||
|
|
||||||
// Filter entities so that we only have scene objects.
|
|
||||||
// FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods
|
|
||||||
// end up having to do this
|
|
||||||
foreach (EntityBase entity in entities)
|
|
||||||
{
|
|
||||||
if (entity is SceneObjectGroup)
|
|
||||||
{
|
|
||||||
SceneObjectGroup sceneObject = (SceneObjectGroup)entity;
|
|
||||||
|
|
||||||
if (!sceneObject.IsDeleted && !sceneObject.IsAttachment)
|
|
||||||
{
|
|
||||||
if (!CanUserArchiveObject(m_scene.RegionInfo.EstateSettings.EstateOwner, sceneObject, checkPermissions))
|
|
||||||
{
|
|
||||||
// The user isn't allowed to copy/transfer this object, so it will not be included in the OAR.
|
|
||||||
++numObjectsSkippedPermissions;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sceneObjects.Add(sceneObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SaveAssets)
|
|
||||||
{
|
|
||||||
UuidGatherer assetGatherer = new UuidGatherer(m_scene.AssetService);
|
|
||||||
|
|
||||||
foreach (SceneObjectGroup sceneObject in sceneObjects)
|
|
||||||
{
|
|
||||||
assetGatherer.GatherAssetUuids(sceneObject, assetUuids);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_log.DebugFormat(
|
|
||||||
"[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets",
|
|
||||||
sceneObjects.Count, assetUuids.Count);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numObjectsSkippedPermissions > 0)
|
|
||||||
{
|
|
||||||
m_log.DebugFormat(
|
|
||||||
"[ARCHIVER]: {0} scene objects skipped due to lack of permissions",
|
|
||||||
numObjectsSkippedPermissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we also request terrain texture assets
|
|
||||||
RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings;
|
|
||||||
|
|
||||||
if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1)
|
|
||||||
assetUuids[regionSettings.TerrainTexture1] = AssetType.Texture;
|
|
||||||
|
|
||||||
if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2)
|
|
||||||
assetUuids[regionSettings.TerrainTexture2] = AssetType.Texture;
|
|
||||||
|
|
||||||
if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3)
|
|
||||||
assetUuids[regionSettings.TerrainTexture3] = AssetType.Texture;
|
|
||||||
|
|
||||||
if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4)
|
|
||||||
assetUuids[regionSettings.TerrainTexture4] = AssetType.Texture;
|
|
||||||
|
|
||||||
TarArchiveWriter archiveWriter = new TarArchiveWriter(m_saveStream);
|
|
||||||
|
|
||||||
// Asynchronously request all the assets required to perform this archive operation
|
|
||||||
ArchiveWriteRequestExecution awre
|
|
||||||
= new ArchiveWriteRequestExecution(
|
|
||||||
sceneObjects,
|
|
||||||
m_scene.RequestModuleInterface<ITerrainModule>(),
|
|
||||||
m_scene.RequestModuleInterface<IRegionSerialiserModule>(),
|
|
||||||
m_scene,
|
|
||||||
archiveWriter,
|
|
||||||
m_requestId,
|
|
||||||
options);
|
|
||||||
|
|
||||||
m_log.InfoFormat("[ARCHIVER]: Creating archive file. This may take some time.");
|
|
||||||
|
|
||||||
// Write out control file. This has to be done first so that subsequent loaders will see this file first
|
|
||||||
// XXX: I know this is a weak way of doing it since external non-OAR aware tar executables will not do this
|
|
||||||
archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(options));
|
|
||||||
m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");
|
|
||||||
|
|
||||||
if (SaveAssets)
|
|
||||||
{
|
|
||||||
AssetsRequest ar
|
|
||||||
= new AssetsRequest(
|
|
||||||
new AssetsArchiver(archiveWriter), assetUuids,
|
|
||||||
m_scene.AssetService, m_scene.UserAccountService,
|
|
||||||
m_scene.RegionInfo.ScopeID, options, awre.ReceivedAllAssets);
|
|
||||||
|
|
||||||
Util.FireAndForget(o => ar.Execute());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
awre.ReceivedAllAssets(new List<UUID>(), new List<UUID>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
m_saveStream.Close();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks whether the user has permission to export an object group to an OAR.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user">The user</param>
|
|
||||||
/// <param name="objGroup">The object group</param>
|
|
||||||
/// <param name="checkPermissions">Which permissions to check: "C" = Copy, "T" = Transfer</param>
|
|
||||||
/// <returns>Whether the user is allowed to export the object to an OAR</returns>
|
|
||||||
private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions)
|
|
||||||
{
|
|
||||||
if (checkPermissions == null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
IPermissionsModule module = m_scene.RequestModuleInterface<IPermissionsModule>();
|
|
||||||
if (module == null)
|
|
||||||
return true; // this shouldn't happen
|
|
||||||
|
|
||||||
// Check whether the user is permitted to export all of the parts in the SOG. If any
|
|
||||||
// part can't be exported then the entire SOG can't be exported.
|
|
||||||
|
|
||||||
bool permitted = true;
|
|
||||||
//int primNumber = 1;
|
|
||||||
|
|
||||||
foreach (SceneObjectPart obj in objGroup.Parts)
|
|
||||||
{
|
|
||||||
uint perm;
|
|
||||||
PermissionClass permissionClass = module.GetPermissionClass(user, obj);
|
|
||||||
switch (permissionClass)
|
|
||||||
{
|
|
||||||
case PermissionClass.Owner:
|
|
||||||
perm = obj.BaseMask;
|
|
||||||
break;
|
|
||||||
case PermissionClass.Group:
|
|
||||||
perm = obj.GroupMask | obj.EveryoneMask;
|
|
||||||
break;
|
|
||||||
case PermissionClass.Everyone:
|
|
||||||
default:
|
|
||||||
perm = obj.EveryoneMask;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool canCopy = (perm & (uint)PermissionMask.Copy) != 0;
|
|
||||||
bool canTransfer = (perm & (uint)PermissionMask.Transfer) != 0;
|
|
||||||
|
|
||||||
// Special case: if Everyone can copy the object then this implies it can also be
|
|
||||||
// Transferred.
|
|
||||||
// However, if the user is the Owner then we don't check EveryoneMask, because it seems that the mask
|
|
||||||
// always (incorrectly) includes the Copy bit set in this case. But that's a mistake: the viewer
|
|
||||||
// does NOT show that the object has Everyone-Copy permissions, and doesn't allow it to be copied.
|
|
||||||
if (permissionClass != PermissionClass.Owner)
|
|
||||||
canTransfer |= (obj.EveryoneMask & (uint)PermissionMask.Copy) != 0;
|
|
||||||
|
|
||||||
bool partPermitted = true;
|
|
||||||
if (checkPermissions.Contains("C") && !canCopy)
|
|
||||||
partPermitted = false;
|
|
||||||
if (checkPermissions.Contains("T") && !canTransfer)
|
|
||||||
partPermitted = false;
|
|
||||||
|
|
||||||
// If the user is the Creator of the object then it can always be included in the OAR
|
|
||||||
bool creator = (obj.CreatorID.Guid == user.Guid);
|
|
||||||
if (creator)
|
|
||||||
partPermitted = true;
|
|
||||||
|
|
||||||
//string name = (objGroup.PrimCount == 1) ? objGroup.Name : string.Format("{0} ({1}/{2})", obj.Name, primNumber, objGroup.PrimCount);
|
|
||||||
//m_log.DebugFormat("[ARCHIVER]: Object permissions: {0}: Base={1:X4}, Owner={2:X4}, Everyone={3:X4}, permissionClass={4}, checkPermissions={5}, canCopy={6}, canTransfer={7}, creator={8}, permitted={9}",
|
|
||||||
// name, obj.BaseMask, obj.OwnerMask, obj.EveryoneMask,
|
|
||||||
// permissionClass, checkPermissions, canCopy, canTransfer, creator, partPermitted);
|
|
||||||
|
|
||||||
if (!partPermitted)
|
|
||||||
{
|
|
||||||
permitted = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//++primNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
return permitted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create the control file for the most up to date archive
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public string CreateControlFile(Dictionary<string, object> options)
|
|
||||||
{
|
|
||||||
int majorVersion = MAX_MAJOR_VERSION, minorVersion = 8;
|
|
||||||
//
|
|
||||||
// if (options.ContainsKey("version"))
|
|
||||||
// {
|
|
||||||
// string[] parts = options["version"].ToString().Split('.');
|
|
||||||
// if (parts.Length >= 1)
|
|
||||||
// {
|
|
||||||
// majorVersion = Int32.Parse(parts[0]);
|
|
||||||
//
|
|
||||||
// if (parts.Length >= 2)
|
|
||||||
// minorVersion = Int32.Parse(parts[1]);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (majorVersion < MIN_MAJOR_VERSION || majorVersion > MAX_MAJOR_VERSION)
|
|
||||||
// {
|
|
||||||
// throw new Exception(
|
|
||||||
// string.Format(
|
|
||||||
// "OAR version number for save must be between {0} and {1}",
|
|
||||||
// MIN_MAJOR_VERSION, MAX_MAJOR_VERSION));
|
|
||||||
// }
|
|
||||||
// else if (majorVersion == MAX_MAJOR_VERSION)
|
|
||||||
// {
|
|
||||||
// // Force 1.0
|
|
||||||
// minorVersion = 0;
|
|
||||||
// }
|
|
||||||
// else if (majorVersion == MIN_MAJOR_VERSION)
|
|
||||||
// {
|
|
||||||
// // Force 0.4
|
|
||||||
// minorVersion = 4;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion);
|
|
||||||
//if (majorVersion == 1)
|
|
||||||
//{
|
|
||||||
// m_log.WarnFormat("[ARCHIVER]: Please be aware that version 1.0 OARs are not compatible with OpenSim 0.7.0.2 and earlier. Please use the --version=0 option if you want to produce a compatible OAR");
|
|
||||||
//}
|
|
||||||
|
|
||||||
String s;
|
|
||||||
|
|
||||||
using (StringWriter sw = new StringWriter())
|
|
||||||
{
|
|
||||||
using (XmlTextWriter xtw = new XmlTextWriter(sw))
|
|
||||||
{
|
|
||||||
xtw.Formatting = Formatting.Indented;
|
|
||||||
xtw.WriteStartDocument();
|
|
||||||
xtw.WriteStartElement("archive");
|
|
||||||
xtw.WriteAttributeString("major_version", majorVersion.ToString());
|
|
||||||
xtw.WriteAttributeString("minor_version", minorVersion.ToString());
|
|
||||||
|
|
||||||
xtw.WriteStartElement("creation_info");
|
|
||||||
DateTime now = DateTime.UtcNow;
|
|
||||||
TimeSpan t = now - new DateTime(1970, 1, 1);
|
|
||||||
xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString());
|
|
||||||
xtw.WriteElementString("id", UUID.Random().ToString());
|
|
||||||
xtw.WriteEndElement();
|
|
||||||
|
|
||||||
xtw.WriteStartElement("region_info");
|
|
||||||
|
|
||||||
bool isMegaregion;
|
|
||||||
Vector2 size;
|
|
||||||
IRegionCombinerModule rcMod = null;
|
|
||||||
|
|
||||||
// FIXME: This is only here for regression test purposes since they do not supply a module. Need to fix
|
|
||||||
// this, possibly by doing control file creation somewhere else.
|
|
||||||
if (m_module != null)
|
|
||||||
rcMod = m_module.RegionCombinerModule;
|
|
||||||
|
|
||||||
if (rcMod != null)
|
|
||||||
isMegaregion = rcMod.IsRootForMegaregion(m_scene.RegionInfo.RegionID);
|
|
||||||
else
|
|
||||||
isMegaregion = false;
|
|
||||||
|
|
||||||
if (isMegaregion)
|
|
||||||
size = rcMod.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID);
|
|
||||||
else
|
|
||||||
size = new Vector2((float)Constants.RegionSize, (float)Constants.RegionSize);
|
|
||||||
|
|
||||||
xtw.WriteElementString("is_megaregion", isMegaregion.ToString());
|
|
||||||
xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y));
|
|
||||||
|
|
||||||
xtw.WriteEndElement();
|
|
||||||
|
|
||||||
xtw.WriteElementString("assets_included", SaveAssets.ToString());
|
|
||||||
|
|
||||||
xtw.WriteEndElement();
|
|
||||||
|
|
||||||
xtw.Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
s = sw.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (m_scene != null)
|
|
||||||
// Console.WriteLine(
|
|
||||||
// "[ARCHIVE WRITE REQUEST PREPARATION]: Control file for {0} is: {1}", m_scene.RegionInfo.RegionName, s);
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -146,6 +146,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
ops.Add("noassets", delegate(string v) { options["noassets"] = v != null; });
|
ops.Add("noassets", delegate(string v) { options["noassets"] = v != null; });
|
||||||
ops.Add("publish", v => options["wipe-owners"] = v != null);
|
ops.Add("publish", v => options["wipe-owners"] = v != null);
|
||||||
ops.Add("perm=", delegate(string v) { options["checkPermissions"] = v; });
|
ops.Add("perm=", delegate(string v) { options["checkPermissions"] = v; });
|
||||||
|
ops.Add("all", delegate(string v) { options["all"] = v != null; });
|
||||||
|
|
||||||
List<string> mainParams = ops.Parse(cmdparams);
|
List<string> mainParams = ops.Parse(cmdparams);
|
||||||
|
|
||||||
|
@ -169,7 +170,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
m_log.InfoFormat(
|
m_log.InfoFormat(
|
||||||
"[ARCHIVER]: Writing archive for region {0} to {1}", Scene.RegionInfo.RegionName, savePath);
|
"[ARCHIVER]: Writing archive for region {0} to {1}", Scene.RegionInfo.RegionName, savePath);
|
||||||
|
|
||||||
new ArchiveWriteRequestPreparation(this, savePath, requestId).ArchiveRegion(options);
|
new ArchiveWriteRequest(Scene, savePath, requestId).ArchiveRegion(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ArchiveRegion(Stream saveStream)
|
public void ArchiveRegion(Stream saveStream)
|
||||||
|
@ -184,7 +185,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
|
|
||||||
public void ArchiveRegion(Stream saveStream, Guid requestId, Dictionary<string, object> options)
|
public void ArchiveRegion(Stream saveStream, Guid requestId, Dictionary<string, object> options)
|
||||||
{
|
{
|
||||||
new ArchiveWriteRequestPreparation(this, saveStream, requestId).ArchiveRegion(options);
|
new ArchiveWriteRequest(Scene, saveStream, requestId).ArchiveRegion(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DearchiveRegion(string loadPath)
|
public void DearchiveRegion(string loadPath)
|
||||||
|
|
|
@ -46,6 +46,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
{
|
{
|
||||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Method called when all the necessary assets for an archive request have been received.
|
||||||
|
/// </summary>
|
||||||
|
public delegate void AssetsRequestCallback(
|
||||||
|
ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids);
|
||||||
|
|
||||||
enum RequestState
|
enum RequestState
|
||||||
{
|
{
|
||||||
Initial,
|
Initial,
|
||||||
|
|
|
@ -0,0 +1,232 @@
|
||||||
|
/*
|
||||||
|
* 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.Text;
|
||||||
|
using OpenSim.Region.Framework.Scenes;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using System.Drawing;
|
||||||
|
using log4net;
|
||||||
|
using System.Reflection;
|
||||||
|
using OpenSim.Framework.Serialization;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.CoreModules.World.Archiver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The regions included in an OAR file.
|
||||||
|
/// </summary>
|
||||||
|
public class DearchiveScenesInfo
|
||||||
|
{
|
||||||
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// One region in the archive.
|
||||||
|
/// </summary>
|
||||||
|
public class RegionInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The subdirectory in which the region is stored.
|
||||||
|
/// </summary>
|
||||||
|
public string Directory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The region's coordinates (relative to the South-West corner of the block).
|
||||||
|
/// </summary>
|
||||||
|
public Point Location { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The UUID of the original scene from which this archived region was saved.
|
||||||
|
/// </summary>
|
||||||
|
public string OriginalID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The scene in the current simulator into which this region is loaded.
|
||||||
|
/// If null then the region doesn't have a corresponding scene, and it won't be loaded.
|
||||||
|
/// </summary>
|
||||||
|
public Scene Scene { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this archive uses the multi-region format.
|
||||||
|
/// </summary>
|
||||||
|
public Boolean MultiRegionFormat { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps (Region directory -> region)
|
||||||
|
/// </summary>
|
||||||
|
protected Dictionary<string, RegionInfo> m_directory2region = new Dictionary<string, RegionInfo>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps (UUID of the scene in the simulator where the region will be loaded -> region)
|
||||||
|
/// </summary>
|
||||||
|
protected Dictionary<UUID, RegionInfo> m_newId2region = new Dictionary<UUID, RegionInfo>();
|
||||||
|
|
||||||
|
public int LoadedCreationDateTime { get; set; }
|
||||||
|
public string DefaultOriginalID { get; set; }
|
||||||
|
|
||||||
|
// These variables are used while reading the archive control file
|
||||||
|
protected int? m_curY = null;
|
||||||
|
protected int? m_curX = null;
|
||||||
|
protected RegionInfo m_curRegion;
|
||||||
|
|
||||||
|
|
||||||
|
public DearchiveScenesInfo()
|
||||||
|
{
|
||||||
|
MultiRegionFormat = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The following methods are used while reading the archive control file
|
||||||
|
|
||||||
|
public void StartRow()
|
||||||
|
{
|
||||||
|
m_curY = (m_curY == null) ? 0 : m_curY + 1;
|
||||||
|
m_curX = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartRegion()
|
||||||
|
{
|
||||||
|
m_curX = (m_curX == null) ? 0 : m_curX + 1;
|
||||||
|
// Note: this doesn't mean we have a real region in this location; this could just be a "hole"
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRegionOriginalID(string id)
|
||||||
|
{
|
||||||
|
m_curRegion = new RegionInfo();
|
||||||
|
m_curRegion.Location = new Point((int)m_curX, (int)m_curY);
|
||||||
|
m_curRegion.OriginalID = id;
|
||||||
|
// 'curRegion' will be saved in 'm_directory2region' when SetRegionDir() is called
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRegionDirectory(string directory)
|
||||||
|
{
|
||||||
|
m_curRegion.Directory = directory;
|
||||||
|
m_directory2region[directory] = m_curRegion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets all the scenes present in the simulator.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method matches regions in the archive to scenes in the simulator according to
|
||||||
|
/// their relative position. We only load regions if there's an existing Scene in the
|
||||||
|
/// grid location where the region should be loaded.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="rootScene">The scene where the Load OAR operation was run</param>
|
||||||
|
/// <param name="simulatorScenes">All the scenes in the simulator</param>
|
||||||
|
public void SetSimulatorScenes(Scene rootScene, ArchiveScenesGroup simulatorScenes)
|
||||||
|
{
|
||||||
|
foreach (RegionInfo archivedRegion in m_directory2region.Values)
|
||||||
|
{
|
||||||
|
Point location = new Point((int)rootScene.RegionInfo.RegionLocX, (int)rootScene.RegionInfo.RegionLocY);
|
||||||
|
location.Offset(archivedRegion.Location);
|
||||||
|
|
||||||
|
Scene scene;
|
||||||
|
if (simulatorScenes.TryGetScene(location, out scene))
|
||||||
|
{
|
||||||
|
archivedRegion.Scene = scene;
|
||||||
|
m_newId2region[scene.RegionInfo.RegionID] = archivedRegion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_log.WarnFormat("[ARCHIVER]: Not loading archived region {0} because there's no existing region at location {1},{2}",
|
||||||
|
archivedRegion.Directory, location.X, location.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the archived region according to the path of a file in the archive.
|
||||||
|
/// Also, converts the full path into a path that is relative to the region's directory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fullPath">The path of a file in the archive</param>
|
||||||
|
/// <param name="scene">The corresponding Scene, or null if none</param>
|
||||||
|
/// <param name="relativePath">The path relative to the region's directory. (Or the original
|
||||||
|
/// path, if this file doesn't belong to a region.)</param>
|
||||||
|
/// <returns>True: use this file; False: skip it</returns>
|
||||||
|
public bool GetRegionFromPath(string fullPath, out Scene scene, out string relativePath)
|
||||||
|
{
|
||||||
|
scene = null;
|
||||||
|
relativePath = fullPath;
|
||||||
|
|
||||||
|
if (!MultiRegionFormat)
|
||||||
|
{
|
||||||
|
if (m_newId2region.Count > 0)
|
||||||
|
scene = m_newId2region.First().Value.Scene;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fullPath.StartsWith(ArchiveConstants.REGIONS_PATH))
|
||||||
|
return true; // this file doesn't belong to a region
|
||||||
|
|
||||||
|
string[] parts = fullPath.Split(new Char[] { '/' }, 3);
|
||||||
|
if (parts.Length != 3)
|
||||||
|
return false;
|
||||||
|
string regionDirectory = parts[1];
|
||||||
|
relativePath = parts[2];
|
||||||
|
|
||||||
|
RegionInfo region;
|
||||||
|
if (m_directory2region.TryGetValue(regionDirectory, out region))
|
||||||
|
{
|
||||||
|
scene = region.Scene;
|
||||||
|
return (scene != null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the original UUID of a region (from the simulator where the OAR was saved),
|
||||||
|
/// given the UUID of the scene it was loaded into in the current simulator.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="newID"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string GetOriginalRegionID(UUID newID)
|
||||||
|
{
|
||||||
|
RegionInfo region;
|
||||||
|
if (m_newId2region.TryGetValue(newID, out region))
|
||||||
|
return region.OriginalID;
|
||||||
|
else
|
||||||
|
return DefaultOriginalID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the scenes that have been (or will be) loaded.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public List<UUID> GetLoadedScenes()
|
||||||
|
{
|
||||||
|
return m_newId2region.Keys.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,7 @@ using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants;
|
||||||
using TarArchiveReader = OpenSim.Framework.Serialization.TarArchiveReader;
|
using TarArchiveReader = OpenSim.Framework.Serialization.TarArchiveReader;
|
||||||
using TarArchiveWriter = OpenSim.Framework.Serialization.TarArchiveWriter;
|
using TarArchiveWriter = OpenSim.Framework.Serialization.TarArchiveWriter;
|
||||||
using RegionSettings = OpenSim.Framework.RegionSettings;
|
using RegionSettings = OpenSim.Framework.RegionSettings;
|
||||||
|
using OpenSim.Region.Framework.Interfaces;
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
{
|
{
|
||||||
|
@ -56,23 +57,28 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
private Guid m_lastRequestId;
|
private Guid m_lastRequestId;
|
||||||
private string m_lastErrorMessage;
|
private string m_lastErrorMessage;
|
||||||
|
|
||||||
|
protected SceneHelpers m_sceneHelpers;
|
||||||
protected TestScene m_scene;
|
protected TestScene m_scene;
|
||||||
protected ArchiverModule m_archiverModule;
|
protected ArchiverModule m_archiverModule;
|
||||||
|
protected SerialiserModule m_serialiserModule;
|
||||||
|
|
||||||
protected TaskInventoryItem m_soundItem;
|
protected TaskInventoryItem m_soundItem;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp()
|
public void SetUp()
|
||||||
{
|
{
|
||||||
|
new SceneManager();
|
||||||
|
|
||||||
m_archiverModule = new ArchiverModule();
|
m_archiverModule = new ArchiverModule();
|
||||||
SerialiserModule serialiserModule = new SerialiserModule();
|
m_serialiserModule = new SerialiserModule();
|
||||||
TerrainModule terrainModule = new TerrainModule();
|
TerrainModule terrainModule = new TerrainModule();
|
||||||
|
|
||||||
m_scene = new SceneHelpers().SetupScene();
|
m_sceneHelpers = new SceneHelpers();
|
||||||
SceneHelpers.SetupSceneModules(m_scene, m_archiverModule, serialiserModule, terrainModule);
|
m_scene = m_sceneHelpers.SetupScene();
|
||||||
|
SceneHelpers.SetupSceneModules(m_scene, m_archiverModule, m_serialiserModule, terrainModule);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadCompleted(Guid requestId, string errorMessage)
|
private void LoadCompleted(Guid requestId, List<UUID> loadedScenes, string errorMessage)
|
||||||
{
|
{
|
||||||
lock (this)
|
lock (this)
|
||||||
{
|
{
|
||||||
|
@ -128,26 +134,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
TestHelpers.InMethod();
|
TestHelpers.InMethod();
|
||||||
// log4net.Config.XmlConfigurator.Configure();
|
// log4net.Config.XmlConfigurator.Configure();
|
||||||
|
|
||||||
SceneObjectPart part1 = CreateSceneObjectPart1();
|
SceneObjectGroup sog1;
|
||||||
SceneObjectGroup sog1 = new SceneObjectGroup(part1);
|
SceneObjectGroup sog2;
|
||||||
m_scene.AddNewSceneObject(sog1, false);
|
UUID ncAssetUuid;
|
||||||
|
CreateTestObjects(m_scene, out sog1, out sog2, out ncAssetUuid);
|
||||||
SceneObjectPart part2 = CreateSceneObjectPart2();
|
|
||||||
|
|
||||||
AssetNotecard nc = new AssetNotecard();
|
|
||||||
nc.BodyText = "Hello World!";
|
|
||||||
nc.Encode();
|
|
||||||
UUID ncAssetUuid = new UUID("00000000-0000-0000-1000-000000000000");
|
|
||||||
UUID ncItemUuid = new UUID("00000000-0000-0000-1100-000000000000");
|
|
||||||
AssetBase ncAsset
|
|
||||||
= AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero);
|
|
||||||
m_scene.AssetService.Store(ncAsset);
|
|
||||||
SceneObjectGroup sog2 = new SceneObjectGroup(part2);
|
|
||||||
TaskInventoryItem ncItem
|
|
||||||
= new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid };
|
|
||||||
part2.Inventory.AddInventoryItem(ncItem, true);
|
|
||||||
|
|
||||||
m_scene.AddNewSceneObject(sog2, false);
|
|
||||||
|
|
||||||
MemoryStream archiveWriteStream = new MemoryStream();
|
MemoryStream archiveWriteStream = new MemoryStream();
|
||||||
m_scene.EventManager.OnOarFileSaved += SaveCompleted;
|
m_scene.EventManager.OnOarFileSaved += SaveCompleted;
|
||||||
|
@ -186,7 +176,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
|
Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
|
||||||
|
|
||||||
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
|
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
|
||||||
arr.LoadControlFile(filePath, data);
|
arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
|
||||||
|
|
||||||
Assert.That(arr.ControlFileLoaded, Is.True);
|
Assert.That(arr.ControlFileLoaded, Is.True);
|
||||||
|
|
||||||
|
@ -211,6 +201,30 @@ 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>
|
||||||
|
@ -270,7 +284,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
|
Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
|
||||||
|
|
||||||
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
|
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
|
||||||
arr.LoadControlFile(filePath, data);
|
arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
|
||||||
|
|
||||||
Assert.That(arr.ControlFileLoaded, Is.True);
|
Assert.That(arr.ControlFileLoaded, Is.True);
|
||||||
|
|
||||||
|
@ -307,7 +321,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
|
|
||||||
tar.WriteFile(
|
tar.WriteFile(
|
||||||
ArchiveConstants.CONTROL_FILE_PATH,
|
ArchiveConstants.CONTROL_FILE_PATH,
|
||||||
new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary<string, Object>()));
|
new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
|
||||||
|
|
||||||
SceneObjectGroup sog1 = SceneHelpers.CreateSceneObject(1, ownerId, "obj1-", 0x11);
|
SceneObjectGroup sog1 = SceneHelpers.CreateSceneObject(1, ownerId, "obj1-", 0x11);
|
||||||
SceneObjectPart sop2
|
SceneObjectPart sop2
|
||||||
|
@ -362,11 +376,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
// Also check that direct entries which will also have a file entry containing that directory doesn't
|
// Also check that direct entries which will also have a file entry containing that directory doesn't
|
||||||
// upset load
|
// upset load
|
||||||
tar.WriteDir(ArchiveConstants.TERRAINS_PATH);
|
tar.WriteDir(ArchiveConstants.TERRAINS_PATH);
|
||||||
|
|
||||||
tar.WriteFile(
|
tar.WriteFile(
|
||||||
ArchiveConstants.CONTROL_FILE_PATH,
|
ArchiveConstants.CONTROL_FILE_PATH,
|
||||||
new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary<string, Object>()));
|
new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
|
||||||
|
|
||||||
SceneObjectPart part1 = CreateSceneObjectPart1();
|
SceneObjectPart part1 = CreateSceneObjectPart1();
|
||||||
|
|
||||||
part1.SitTargetOrientation = new Quaternion(0.2f, 0.3f, 0.4f, 0.5f);
|
part1.SitTargetOrientation = new Quaternion(0.2f, 0.3f, 0.4f, 0.5f);
|
||||||
|
@ -389,31 +402,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
Assert.That(soundDataResourceName, Is.Not.Null);
|
Assert.That(soundDataResourceName, Is.Not.Null);
|
||||||
|
|
||||||
byte[] soundData;
|
byte[] soundData;
|
||||||
Console.WriteLine("Loading " + soundDataResourceName);
|
UUID soundUuid;
|
||||||
using (Stream resource = assembly.GetManifestResourceStream(soundDataResourceName))
|
CreateSoundAsset(tar, assembly, soundDataResourceName, out soundData, out soundUuid);
|
||||||
{
|
|
||||||
using (BinaryReader br = new BinaryReader(resource))
|
TaskInventoryItem item1
|
||||||
{
|
= new TaskInventoryItem { AssetID = soundUuid, ItemID = soundItemUuid, Name = soundItemName };
|
||||||
// FIXME: Use the inspector instead
|
part1.Inventory.AddInventoryItem(item1, true);
|
||||||
soundData = br.ReadBytes(99999999);
|
|
||||||
UUID 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";
|
|
||||||
*/
|
|
||||||
|
|
||||||
TaskInventoryItem item1
|
|
||||||
= new TaskInventoryItem { AssetID = soundUuid, ItemID = soundItemUuid, Name = soundItemName };
|
|
||||||
part1.Inventory.AddInventoryItem(item1, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_scene.AddNewSceneObject(object1, false);
|
m_scene.AddNewSceneObject(object1, false);
|
||||||
|
|
||||||
string object1FileName = string.Format(
|
string object1FileName = string.Format(
|
||||||
|
@ -435,6 +429,34 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
|
|
||||||
Assert.That(m_lastErrorMessage, Is.Null);
|
Assert.That(m_lastErrorMessage, Is.Null);
|
||||||
|
|
||||||
|
TestLoadedRegion(part1, soundItemName, soundData);
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TestLoadedRegion(SceneObjectPart part1, string soundItemName, byte[] soundData)
|
||||||
|
{
|
||||||
SceneObjectPart object1PartLoaded = m_scene.GetSceneObjectPart(part1.Name);
|
SceneObjectPart object1PartLoaded = m_scene.GetSceneObjectPart(part1.Name);
|
||||||
|
|
||||||
Assert.That(object1PartLoaded, Is.Not.Null, "object1 was not loaded");
|
Assert.That(object1PartLoaded, Is.Not.Null, "object1 was not loaded");
|
||||||
|
@ -454,9 +476,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
Assert.That(loadedSoundAsset.Data, Is.EqualTo(soundData), "saved and loaded sound data do not match");
|
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");
|
Assert.Greater(m_scene.LandChannel.AllParcels().Count, 0, "incorrect number of parcels");
|
||||||
|
|
||||||
// Temporary
|
|
||||||
Console.WriteLine("Successfully completed {0}", MethodBase.GetCurrentMethod());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -516,7 +535,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
SerialiserModule serialiserModule = new SerialiserModule();
|
SerialiserModule serialiserModule = new SerialiserModule();
|
||||||
TerrainModule terrainModule = new TerrainModule();
|
TerrainModule terrainModule = new TerrainModule();
|
||||||
|
|
||||||
TestScene scene2 = new SceneHelpers().SetupScene();
|
m_sceneHelpers = new SceneHelpers();
|
||||||
|
TestScene scene2 = m_sceneHelpers.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
|
||||||
|
@ -554,7 +574,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
tar.WriteDir(ArchiveConstants.TERRAINS_PATH);
|
tar.WriteDir(ArchiveConstants.TERRAINS_PATH);
|
||||||
tar.WriteFile(
|
tar.WriteFile(
|
||||||
ArchiveConstants.CONTROL_FILE_PATH,
|
ArchiveConstants.CONTROL_FILE_PATH,
|
||||||
new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary<string, Object>()));
|
new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
|
||||||
|
|
||||||
RegionSettings rs = new RegionSettings();
|
RegionSettings rs = new RegionSettings();
|
||||||
rs.AgentLimit = 17;
|
rs.AgentLimit = 17;
|
||||||
|
@ -664,7 +684,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
SerialiserModule serialiserModule = new SerialiserModule();
|
SerialiserModule serialiserModule = new SerialiserModule();
|
||||||
TerrainModule terrainModule = new TerrainModule();
|
TerrainModule terrainModule = new TerrainModule();
|
||||||
|
|
||||||
Scene scene = new SceneHelpers().SetupScene();
|
Scene scene = m_sceneHelpers.SetupScene();
|
||||||
SceneHelpers.SetupSceneModules(scene, archiverModule, serialiserModule, terrainModule);
|
SceneHelpers.SetupSceneModules(scene, archiverModule, serialiserModule, terrainModule);
|
||||||
|
|
||||||
m_scene.AddNewSceneObject(new SceneObjectGroup(part2), false);
|
m_scene.AddNewSceneObject(new SceneObjectGroup(part2), false);
|
||||||
|
@ -700,5 +720,258 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
|
||||||
Assert.That(object2PartMerged.GroupPosition, Is.EqualTo(part2.GroupPosition), "object2 group position not equal after merge");
|
Assert.That(object2PartMerged.GroupPosition, Is.EqualTo(part2.GroupPosition), "object2 group position not equal after merge");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test saving a multi-region OAR.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestSaveMultiRegionOar()
|
||||||
|
{
|
||||||
|
TestHelpers.InMethod();
|
||||||
|
|
||||||
|
// Create test regions
|
||||||
|
|
||||||
|
int WIDTH = 2;
|
||||||
|
int HEIGHT = 2;
|
||||||
|
|
||||||
|
List<Scene> scenes = new List<Scene>();
|
||||||
|
|
||||||
|
// Maps (Directory in OAR file -> scene)
|
||||||
|
Dictionary<string, Scene> regionPaths = new Dictionary<string, Scene>();
|
||||||
|
|
||||||
|
// Maps (Scene -> expected object paths)
|
||||||
|
Dictionary<UUID, List<string>> expectedPaths = new Dictionary<UUID, List<string>>();
|
||||||
|
|
||||||
|
// List of expected assets
|
||||||
|
List<UUID> expectedAssets = new List<UUID>();
|
||||||
|
|
||||||
|
for (uint y = 0; y < HEIGHT; y++)
|
||||||
|
{
|
||||||
|
for (uint x = 0; x < WIDTH; x++)
|
||||||
|
{
|
||||||
|
Scene scene;
|
||||||
|
if (x == 0 && y == 0)
|
||||||
|
{
|
||||||
|
scene = m_scene; // this scene was already created in SetUp()
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scene = m_sceneHelpers.SetupScene(string.Format("Unit test region {0}", (y * WIDTH) + x + 1), UUID.Random(), 1000 + x, 1000 + y);
|
||||||
|
SceneHelpers.SetupSceneModules(scene, new ArchiverModule(), m_serialiserModule, new TerrainModule());
|
||||||
|
}
|
||||||
|
scenes.Add(scene);
|
||||||
|
|
||||||
|
string dir = String.Format("{0}_{1}_{2}", x + 1, y + 1, scene.RegionInfo.RegionName.Replace(" ", "_"));
|
||||||
|
regionPaths[dir] = scene;
|
||||||
|
|
||||||
|
SceneObjectGroup sog1;
|
||||||
|
SceneObjectGroup sog2;
|
||||||
|
UUID ncAssetUuid;
|
||||||
|
|
||||||
|
CreateTestObjects(scene, out sog1, out sog2, out ncAssetUuid);
|
||||||
|
|
||||||
|
expectedPaths[scene.RegionInfo.RegionID] = new List<string>();
|
||||||
|
expectedPaths[scene.RegionInfo.RegionID].Add(ArchiveHelpers.CreateObjectPath(sog1));
|
||||||
|
expectedPaths[scene.RegionInfo.RegionID].Add(ArchiveHelpers.CreateObjectPath(sog2));
|
||||||
|
|
||||||
|
expectedAssets.Add(ncAssetUuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Save OAR
|
||||||
|
|
||||||
|
MemoryStream archiveWriteStream = new MemoryStream();
|
||||||
|
m_scene.EventManager.OnOarFileSaved += SaveCompleted;
|
||||||
|
|
||||||
|
Guid requestId = new Guid("00000000-0000-0000-0000-808080808080");
|
||||||
|
|
||||||
|
Dictionary<string, Object> options = new Dictionary<string, Object>();
|
||||||
|
options.Add("all", true);
|
||||||
|
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
m_archiverModule.ArchiveRegion(archiveWriteStream, requestId, options);
|
||||||
|
Monitor.Wait(this, 60000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check that the OAR contains the expected data
|
||||||
|
|
||||||
|
Assert.That(m_lastRequestId, Is.EqualTo(requestId));
|
||||||
|
|
||||||
|
byte[] archive = archiveWriteStream.ToArray();
|
||||||
|
MemoryStream archiveReadStream = new MemoryStream(archive);
|
||||||
|
TarArchiveReader tar = new TarArchiveReader(archiveReadStream);
|
||||||
|
|
||||||
|
Dictionary<UUID, List<string>> foundPaths = new Dictionary<UUID, List<string>>();
|
||||||
|
List<UUID> foundAssets = new List<UUID>();
|
||||||
|
|
||||||
|
foreach (Scene scene in scenes)
|
||||||
|
{
|
||||||
|
foundPaths[scene.RegionInfo.RegionID] = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
string filePath;
|
||||||
|
TarArchiveReader.TarEntryType tarEntryType;
|
||||||
|
|
||||||
|
byte[] data = tar.ReadEntry(out filePath, out tarEntryType);
|
||||||
|
Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
|
||||||
|
|
||||||
|
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
|
||||||
|
arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
|
||||||
|
|
||||||
|
Assert.That(arr.ControlFileLoaded, Is.True);
|
||||||
|
|
||||||
|
while (tar.ReadEntry(out filePath, out tarEntryType) != null)
|
||||||
|
{
|
||||||
|
if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH))
|
||||||
|
{
|
||||||
|
// Assets are shared, so this file doesn't belong to any specific region.
|
||||||
|
string fileName = filePath.Remove(0, ArchiveConstants.ASSETS_PATH.Length);
|
||||||
|
if (fileName.EndsWith("_notecard.txt"))
|
||||||
|
foundAssets.Add(UUID.Parse(fileName.Substring(0, fileName.Length - "_notecard.txt".Length)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This file belongs to one of the regions. Find out which one.
|
||||||
|
Assert.IsTrue(filePath.StartsWith(ArchiveConstants.REGIONS_PATH));
|
||||||
|
string[] parts = filePath.Split(new Char[] { '/' }, 3);
|
||||||
|
Assert.AreEqual(3, parts.Length);
|
||||||
|
string regionDirectory = parts[1];
|
||||||
|
string relativePath = parts[2];
|
||||||
|
Scene scene = regionPaths[regionDirectory];
|
||||||
|
|
||||||
|
if (relativePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
|
||||||
|
{
|
||||||
|
foundPaths[scene.RegionInfo.RegionID].Add(relativePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(scenes.Count, foundPaths.Count);
|
||||||
|
foreach (Scene scene in scenes)
|
||||||
|
{
|
||||||
|
Assert.That(foundPaths[scene.RegionInfo.RegionID], Is.EquivalentTo(expectedPaths[scene.RegionInfo.RegionID]));
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(foundAssets, Is.EquivalentTo(expectedAssets));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test loading a multi-region OAR.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestLoadMultiRegionOar()
|
||||||
|
{
|
||||||
|
TestHelpers.InMethod();
|
||||||
|
|
||||||
|
// Create an ArchiveScenesGroup with the regions in the OAR. This is needed to generate the control file.
|
||||||
|
|
||||||
|
int WIDTH = 2;
|
||||||
|
int HEIGHT = 2;
|
||||||
|
|
||||||
|
for (uint y = 0; y < HEIGHT; y++)
|
||||||
|
{
|
||||||
|
for (uint x = 0; x < WIDTH; x++)
|
||||||
|
{
|
||||||
|
Scene scene;
|
||||||
|
if (x == 0 && y == 0)
|
||||||
|
{
|
||||||
|
scene = m_scene; // this scene was already created in SetUp()
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scene = m_sceneHelpers.SetupScene(string.Format("Unit test region {0}", (y * WIDTH) + x + 1), UUID.Random(), 1000 + x, 1000 + y);
|
||||||
|
SceneHelpers.SetupSceneModules(scene, new ArchiverModule(), m_serialiserModule, new TerrainModule());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();
|
||||||
|
SceneManager.Instance.ForEachScene(delegate(Scene scene)
|
||||||
|
{
|
||||||
|
scenesGroup.AddScene(scene);
|
||||||
|
});
|
||||||
|
scenesGroup.CalcSceneLocations();
|
||||||
|
|
||||||
|
// Generate the OAR file
|
||||||
|
|
||||||
|
MemoryStream archiveWriteStream = new MemoryStream();
|
||||||
|
TarArchiveWriter tar = new TarArchiveWriter(archiveWriteStream);
|
||||||
|
|
||||||
|
ArchiveWriteRequest writeRequest = new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty);
|
||||||
|
writeRequest.MultiRegionFormat = true;
|
||||||
|
tar.WriteFile(
|
||||||
|
ArchiveConstants.CONTROL_FILE_PATH, writeRequest.CreateControlFile(scenesGroup));
|
||||||
|
|
||||||
|
SceneObjectPart part1 = CreateSceneObjectPart1();
|
||||||
|
part1.SitTargetOrientation = new Quaternion(0.2f, 0.3f, 0.4f, 0.5f);
|
||||||
|
part1.SitTargetPosition = new Vector3(1, 2, 3);
|
||||||
|
|
||||||
|
SceneObjectGroup object1 = new SceneObjectGroup(part1);
|
||||||
|
|
||||||
|
// Let's put some inventory items into our object
|
||||||
|
string soundItemName = "sound-item1";
|
||||||
|
UUID soundItemUuid = UUID.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
|
Type type = GetType();
|
||||||
|
Assembly assembly = type.Assembly;
|
||||||
|
string soundDataResourceName = null;
|
||||||
|
string[] names = assembly.GetManifestResourceNames();
|
||||||
|
foreach (string name in names)
|
||||||
|
{
|
||||||
|
if (name.EndsWith(".Resources.test-sound.wav"))
|
||||||
|
soundDataResourceName = name;
|
||||||
|
}
|
||||||
|
Assert.That(soundDataResourceName, Is.Not.Null);
|
||||||
|
|
||||||
|
byte[] soundData;
|
||||||
|
UUID soundUuid;
|
||||||
|
CreateSoundAsset(tar, assembly, soundDataResourceName, out soundData, out soundUuid);
|
||||||
|
|
||||||
|
TaskInventoryItem item1
|
||||||
|
= new TaskInventoryItem { AssetID = soundUuid, ItemID = soundItemUuid, Name = soundItemName };
|
||||||
|
part1.Inventory.AddInventoryItem(item1, true);
|
||||||
|
m_scene.AddNewSceneObject(object1, false);
|
||||||
|
|
||||||
|
string object1FileName = string.Format(
|
||||||
|
"{0}_{1:000}-{2:000}-{3:000}__{4}.xml",
|
||||||
|
part1.Name,
|
||||||
|
Math.Round(part1.GroupPosition.X), Math.Round(part1.GroupPosition.Y), Math.Round(part1.GroupPosition.Z),
|
||||||
|
part1.UUID);
|
||||||
|
string path = "regions/1_1_Unit_test_region/" + ArchiveConstants.OBJECTS_PATH + object1FileName;
|
||||||
|
tar.WriteFile(path, SceneObjectSerializer.ToXml2Format(object1));
|
||||||
|
|
||||||
|
tar.Close();
|
||||||
|
|
||||||
|
|
||||||
|
// Delete the current objects, to test that they're loaded from the OAR and didn't
|
||||||
|
// just remain in the scene.
|
||||||
|
SceneManager.Instance.ForEachScene(delegate(Scene scene)
|
||||||
|
{
|
||||||
|
scene.DeleteAllSceneObjects();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a "hole", to test that that the corresponding region isn't loaded from the OAR
|
||||||
|
SceneManager.Instance.CloseScene(SceneManager.Instance.Scenes[1]);
|
||||||
|
|
||||||
|
|
||||||
|
// Check thay the OAR file contains the expected data
|
||||||
|
|
||||||
|
MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray());
|
||||||
|
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
m_scene.EventManager.OnOarFileLoaded += LoadCompleted;
|
||||||
|
m_archiverModule.DearchiveRegion(archiveReadStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(m_lastErrorMessage, Is.Null);
|
||||||
|
|
||||||
|
Assert.AreEqual(3, SceneManager.Instance.Scenes.Count);
|
||||||
|
|
||||||
|
TestLoadedRegion(part1, soundItemName, soundData);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,11 @@ namespace OpenSim.Region.Framework.Interfaces
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void sendRegionHandshakeToAll();
|
void sendRegionHandshakeToAll();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fires the OnRegionInfoChange event.
|
||||||
|
/// </summary>
|
||||||
|
void TriggerRegionInfoChange();
|
||||||
|
|
||||||
void setEstateTerrainBaseTexture(int level, UUID texture);
|
void setEstateTerrainBaseTexture(int level, UUID texture);
|
||||||
void setEstateTerrainTextureHeights(int corner, float lowValue, float highValue);
|
void setEstateTerrainTextureHeights(int corner, float lowValue, float highValue);
|
||||||
}
|
}
|
||||||
|
|
|
@ -531,7 +531,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
/// the scripts may not have started yet
|
/// the scripts may not have started yet
|
||||||
/// Message is non empty string if there were problems loading the oar file
|
/// Message is non empty string if there were problems loading the oar file
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public delegate void OarFileLoaded(Guid guid, string message);
|
public delegate void OarFileLoaded(Guid guid, List<UUID> loadedScenes, string message);
|
||||||
public event OarFileLoaded OnOarFileLoaded;
|
public event OarFileLoaded OnOarFileLoaded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -2195,7 +2195,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TriggerOarFileLoaded(Guid requestId, string message)
|
public void TriggerOarFileLoaded(Guid requestId, List<UUID> loadedScenes, string message)
|
||||||
{
|
{
|
||||||
OarFileLoaded handlerOarFileLoaded = OnOarFileLoaded;
|
OarFileLoaded handlerOarFileLoaded = OnOarFileLoaded;
|
||||||
if (handlerOarFileLoaded != null)
|
if (handlerOarFileLoaded != null)
|
||||||
|
@ -2204,7 +2204,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
d(requestId, message);
|
d(requestId, loadedScenes, message);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -92,7 +92,11 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
private static SceneManager m_instance = null;
|
private static SceneManager m_instance = null;
|
||||||
public static SceneManager Instance
|
public static SceneManager Instance
|
||||||
{
|
{
|
||||||
get { return m_instance; }
|
get {
|
||||||
|
if (m_instance == null)
|
||||||
|
m_instance = new SceneManager();
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<Scene> m_localScenes = new List<Scene>();
|
private readonly List<Scene> m_localScenes = new List<Scene>();
|
||||||
|
|
|
@ -181,7 +181,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnOarFileLoaded(Guid requestId, string message)
|
void OnOarFileLoaded(Guid requestId, List<UUID> loadedScenes, string message)
|
||||||
{
|
{
|
||||||
m_oarFileLoading = true;
|
m_oarFileLoading = true;
|
||||||
|
|
||||||
|
|
|
@ -131,8 +131,6 @@ public class BSCharacter : BSPhysObject
|
||||||
BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy);
|
BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy);
|
||||||
|
|
||||||
BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(Scene.World.Ptr, LocalID));
|
BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(Scene.World.Ptr, LocalID));
|
||||||
// avatars get all collisions no matter what (makes walking on ground and such work)
|
|
||||||
BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -480,11 +478,10 @@ public class BSCharacter : BSPhysObject
|
||||||
// Stop collision events
|
// Stop collision events
|
||||||
public override void UnSubscribeEvents() {
|
public override void UnSubscribeEvents() {
|
||||||
_subscribedEventsMs = 0;
|
_subscribedEventsMs = 0;
|
||||||
// Avatars get all their collision events
|
Scene.TaintedObject("BSCharacter.UnSubscribeEvents", delegate()
|
||||||
// Scene.TaintedObject("BSCharacter.UnSubscribeEvents", delegate()
|
{
|
||||||
// {
|
BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||||
// BulletSimAPI.RemoveFromCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
});
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
// Return 'true' if someone has subscribed to events
|
// Return 'true' if someone has subscribed to events
|
||||||
public override bool SubscribedEvents() {
|
public override bool SubscribedEvents() {
|
||||||
|
@ -532,20 +529,20 @@ public class BSCharacter : BSPhysObject
|
||||||
// The collision, if it should be reported to the character, is placed in a collection
|
// The collision, if it should be reported to the character, is placed in a collection
|
||||||
// that will later be sent to the simulator when SendCollisions() is called.
|
// that will later be sent to the simulator when SendCollisions() is called.
|
||||||
CollisionEventUpdate collisionCollection = null;
|
CollisionEventUpdate collisionCollection = null;
|
||||||
public override void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth)
|
public override bool Collide(uint collidingWith, BSPhysObject collidee, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth)
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith);
|
bool ret = false;
|
||||||
|
|
||||||
// The following makes IsColliding() and IsCollidingGround() work
|
// The following makes IsColliding() and IsCollidingGround() work
|
||||||
_collidingStep = Scene.SimulationStep;
|
_collidingStep = Scene.SimulationStep;
|
||||||
if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID)
|
if (collidingWith <= Scene.TerrainManager.HighestTerrainID)
|
||||||
{
|
{
|
||||||
_collidingGroundStep = Scene.SimulationStep;
|
_collidingGroundStep = Scene.SimulationStep;
|
||||||
}
|
}
|
||||||
// DetailLog("{0},BSCharacter.Collison,call,with={1}", LocalID, collidingWith);
|
// DetailLog("{0},BSCharacter.Collison,call,with={1}", LocalID, collidingWith);
|
||||||
|
|
||||||
// throttle collisions to the rate specified in the subscription
|
// throttle collisions to the rate specified in the subscription
|
||||||
if (_subscribedEventsMs != 0) {
|
if (SubscribedEvents()) {
|
||||||
int nowTime = Scene.SimulationNowTime;
|
int nowTime = Scene.SimulationNowTime;
|
||||||
if (nowTime >= _nextCollisionOkTime) {
|
if (nowTime >= _nextCollisionOkTime) {
|
||||||
_nextCollisionOkTime = nowTime + _subscribedEventsMs;
|
_nextCollisionOkTime = nowTime + _subscribedEventsMs;
|
||||||
|
@ -553,8 +550,10 @@ public class BSCharacter : BSPhysObject
|
||||||
if (collisionCollection == null)
|
if (collisionCollection == null)
|
||||||
collisionCollection = new CollisionEventUpdate();
|
collisionCollection = new CollisionEventUpdate();
|
||||||
collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
|
collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
|
||||||
|
ret = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SendCollisions()
|
public override void SendCollisions()
|
||||||
|
|
|
@ -74,6 +74,17 @@ public abstract class BSConstraint : IDisposable
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual bool SetSolverIterations(float cnt)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (m_enabled)
|
||||||
|
{
|
||||||
|
BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.Ptr, cnt);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual bool CalculateTransforms()
|
public virtual bool CalculateTransforms()
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
@ -96,12 +107,9 @@ public abstract class BSConstraint : IDisposable
|
||||||
ret = CalculateTransforms();
|
ret = CalculateTransforms();
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
// m_world.scene.PhysicsLogging.Write("{0},BSConstraint.RecomputeConstraintVariables,taint,enabling,A={1},B={2}",
|
|
||||||
// BSScene.DetailLogZero, Body1.ID, Body2.ID);
|
|
||||||
|
|
||||||
// Setting an object's mass to zero (making it static like when it's selected)
|
// Setting an object's mass to zero (making it static like when it's selected)
|
||||||
// automatically disables the constraints.
|
// automatically disables the constraints.
|
||||||
// If enabled, be sure to set the constraint itself to enabled.
|
// If the link is enabled, be sure to set the constraint itself to enabled.
|
||||||
BulletSimAPI.SetConstraintEnable2(m_constraint.Ptr, m_world.scene.NumericBool(true));
|
BulletSimAPI.SetConstraintEnable2(m_constraint.Ptr, m_world.scene.NumericBool(true));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -80,7 +80,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
// Linear properties
|
// Linear properties
|
||||||
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_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL
|
private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL
|
||||||
private Vector3 m_dir = Vector3.Zero; // velocity applied to body
|
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;
|
||||||
|
@ -131,7 +131,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
m_type = Vehicle.TYPE_NONE;
|
m_type = Vehicle.TYPE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue, float timestep)
|
internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue)
|
||||||
{
|
{
|
||||||
VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
|
VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
|
||||||
switch (pParam)
|
switch (pParam)
|
||||||
|
@ -230,7 +230,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
}
|
}
|
||||||
}//end ProcessFloatVehicleParam
|
}//end ProcessFloatVehicleParam
|
||||||
|
|
||||||
internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue, float timestep)
|
internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue)
|
||||||
{
|
{
|
||||||
VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
|
VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
|
||||||
switch (pParam)
|
switch (pParam)
|
||||||
|
@ -299,7 +299,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
}
|
}
|
||||||
}//end ProcessVehicleFlags
|
}//end ProcessVehicleFlags
|
||||||
|
|
||||||
internal void ProcessTypeChange(Vehicle pType, float stepSize)
|
internal void ProcessTypeChange(Vehicle pType)
|
||||||
{
|
{
|
||||||
VDetailLog("{0},ProcessTypeChange,type={1}", m_prim.LocalID, pType);
|
VDetailLog("{0},ProcessTypeChange,type={1}", m_prim.LocalID, pType);
|
||||||
// Set Defaults For Type
|
// Set Defaults For Type
|
||||||
|
@ -478,29 +478,30 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
MoveAngular(pTimestep);
|
MoveAngular(pTimestep);
|
||||||
LimitRotation(pTimestep);
|
LimitRotation(pTimestep);
|
||||||
|
|
||||||
|
// remember the position so next step we can limit absolute movement effects
|
||||||
|
m_lastPositionVector = m_prim.Position;
|
||||||
|
|
||||||
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}",
|
||||||
m_prim.LocalID, m_prim.Position, m_prim.Force, m_prim.Velocity, m_prim.RotationalVelocity);
|
m_prim.LocalID, m_prim.Position, m_prim.Force, m_prim.Velocity, m_prim.RotationalVelocity);
|
||||||
}// end Step
|
}// end Step
|
||||||
|
|
||||||
private void MoveLinear(float pTimestep)
|
private void MoveLinear(float pTimestep)
|
||||||
{
|
{
|
||||||
// requested m_linearMotorDirection is significant
|
// m_linearMotorDirection is the direction we are moving relative to the vehicle coordinates
|
||||||
// if (!m_linearMotorDirection.ApproxEquals(Vector3.Zero, 0.01f))
|
// m_lastLinearVelocityVector is the speed we are moving in that direction
|
||||||
if (m_linearMotorDirection.LengthSquared() > 0.0001f)
|
if (m_linearMotorDirection.LengthSquared() > 0.001f)
|
||||||
{
|
{
|
||||||
Vector3 origDir = m_linearMotorDirection;
|
Vector3 origDir = m_linearMotorDirection;
|
||||||
Vector3 origVel = m_lastLinearVelocityVector;
|
Vector3 origVel = m_lastLinearVelocityVector;
|
||||||
|
|
||||||
// add drive to body
|
// add drive to body
|
||||||
// Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale/pTimestep);
|
// Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale / pTimestep);
|
||||||
Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale);
|
Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/(m_linearMotorTimescale / pTimestep);
|
||||||
// lastLinearVelocityVector is the current body velocity vector?
|
// lastLinearVelocityVector is the current body velocity vector
|
||||||
// RA: Not sure what the *10 is for. A correction for pTimestep?
|
// RA: Not sure what the *10 is for. A correction for pTimestep?
|
||||||
// m_lastLinearVelocityVector += (addAmount*10);
|
// m_lastLinearVelocityVector += (addAmount*10);
|
||||||
m_lastLinearVelocityVector += addAmount;
|
m_lastLinearVelocityVector += addAmount;
|
||||||
|
|
||||||
// This will work temporarily, but we really need to compare speed on an axis
|
|
||||||
// KF: Limit body velocity to applied velocity?
|
|
||||||
// Limit the velocity vector to less than the last set linear motor direction
|
// Limit the velocity vector to less than the last set linear motor direction
|
||||||
if (Math.Abs(m_lastLinearVelocityVector.X) > Math.Abs(m_linearMotorDirectionLASTSET.X))
|
if (Math.Abs(m_lastLinearVelocityVector.X) > Math.Abs(m_linearMotorDirectionLASTSET.X))
|
||||||
m_lastLinearVelocityVector.X = m_linearMotorDirectionLASTSET.X;
|
m_lastLinearVelocityVector.X = m_linearMotorDirectionLASTSET.X;
|
||||||
|
@ -509,53 +510,108 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
if (Math.Abs(m_lastLinearVelocityVector.Z) > Math.Abs(m_linearMotorDirectionLASTSET.Z))
|
if (Math.Abs(m_lastLinearVelocityVector.Z) > Math.Abs(m_linearMotorDirectionLASTSET.Z))
|
||||||
m_lastLinearVelocityVector.Z = m_linearMotorDirectionLASTSET.Z;
|
m_lastLinearVelocityVector.Z = m_linearMotorDirectionLASTSET.Z;
|
||||||
|
|
||||||
// decay applied velocity
|
|
||||||
Vector3 decayfraction = ((Vector3.One/(m_linearMotorDecayTimescale/pTimestep)));
|
|
||||||
m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/m_linearMotorTimescale;
|
// decay applied velocity
|
||||||
m_lastLinearVelocityVector += addAmount;
|
Vector3 decayfraction = Vector3.One/(m_linearMotorDecayTimescale / pTimestep);
|
||||||
|
// (RA: do not know where the 0.5f comes from)
|
||||||
float decayfraction = (1.0f - 1.0f / m_linearMotorDecayTimescale);
|
m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f;
|
||||||
m_linearMotorDirection *= decayfraction;
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
float keepfraction = 1.0f - (1.0f / (m_linearMotorDecayTimescale / pTimestep));
|
||||||
|
m_linearMotorDirection *= keepfraction;
|
||||||
|
|
||||||
VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},decay={4},dir={5},vel={6}",
|
VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},notDecay={4},dir={5},vel={6}",
|
||||||
m_prim.LocalID, origDir, origVel, addAmount, decayfraction, m_linearMotorDirection, m_lastLinearVelocityVector);
|
m_prim.LocalID, origDir, origVel, addAmount, keepfraction, m_linearMotorDirection, m_lastLinearVelocityVector);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// if what remains of applied is small, zero it.
|
// if what remains of direction is very small, zero it.
|
||||||
// if (m_lastLinearVelocityVector.ApproxEquals(Vector3.Zero, 0.01f))
|
|
||||||
// m_lastLinearVelocityVector = Vector3.Zero;
|
|
||||||
m_linearMotorDirection = Vector3.Zero;
|
m_linearMotorDirection = Vector3.Zero;
|
||||||
m_lastLinearVelocityVector = Vector3.Zero;
|
m_lastLinearVelocityVector = Vector3.Zero;
|
||||||
|
VDetailLog("{0},MoveLinear,zeroed", m_prim.LocalID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert requested object velocity to object relative vector
|
// convert requested object velocity to object relative vector
|
||||||
Quaternion rotq = m_prim.Orientation;
|
Quaternion rotq = m_prim.Orientation;
|
||||||
m_dir = m_lastLinearVelocityVector * rotq;
|
m_newVelocity = m_lastLinearVelocityVector * rotq;
|
||||||
|
|
||||||
// Add the various forces into m_dir which will be our new direction vector (velocity)
|
// Add the various forces into m_dir which will be our new direction vector (velocity)
|
||||||
|
|
||||||
// add Gravity and Buoyancy
|
// add Gravity and Buoyancy
|
||||||
// KF: So far I have found no good method to combine a script-requested
|
|
||||||
// .Z velocity and gravity. Therefore only 0g will used script-requested
|
|
||||||
// .Z velocity. >0g (m_VehicleBuoyancy < 1) will used modified gravity only.
|
|
||||||
Vector3 grav = Vector3.Zero;
|
|
||||||
// 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;
|
||||||
grav.Z = m_prim.Scene.DefaultGravity.Z * m_prim.Mass * (1f - m_VehicleBuoyancy);
|
Vector3 grav = m_prim.Scene.DefaultGravity * (m_prim.Mass * (1f - m_VehicleBuoyancy));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RA: Not sure why one would do this
|
||||||
// Preserve the current Z velocity
|
// Preserve the current Z velocity
|
||||||
Vector3 vel_now = m_prim.Velocity;
|
Vector3 vel_now = m_prim.Velocity;
|
||||||
m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity
|
m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity
|
||||||
|
*/
|
||||||
|
|
||||||
Vector3 pos = m_prim.Position;
|
Vector3 pos = m_prim.Position;
|
||||||
Vector3 posChange = pos;
|
|
||||||
// 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);
|
// 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);
|
||||||
double Zchange = Math.Abs(posChange.Z);
|
|
||||||
|
// If below the terrain, move us above the ground a little.
|
||||||
|
float terrainHeight = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos);
|
||||||
|
// Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset.
|
||||||
|
// Need to add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass.
|
||||||
|
// Vector3 rotatedSize = m_prim.Size * m_prim.Orientation;
|
||||||
|
// if (rotatedSize.Z < terrainHeight)
|
||||||
|
if (pos.Z < terrainHeight)
|
||||||
|
{
|
||||||
|
pos.Z = terrainHeight + 2;
|
||||||
|
m_prim.Position = pos;
|
||||||
|
VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", m_prim.LocalID, terrainHeight, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if hovering
|
||||||
|
if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
|
||||||
|
{
|
||||||
|
// We should hover, get the target height
|
||||||
|
if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
|
||||||
|
{
|
||||||
|
m_VhoverTargetHeight = m_prim.Scene.GetWaterLevelAtXYZ(pos) + m_VhoverHeight;
|
||||||
|
}
|
||||||
|
if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
|
||||||
|
{
|
||||||
|
m_VhoverTargetHeight = terrainHeight + m_VhoverHeight;
|
||||||
|
}
|
||||||
|
if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
|
||||||
|
{
|
||||||
|
m_VhoverTargetHeight = m_VhoverHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
|
||||||
|
{
|
||||||
|
// If body is aready heigher, use its height as target height
|
||||||
|
if (pos.Z > m_VhoverTargetHeight) m_VhoverTargetHeight = pos.Z;
|
||||||
|
}
|
||||||
|
if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
|
||||||
|
{
|
||||||
|
if ((pos.Z - m_VhoverTargetHeight) > .2 || (pos.Z - m_VhoverTargetHeight) < -.2)
|
||||||
|
{
|
||||||
|
m_prim.Position = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float herr0 = pos.Z - m_VhoverTargetHeight;
|
||||||
|
// Replace Vertical speed with correction figure if significant
|
||||||
|
if (Math.Abs(herr0) > 0.01f)
|
||||||
|
{
|
||||||
|
m_newVelocity.Z = -((herr0 * pTimestep * 50.0f) / m_VhoverTimescale);
|
||||||
|
//KF: m_VhoverEfficiency is not yet implemented
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_newVelocity.Z = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", m_prim.LocalID, pos, m_newVelocity, m_VhoverHeight, m_VhoverTargetHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 posChange = pos - m_lastPositionVector;
|
||||||
if (m_BlockingEndPoint != Vector3.Zero)
|
if (m_BlockingEndPoint != Vector3.Zero)
|
||||||
{
|
{
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
@ -592,125 +648,43 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If below the terrain, move us above the ground a little.
|
float Zchange = Math.Abs(posChange.Z);
|
||||||
if (pos.Z < m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos))
|
|
||||||
{
|
|
||||||
pos.Z = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 2;
|
|
||||||
m_prim.Position = pos;
|
|
||||||
VDetailLog("{0},MoveLinear,terrainHeight,pos={1}", m_prim.LocalID, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if hovering
|
|
||||||
if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
|
|
||||||
{
|
|
||||||
// We should hover, get the target height
|
|
||||||
if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
|
|
||||||
{
|
|
||||||
m_VhoverTargetHeight = m_prim.Scene.GetWaterLevel() + m_VhoverHeight;
|
|
||||||
}
|
|
||||||
if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
|
|
||||||
{
|
|
||||||
m_VhoverTargetHeight = m_prim.Scene.TerrainManager.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight;
|
|
||||||
}
|
|
||||||
if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
|
|
||||||
{
|
|
||||||
m_VhoverTargetHeight = m_VhoverHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
|
|
||||||
{
|
|
||||||
// If body is aready heigher, use its height as target height
|
|
||||||
if (pos.Z > m_VhoverTargetHeight) m_VhoverTargetHeight = pos.Z;
|
|
||||||
}
|
|
||||||
if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
|
|
||||||
{
|
|
||||||
if ((pos.Z - m_VhoverTargetHeight) > .2 || (pos.Z - m_VhoverTargetHeight) < -.2)
|
|
||||||
{
|
|
||||||
m_prim.Position = pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
float herr0 = pos.Z - m_VhoverTargetHeight;
|
|
||||||
// Replace Vertical speed with correction figure if significant
|
|
||||||
if (Math.Abs(herr0) > 0.01f)
|
|
||||||
{
|
|
||||||
m_dir.Z = -((herr0 * pTimestep * 50.0f) / m_VhoverTimescale);
|
|
||||||
//KF: m_VhoverEfficiency is not yet implemented
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_dir.Z = 0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", m_prim.LocalID, pos, m_dir, m_VhoverHeight, m_VhoverTargetHeight);
|
|
||||||
|
|
||||||
// m_VhoverEfficiency = 0f; // 0=boucy, 1=Crit.damped
|
|
||||||
// m_VhoverTimescale = 0f; // time to acheive height
|
|
||||||
// pTimestep is time since last frame,in secs
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
|
if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
|
||||||
{
|
{
|
||||||
//Start Experimental Values
|
|
||||||
if (Zchange > .3)
|
if (Zchange > .3)
|
||||||
{
|
|
||||||
grav.Z = (float)(grav.Z * 3);
|
grav.Z = (float)(grav.Z * 3);
|
||||||
}
|
|
||||||
if (Zchange > .15)
|
if (Zchange > .15)
|
||||||
{
|
|
||||||
grav.Z = (float)(grav.Z * 2);
|
grav.Z = (float)(grav.Z * 2);
|
||||||
}
|
|
||||||
if (Zchange > .75)
|
if (Zchange > .75)
|
||||||
{
|
|
||||||
grav.Z = (float)(grav.Z * 1.5);
|
grav.Z = (float)(grav.Z * 1.5);
|
||||||
}
|
|
||||||
if (Zchange > .05)
|
if (Zchange > .05)
|
||||||
{
|
|
||||||
grav.Z = (float)(grav.Z * 1.25);
|
grav.Z = (float)(grav.Z * 1.25);
|
||||||
}
|
|
||||||
if (Zchange > .025)
|
if (Zchange > .025)
|
||||||
{
|
|
||||||
grav.Z = (float)(grav.Z * 1.125);
|
grav.Z = (float)(grav.Z * 1.125);
|
||||||
}
|
float postemp = (pos.Z - terrainHeight);
|
||||||
float terraintemp = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos);
|
|
||||||
float postemp = (pos.Z - terraintemp);
|
|
||||||
if (postemp > 2.5f)
|
if (postemp > 2.5f)
|
||||||
{
|
|
||||||
grav.Z = (float)(grav.Z * 1.037125);
|
grav.Z = (float)(grav.Z * 1.037125);
|
||||||
}
|
|
||||||
VDetailLog("{0},MoveLinear,limitMotorUp,grav={1}", m_prim.LocalID, grav);
|
VDetailLog("{0},MoveLinear,limitMotorUp,grav={1}", m_prim.LocalID, grav);
|
||||||
//End Experimental Values
|
|
||||||
}
|
}
|
||||||
if ((m_flags & (VehicleFlag.NO_X)) != 0)
|
if ((m_flags & (VehicleFlag.NO_X)) != 0)
|
||||||
{
|
m_newVelocity.X = 0;
|
||||||
m_dir.X = 0;
|
|
||||||
}
|
|
||||||
if ((m_flags & (VehicleFlag.NO_Y)) != 0)
|
if ((m_flags & (VehicleFlag.NO_Y)) != 0)
|
||||||
{
|
m_newVelocity.Y = 0;
|
||||||
m_dir.Y = 0;
|
|
||||||
}
|
|
||||||
if ((m_flags & (VehicleFlag.NO_Z)) != 0)
|
if ((m_flags & (VehicleFlag.NO_Z)) != 0)
|
||||||
{
|
m_newVelocity.Z = 0;
|
||||||
m_dir.Z = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_lastPositionVector = m_prim.Position;
|
|
||||||
|
|
||||||
// Apply velocity
|
// Apply velocity
|
||||||
m_prim.Velocity = m_dir;
|
m_prim.Velocity = m_newVelocity;
|
||||||
// apply gravity force
|
// apply gravity force
|
||||||
// Why is this set here? The physics engine already does gravity.
|
// Why is this set here? The physics engine already does gravity.
|
||||||
// m_prim.AddForce(grav, false);
|
// m_prim.AddForce(grav, false);
|
||||||
// m_prim.Force = grav;
|
|
||||||
|
|
||||||
// Apply friction
|
// Apply friction
|
||||||
Vector3 decayamount = Vector3.One / (m_linearFrictionTimescale / pTimestep);
|
Vector3 keepFraction = Vector3.One - (Vector3.One / (m_linearFrictionTimescale / pTimestep));
|
||||||
m_lastLinearVelocityVector -= m_lastLinearVelocityVector * decayamount;
|
m_lastLinearVelocityVector *= keepFraction;
|
||||||
|
|
||||||
VDetailLog("{0},MoveLinear,done,pos={1},vel={2},force={3},decay={4}",
|
VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},grav={4},1Mdecay={5}",
|
||||||
m_prim.LocalID, m_lastPositionVector, m_dir, grav, decayamount);
|
m_prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, grav, keepFraction);
|
||||||
|
|
||||||
} // end MoveLinear()
|
} // end MoveLinear()
|
||||||
|
|
||||||
|
@ -735,17 +709,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
// There are m_angularMotorApply steps.
|
// There are m_angularMotorApply steps.
|
||||||
Vector3 origAngularVelocity = m_angularMotorVelocity;
|
Vector3 origAngularVelocity = m_angularMotorVelocity;
|
||||||
// ramp up to new value
|
// ramp up to new value
|
||||||
// current velocity += error / (time to get there / step interval)
|
// current velocity += error / ( time to get there / step interval)
|
||||||
// requested speed - last motor speed
|
// requested speed - last motor speed
|
||||||
m_angularMotorVelocity.X += (m_angularMotorDirection.X - m_angularMotorVelocity.X) / (m_angularMotorTimescale / pTimestep);
|
m_angularMotorVelocity.X += (m_angularMotorDirection.X - m_angularMotorVelocity.X) / (m_angularMotorTimescale / pTimestep);
|
||||||
m_angularMotorVelocity.Y += (m_angularMotorDirection.Y - m_angularMotorVelocity.Y) / (m_angularMotorTimescale / pTimestep);
|
m_angularMotorVelocity.Y += (m_angularMotorDirection.Y - m_angularMotorVelocity.Y) / (m_angularMotorTimescale / pTimestep);
|
||||||
m_angularMotorVelocity.Z += (m_angularMotorDirection.Z - m_angularMotorVelocity.Z) / (m_angularMotorTimescale / pTimestep);
|
m_angularMotorVelocity.Z += (m_angularMotorDirection.Z - m_angularMotorVelocity.Z) / (m_angularMotorTimescale / pTimestep);
|
||||||
|
|
||||||
VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},origvel={2},dir={3},vel={4}",
|
VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},angTScale={2},timeStep={3},origvel={4},dir={5},vel={6}",
|
||||||
m_prim.LocalID,m_angularMotorApply,origAngularVelocity, m_angularMotorDirection, m_angularMotorVelocity);
|
m_prim.LocalID, m_angularMotorApply, m_angularMotorTimescale, pTimestep, origAngularVelocity, m_angularMotorDirection, m_angularMotorVelocity);
|
||||||
|
|
||||||
m_angularMotorApply--; // This is done so that if script request rate is less than phys frame rate the expected
|
// This is done so that if script request rate is less than phys frame rate the expected
|
||||||
// velocity may still be acheived.
|
// velocity may still be acheived.
|
||||||
|
m_angularMotorApply--;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -760,7 +735,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
Vector3 vertattr = Vector3.Zero;
|
Vector3 vertattr = Vector3.Zero;
|
||||||
if (m_verticalAttractionTimescale < 300)
|
if (m_verticalAttractionTimescale < 300)
|
||||||
{
|
{
|
||||||
float VAservo = 0.2f / (m_verticalAttractionTimescale * pTimestep);
|
float VAservo = 0.2f / (m_verticalAttractionTimescale / pTimestep);
|
||||||
// get present body rotation
|
// get present body rotation
|
||||||
Quaternion rotq = m_prim.Orientation;
|
Quaternion rotq = m_prim.Orientation;
|
||||||
// make a vector pointing up
|
// make a vector pointing up
|
||||||
|
@ -863,16 +838,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
m_rot.Y = 0;
|
m_rot.Y = 0;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0)
|
|
||||||
{
|
|
||||||
m_rot.X = 0;
|
|
||||||
m_rot.Y = 0;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (changed)
|
if (changed)
|
||||||
|
{
|
||||||
m_prim.Orientation = m_rot;
|
m_prim.Orientation = m_rot;
|
||||||
|
VDetailLog("{0},LimitRotation,done,orig={1},new={2}", m_prim.LocalID, rotq, m_rot);
|
||||||
|
}
|
||||||
|
|
||||||
VDetailLog("{0},LimitRotation,done,changed={1},orig={2},new={3}", m_prim.LocalID, changed, rotq, m_rot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke the detailed logger and output something if it's enabled.
|
// Invoke the detailed logger and output something if it's enabled.
|
||||||
|
|
|
@ -202,11 +202,33 @@ public class BSLinkset
|
||||||
return com;
|
return com;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The object is going dynamic (physical). Do any setup necessary
|
||||||
|
// for a dynamic linkset.
|
||||||
|
// Only the state of the passed object can be modified. The rest of the linkset
|
||||||
|
// has not yet been fully constructed.
|
||||||
|
// Return 'true' if any properties updated on the passed object.
|
||||||
|
// Called at taint-time!
|
||||||
|
public bool MakeDynamic(BSPhysObject child)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The object is going static (non-physical). Do any setup necessary
|
||||||
|
// for a static linkset.
|
||||||
|
// Return 'true' if any properties updated on the passed object.
|
||||||
|
// Called at taint-time!
|
||||||
|
public bool MakeStatic(BSPhysObject child)
|
||||||
|
{
|
||||||
|
// What is done for each object in BSPrim is what we want.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// When physical properties are changed the linkset needs to recalculate
|
// When physical properties are changed the linkset needs to recalculate
|
||||||
// its internal properties.
|
// its internal properties.
|
||||||
public void Refresh(BSPhysObject requestor)
|
public void Refresh(BSPhysObject requestor)
|
||||||
{
|
{
|
||||||
// If there are no children, there aren't any constraints to recompute
|
// If there are no children, there can't be any constraints to recompute
|
||||||
if (!HasAnyChildren)
|
if (!HasAnyChildren)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -225,11 +247,12 @@ public class BSLinkset
|
||||||
// from a linkset to make sure the constraints know about the new mass and
|
// from a linkset to make sure the constraints know about the new mass and
|
||||||
// geometry.
|
// geometry.
|
||||||
// Must only be called at taint time!!
|
// Must only be called at taint time!!
|
||||||
private bool RecomputeLinksetConstraintVariables()
|
private void RecomputeLinksetConstraintVariables()
|
||||||
{
|
{
|
||||||
float linksetMass = LinksetMass;
|
float linksetMass = LinksetMass;
|
||||||
lock (m_linksetActivityLock)
|
lock (m_linksetActivityLock)
|
||||||
{
|
{
|
||||||
|
bool somethingMissing = false;
|
||||||
foreach (BSPhysObject child in m_children)
|
foreach (BSPhysObject child in m_children)
|
||||||
{
|
{
|
||||||
BSConstraint constrain;
|
BSConstraint constrain;
|
||||||
|
@ -241,16 +264,36 @@ public class BSLinkset
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Non-fatal error that can happen when children are being added to the linkset but
|
// Non-fatal error that happens when children are being added to the linkset but
|
||||||
// their constraints have not been created yet.
|
// their constraints have not been created yet.
|
||||||
// Caused by the fact that m_children is built at run time but building constraints
|
// Caused by the fact that m_children is built at run time but building constraints
|
||||||
// happens at taint time.
|
// happens at taint time.
|
||||||
// m_physicsScene.Logger.ErrorFormat("{0} RecomputeLinksetConstraintVariables: constraint not found for root={1}, child={2}",
|
somethingMissing = true;
|
||||||
// LogHeader, m_linksetRoot.Body.ID, child.Body.ID);
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the whole linkset is not here, doesn't make sense to recompute linkset wide values
|
||||||
|
if (!somethingMissing)
|
||||||
|
{
|
||||||
|
// If this is a multiple object linkset, set everybody's center of mass to the set's center of mass
|
||||||
|
OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass();
|
||||||
|
BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.Ptr, centerOfMass, OMV.Quaternion.Identity);
|
||||||
|
foreach (BSPhysObject child in m_children)
|
||||||
|
{
|
||||||
|
BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.Ptr, centerOfMass, OMV.Quaternion.Identity);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// The root prim takes on the weight of the whole linkset
|
||||||
|
OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(LinksetRoot.BSShape.Ptr, linksetMass);
|
||||||
|
BulletSimAPI.SetMassProps2(LinksetRoot.BSBody.Ptr, linksetMass, inertia);
|
||||||
|
OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass();
|
||||||
|
BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.Ptr, centerOfMass, OMV.Quaternion.Identity);
|
||||||
|
BulletSimAPI.UpdateInertiaTensor2(LinksetRoot.BSBody.Ptr);
|
||||||
|
*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// I am the root of a linkset and a new child is being added
|
// I am the root of a linkset and a new child is being added
|
||||||
|
@ -296,9 +339,9 @@ public class BSLinkset
|
||||||
DetailLog("{0},RemoveChildFromLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID);
|
DetailLog("{0},RemoveChildFromLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID);
|
||||||
|
|
||||||
PhysicallyUnlinkAChildFromRoot(rootx, childx);
|
PhysicallyUnlinkAChildFromRoot(rootx, childx);
|
||||||
|
RecomputeLinksetConstraintVariables();
|
||||||
});
|
});
|
||||||
|
|
||||||
RecomputeLinksetConstraintVariables();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -377,6 +420,10 @@ public class BSLinkset
|
||||||
PhysicsScene.Params.linkConstraintTransMotorMaxVel,
|
PhysicsScene.Params.linkConstraintTransMotorMaxVel,
|
||||||
PhysicsScene.Params.linkConstraintTransMotorMaxForce);
|
PhysicsScene.Params.linkConstraintTransMotorMaxForce);
|
||||||
constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP);
|
constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP);
|
||||||
|
if (PhysicsScene.Params.linkConstraintSolverIterations != 0f)
|
||||||
|
{
|
||||||
|
constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations);
|
||||||
|
}
|
||||||
|
|
||||||
RecomputeLinksetConstraintVariables();
|
RecomputeLinksetConstraintVariables();
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ public abstract class BSPhysObject : PhysicsActor
|
||||||
{
|
{
|
||||||
public abstract BSLinkset Linkset { get; set; }
|
public abstract BSLinkset Linkset { get; set; }
|
||||||
|
|
||||||
public abstract void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type,
|
public abstract bool Collide(uint collidingWith, BSPhysObject collidee,
|
||||||
OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth);
|
OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth);
|
||||||
public abstract void SendCollisions();
|
public abstract void SendCollisions();
|
||||||
|
|
||||||
|
|
|
@ -136,11 +136,11 @@ public sealed class BSPrim : BSPhysObject
|
||||||
Linkset = new BSLinkset(Scene, this); // a linkset of one
|
Linkset = new BSLinkset(Scene, this); // a linkset of one
|
||||||
_vehicle = new BSDynamics(Scene, this); // add vehicleness
|
_vehicle = new BSDynamics(Scene, this); // add vehicleness
|
||||||
_mass = CalculateMass();
|
_mass = CalculateMass();
|
||||||
// do the actual object creation at taint time
|
|
||||||
DetailLog("{0},BSPrim.constructor,call", LocalID);
|
DetailLog("{0},BSPrim.constructor,call", LocalID);
|
||||||
|
// do the actual object creation at taint time
|
||||||
_scene.TaintedObject("BSPrim.create", delegate()
|
_scene.TaintedObject("BSPrim.create", delegate()
|
||||||
{
|
{
|
||||||
RecreateGeomAndObject();
|
CreateGeomAndObject(true);
|
||||||
|
|
||||||
// Get the pointer to the physical body for this object.
|
// Get the pointer to the physical body for this object.
|
||||||
// At the moment, we're still letting BulletSim manage the creation and destruction
|
// At the moment, we're still letting BulletSim manage the creation and destruction
|
||||||
|
@ -186,9 +186,10 @@ public sealed class BSPrim : BSPhysObject
|
||||||
_scene.TaintedObject("BSPrim.setSize", delegate()
|
_scene.TaintedObject("BSPrim.setSize", delegate()
|
||||||
{
|
{
|
||||||
_mass = CalculateMass(); // changing size changes the mass
|
_mass = CalculateMass(); // changing size changes the mass
|
||||||
BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, (IsPhysical ? _mass : 0f), IsPhysical);
|
// Since _size changed, the mesh needs to be rebuilt. If rebuilt, all the correct
|
||||||
DetailLog("{0}: BSPrim.setSize: size={1}, mass={2}, physical={3}", LocalID, _size, _mass, IsPhysical);
|
// scale and margins are set.
|
||||||
RecreateGeomAndObject();
|
CreateGeomAndObject(true);
|
||||||
|
DetailLog("{0}: BSPrim.setSize: size={1}, scale={2}, mass={3}, physical={4}", LocalID, _size, _scale, _mass, IsPhysical);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,7 +199,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
_scene.TaintedObject("BSPrim.setShape", delegate()
|
_scene.TaintedObject("BSPrim.setShape", delegate()
|
||||||
{
|
{
|
||||||
_mass = CalculateMass(); // changing the shape changes the mass
|
_mass = CalculateMass(); // changing the shape changes the mass
|
||||||
RecreateGeomAndObject();
|
CreateGeomAndObject(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,7 +280,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
get {
|
get {
|
||||||
if (!Linkset.IsRoot(this))
|
if (!Linkset.IsRoot(this))
|
||||||
// child prims move around based on their parent. Need to get the latest location
|
// child prims move around based on their parent. Need to get the latest location
|
||||||
_position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
|
_position = BulletSimAPI.GetPosition2(BSBody.Ptr);
|
||||||
|
|
||||||
// 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.GetObjectPosition(_scene.WorldID, _localID);
|
// _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
|
||||||
|
@ -291,7 +292,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
_scene.TaintedObject("BSPrim.setPosition", delegate()
|
_scene.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);
|
||||||
BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation);
|
BulletSimAPI.SetTranslation2(BSBody.Ptr, _position, _orientation);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,7 +303,8 @@ public sealed class BSPrim : BSPhysObject
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return Linkset.LinksetMass;
|
// return Linkset.LinksetMass;
|
||||||
|
return _mass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +330,6 @@ public sealed class BSPrim : BSPhysObject
|
||||||
_scene.TaintedObject("BSPrim.setForce", delegate()
|
_scene.TaintedObject("BSPrim.setForce", delegate()
|
||||||
{
|
{
|
||||||
DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force);
|
DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force);
|
||||||
// BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force);
|
|
||||||
BulletSimAPI.SetObjectForce2(BSBody.Ptr, _force);
|
BulletSimAPI.SetObjectForce2(BSBody.Ptr, _force);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -345,7 +346,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
{
|
{
|
||||||
// Done at taint time so we're sure the physics engine is not using the variables
|
// Done at taint time so we're sure the physics engine is not using the variables
|
||||||
// Vehicle code changes the parameters for this vehicle type.
|
// Vehicle code changes the parameters for this vehicle type.
|
||||||
_vehicle.ProcessTypeChange(type, Scene.LastSimulatedTimestep);
|
_vehicle.ProcessTypeChange(type);
|
||||||
// Tell the scene about the vehicle so it will get processing each frame.
|
// Tell the scene about the vehicle so it will get processing each frame.
|
||||||
_scene.VehicleInSceneTypeChanged(this, type);
|
_scene.VehicleInSceneTypeChanged(this, type);
|
||||||
});
|
});
|
||||||
|
@ -355,14 +356,14 @@ public sealed class BSPrim : BSPhysObject
|
||||||
{
|
{
|
||||||
_scene.TaintedObject("BSPrim.VehicleFloatParam", delegate()
|
_scene.TaintedObject("BSPrim.VehicleFloatParam", delegate()
|
||||||
{
|
{
|
||||||
_vehicle.ProcessFloatVehicleParam((Vehicle)param, value, _scene.LastSimulatedTimestep);
|
_vehicle.ProcessFloatVehicleParam((Vehicle)param, value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public override void VehicleVectorParam(int param, OMV.Vector3 value)
|
public override void VehicleVectorParam(int param, OMV.Vector3 value)
|
||||||
{
|
{
|
||||||
_scene.TaintedObject("BSPrim.VehicleVectorParam", delegate()
|
_scene.TaintedObject("BSPrim.VehicleVectorParam", delegate()
|
||||||
{
|
{
|
||||||
_vehicle.ProcessVectorVehicleParam((Vehicle)param, value, _scene.LastSimulatedTimestep);
|
_vehicle.ProcessVectorVehicleParam((Vehicle)param, value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public override void VehicleRotationParam(int param, OMV.Quaternion rotation)
|
public override void VehicleRotationParam(int param, OMV.Quaternion rotation)
|
||||||
|
@ -406,7 +407,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
_scene.TaintedObject("BSPrim.setVelocity", delegate()
|
_scene.TaintedObject("BSPrim.setVelocity", delegate()
|
||||||
{
|
{
|
||||||
DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
|
DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
|
||||||
BulletSimAPI.SetObjectVelocity(_scene.WorldID, LocalID, _velocity);
|
BulletSimAPI.SetLinearVelocity2(BSBody.Ptr, _velocity);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,7 +431,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
if (!Linkset.IsRoot(this))
|
if (!Linkset.IsRoot(this))
|
||||||
{
|
{
|
||||||
// Children move around because tied to parent. Get a fresh value.
|
// Children move around because tied to parent. Get a fresh value.
|
||||||
_orientation = BulletSimAPI.GetObjectOrientation(_scene.WorldID, LocalID);
|
_orientation = BulletSimAPI.GetOrientation2(BSBody.Ptr);
|
||||||
}
|
}
|
||||||
return _orientation;
|
return _orientation;
|
||||||
}
|
}
|
||||||
|
@ -441,7 +442,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
{
|
{
|
||||||
// _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
|
// _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
|
||||||
DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
||||||
BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation);
|
BulletSimAPI.SetTranslation2(BSBody.Ptr, _position, _orientation);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -483,31 +484,37 @@ public sealed class BSPrim : BSPhysObject
|
||||||
{
|
{
|
||||||
// If it's becoming dynamic, it will need hullness
|
// If it's becoming dynamic, it will need hullness
|
||||||
VerifyCorrectPhysicalShape();
|
VerifyCorrectPhysicalShape();
|
||||||
|
UpdatePhysicalParameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePhysicalParameters()
|
||||||
|
{
|
||||||
|
/*
|
||||||
// Bullet wants static objects to have a mass of zero
|
// Bullet wants static objects to have a mass of zero
|
||||||
float mass = IsStatic ? 0f : _mass;
|
float mass = IsStatic ? 0f : _mass;
|
||||||
|
|
||||||
BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), mass);
|
BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), mass);
|
||||||
/*
|
*/
|
||||||
BulletSimAPI.RemoveObjectFromWorld2(Scene.World.Ptr, BSBody.Ptr);
|
BulletSimAPI.RemoveObjectFromWorld2(Scene.World.Ptr, BSBody.Ptr);
|
||||||
|
|
||||||
// Set up the object physicalness (static or dynamic)
|
// Set up the object physicalness (does gravity and collisions move this object)
|
||||||
MakeDynamic();
|
MakeDynamic(IsStatic);
|
||||||
|
|
||||||
// Make solid or not and arrange for collisions, etc
|
// Make solid or not (do things bounce off or pass through this object)
|
||||||
MakeSolid();
|
MakeSolid(IsSolid);
|
||||||
|
|
||||||
m_currentCollisionFlags = BulletSimAPI.GetCollisionFlags2(BSBody.Ptr);
|
// Arrange for collisions events if the simulator wants them
|
||||||
|
EnableCollisions(SubscribedEvents());
|
||||||
|
|
||||||
BulletSimAPI.AddObjectToWorld2(Scene.World.Ptr, BSBody.Ptr);
|
BulletSimAPI.AddObjectToWorld2(Scene.World.Ptr, BSBody.Ptr);
|
||||||
*/
|
|
||||||
|
|
||||||
// Recompute any linkset parameters.
|
// Recompute any linkset parameters.
|
||||||
// When going from non-physical to physical, this re-enables the constraints that
|
// When going from non-physical to physical, this re-enables the constraints that
|
||||||
// had been automatically disabled when the mass was set to zero.
|
// had been automatically disabled when the mass was set to zero.
|
||||||
Linkset.Refresh(this);
|
Linkset.Refresh(this);
|
||||||
|
|
||||||
DetailLog("{0},BSPrim.SetObjectDynamic,taint,static={1},solid={2},mass={3}, cf={4}", LocalID, IsStatic, IsSolid, mass, m_currentCollisionFlags);
|
DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,static={1},solid={2},mass={3}, cf={4}",
|
||||||
|
LocalID, IsStatic, IsSolid, _mass, m_currentCollisionFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Making dynamic" means changing to and from static.
|
// "Making dynamic" means changing to and from static.
|
||||||
|
@ -515,39 +522,84 @@ public sealed class BSPrim : BSPhysObject
|
||||||
// When dynamic, the object can fall and be pushed by others.
|
// When dynamic, the object can fall and be pushed by others.
|
||||||
// This is independent of its 'solidness' which controls what passes through
|
// This is independent of its 'solidness' which controls what passes through
|
||||||
// this object and what interacts with it.
|
// this object and what interacts with it.
|
||||||
private void MakeDynamic()
|
private void MakeDynamic(bool makeStatic)
|
||||||
{
|
{
|
||||||
if (IsStatic)
|
if (makeStatic)
|
||||||
{
|
{
|
||||||
// Become a Bullet 'static' object type
|
// Become a Bullet 'static' object type
|
||||||
BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
|
m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
|
||||||
// Stop all movement
|
// Stop all movement
|
||||||
BulletSimAPI.ClearAllForces2(BSBody.Ptr);
|
BulletSimAPI.ClearAllForces2(BSBody.Ptr);
|
||||||
|
// Center of mass is at the center of the object
|
||||||
|
BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.BSBody.Ptr, _position, _orientation);
|
||||||
// Mass is zero which disables a bunch of physics stuff in Bullet
|
// Mass is zero which disables a bunch of physics stuff in Bullet
|
||||||
BulletSimAPI.SetMassProps2(BSBody.Ptr, 0f, OMV.Vector3.Zero);
|
BulletSimAPI.SetMassProps2(BSBody.Ptr, 0f, OMV.Vector3.Zero);
|
||||||
// There is no inertia in a static object
|
// There is no inertia in a static object
|
||||||
BulletSimAPI.UpdateInertiaTensor2(BSBody.Ptr);
|
BulletSimAPI.UpdateInertiaTensor2(BSBody.Ptr);
|
||||||
|
// There can be special things needed for implementing linksets
|
||||||
|
Linkset.MakeStatic(this);
|
||||||
// The activation state is 'sleeping' so Bullet will not try to act on it
|
// The activation state is 'sleeping' so Bullet will not try to act on it
|
||||||
BulletSimAPI.ForceActivationState2(BSBody.Ptr, ActivationState.ISLAND_SLEEPING);
|
BulletSimAPI.ForceActivationState2(BSBody.Ptr, ActivationState.ISLAND_SLEEPING);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Not a Bullet static object
|
// Not a Bullet static object
|
||||||
BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
|
m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
|
||||||
|
|
||||||
|
// Set various physical properties so internal things will get computed correctly as they are set
|
||||||
|
BulletSimAPI.SetFriction2(BSBody.Ptr, Scene.Params.defaultFriction);
|
||||||
|
BulletSimAPI.SetRestitution2(BSBody.Ptr, Scene.Params.defaultRestitution);
|
||||||
|
// per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
|
||||||
|
BulletSimAPI.SetInterpolationLinearVelocity2(BSBody.Ptr, OMV.Vector3.Zero);
|
||||||
|
BulletSimAPI.SetInterpolationAngularVelocity2(BSBody.Ptr, OMV.Vector3.Zero);
|
||||||
|
BulletSimAPI.SetInterpolationVelocity2(BSBody.Ptr, OMV.Vector3.Zero, OMV.Vector3.Zero);
|
||||||
|
|
||||||
// A dynamic object has mass
|
// A dynamic object has mass
|
||||||
BulletSimAPI.SetMassProps2(BSBody.Ptr, _mass, OMV.Vector3.Zero);
|
|
||||||
// The shape is interesting and has mass and a center of gravity
|
|
||||||
IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.Ptr);
|
IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.Ptr);
|
||||||
BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, _mass, OMV.Vector3.Zero);
|
OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, Linkset.LinksetMass);
|
||||||
|
BulletSimAPI.SetMassProps2(BSBody.Ptr, _mass, inertia);
|
||||||
// Inertia is based on our new mass
|
// Inertia is based on our new mass
|
||||||
BulletSimAPI.UpdateInertiaTensor2(BSBody.Ptr);
|
BulletSimAPI.UpdateInertiaTensor2(BSBody.Ptr);
|
||||||
|
|
||||||
|
// Various values for simulation limits
|
||||||
|
BulletSimAPI.SetDamping2(BSBody.Ptr, Scene.Params.linearDamping, Scene.Params.angularDamping);
|
||||||
|
BulletSimAPI.SetDeactivationTime2(BSBody.Ptr, Scene.Params.deactivationTime);
|
||||||
|
BulletSimAPI.SetSleepingThresholds2(BSBody.Ptr, Scene.Params.linearSleepingThreshold, Scene.Params.angularSleepingThreshold);
|
||||||
|
BulletSimAPI.SetContactProcessingThreshold2(BSBody.Ptr, Scene.Params.contactProcessingThreshold);
|
||||||
|
|
||||||
|
// There can be special things needed for implementing linksets
|
||||||
|
Linkset.MakeDynamic(this);
|
||||||
|
|
||||||
// Force activation of the object so Bullet will act on it.
|
// Force activation of the object so Bullet will act on it.
|
||||||
BulletSimAPI.Activate2(BSBody.Ptr, true);
|
BulletSimAPI.Activate2(BSBody.Ptr, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MakeSolid()
|
// "Making solid" means that other object will not pass through this object.
|
||||||
|
private void MakeSolid(bool makeSolid)
|
||||||
{
|
{
|
||||||
|
if (makeSolid)
|
||||||
|
{
|
||||||
|
// Easy in Bullet -- just remove the object flag that controls collision response
|
||||||
|
m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn on or off the flag controlling whether collision events are returned to the simulator.
|
||||||
|
private void EnableCollisions(bool wantsCollisionEvents)
|
||||||
|
{
|
||||||
|
if (wantsCollisionEvents)
|
||||||
|
{
|
||||||
|
m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// prims don't fly
|
// prims don't fly
|
||||||
|
@ -607,7 +659,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
_scene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
|
_scene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
|
||||||
{
|
{
|
||||||
DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
|
DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
|
||||||
BulletSimAPI.SetObjectAngularVelocity(_scene.WorldID, LocalID, _rotationalVelocity);
|
BulletSimAPI.SetAngularVelocity2(BSBody.Ptr, _rotationalVelocity);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -624,7 +676,10 @@ public sealed class BSPrim : BSPhysObject
|
||||||
_scene.TaintedObject("BSPrim.setBuoyancy", delegate()
|
_scene.TaintedObject("BSPrim.setBuoyancy", delegate()
|
||||||
{
|
{
|
||||||
DetailLog("{0},BSPrim.SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
|
DetailLog("{0},BSPrim.SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
|
||||||
BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy);
|
// Buoyancy is faked by changing the gravity applied to the object
|
||||||
|
float grav = Scene.Params.gravity * (1f - _buoyancy);
|
||||||
|
BulletSimAPI.SetGravity2(BSBody.Ptr, new OMV.Vector3(0f, 0f, grav));
|
||||||
|
// BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -686,8 +741,8 @@ public sealed class BSPrim : BSPhysObject
|
||||||
}
|
}
|
||||||
m_accumulatedForces.Clear();
|
m_accumulatedForces.Clear();
|
||||||
}
|
}
|
||||||
DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, _force);
|
DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, fSum);
|
||||||
// For unknown reason, "ApplyCentralForce" is really additive.
|
// For unknown reasons, "ApplyCentralForce" adds this force to the total force on the object.
|
||||||
BulletSimAPI.ApplyCentralForce2(BSBody.Ptr, fSum);
|
BulletSimAPI.ApplyCentralForce2(BSBody.Ptr, fSum);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1023,36 +1078,44 @@ public sealed class BSPrim : BSPhysObject
|
||||||
}// end CalculateMass
|
}// end CalculateMass
|
||||||
#endregion Mass Calculation
|
#endregion Mass Calculation
|
||||||
|
|
||||||
// Create the geometry information in Bullet for later use
|
// Create the geometry information in Bullet for later use.
|
||||||
// The objects needs a hull if it's physical otherwise a mesh is enough
|
// The objects needs a hull if it's physical otherwise a mesh is enough.
|
||||||
// No locking here because this is done when we know physics is not simulating
|
// No locking here because this is done when we know physics is not simulating.
|
||||||
// if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used
|
// if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used.
|
||||||
// Returns 'true' if the geometry was rebuilt
|
// Returns 'true' if the geometry was rebuilt.
|
||||||
|
// Called at taint-time!
|
||||||
private bool CreateGeom(bool forceRebuild)
|
private bool CreateGeom(bool forceRebuild)
|
||||||
{
|
{
|
||||||
// the mesher thought this was too simple to mesh. Use a native Bullet collision shape.
|
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
if (!_scene.NeedsMeshing(_pbs))
|
bool haveShape = false;
|
||||||
|
|
||||||
|
// If the prim attributes are simple, this could be a simple Bullet native shape
|
||||||
|
if ((_pbs.SculptEntry && !Scene.ShouldMeshSculptedPrim)
|
||||||
|
|| (_pbs.ProfileBegin == 0 && _pbs.ProfileEnd == 0
|
||||||
|
&& _pbs.ProfileHollow == 0
|
||||||
|
&& _pbs.PathTwist == 0 && _pbs.PathTwistBegin == 0
|
||||||
|
&& _pbs.PathBegin == 0 && _pbs.PathEnd == 0
|
||||||
|
&& _pbs.PathTaperX == 0 && _pbs.PathTaperY == 0
|
||||||
|
&& _pbs.PathScaleX == 100 && _pbs.PathScaleY == 100
|
||||||
|
&& _pbs.PathShearX == 0 && _pbs.PathShearY == 0) )
|
||||||
{
|
{
|
||||||
if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1)
|
if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1)
|
||||||
{
|
{
|
||||||
// if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z)
|
haveShape = true;
|
||||||
// {
|
if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_SPHERE))
|
||||||
// m_log.DebugFormat("{0}: CreateGeom: Defaulting to sphere of size {1}", LogHeader, _size);
|
{
|
||||||
if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_SPHERE))
|
DetailLog("{0},BSPrim.CreateGeom,sphere (force={1}", LocalID, forceRebuild);
|
||||||
{
|
_shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE;
|
||||||
DetailLog("{0},BSPrim.CreateGeom,sphere (force={1}", LocalID, forceRebuild);
|
// Bullet native objects are scaled by the Bullet engine so pass the size in
|
||||||
_shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE;
|
_scale = _size;
|
||||||
// Bullet native objects are scaled by the Bullet engine so pass the size in
|
// TODO: do we need to check for and destroy a mesh or hull that might have been left from before?
|
||||||
_scale = _size;
|
ret = true;
|
||||||
// TODO: do we need to check for and destroy a mesh or hull that might have been left from before?
|
}
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, type={2}, size={3}", LogHeader, LocalID, _shapeType, _size);
|
// m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, type={2}, size={3}", LogHeader, LocalID, _shapeType, _size);
|
||||||
|
haveShape = true;
|
||||||
if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_BOX))
|
if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_BOX))
|
||||||
{
|
{
|
||||||
DetailLog("{0},BSPrim.CreateGeom,box (force={1})", LocalID, forceRebuild);
|
DetailLog("{0},BSPrim.CreateGeom,box (force={1})", LocalID, forceRebuild);
|
||||||
|
@ -1063,16 +1126,16 @@ public sealed class BSPrim : BSPhysObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
// If a simple shape isn't happening, create a mesh and possibly a hull
|
||||||
|
if (!haveShape)
|
||||||
{
|
{
|
||||||
if (IsPhysical)
|
if (IsPhysical)
|
||||||
{
|
{
|
||||||
if (forceRebuild || _hullKey == 0)
|
if (forceRebuild || _hullKey == 0)
|
||||||
{
|
{
|
||||||
// physical objects require a hull for interaction.
|
// physical objects require a hull for interaction.
|
||||||
// This will create the mesh if it doesn't already exist
|
// This also creates the mesh if it doesn't already exist
|
||||||
CreateGeomHull();
|
ret = CreateGeomHull();
|
||||||
ret = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1080,8 +1143,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
if (forceRebuild || _meshKey == 0)
|
if (forceRebuild || _meshKey == 0)
|
||||||
{
|
{
|
||||||
// Static (non-physical) objects only need a mesh for bumping into
|
// Static (non-physical) objects only need a mesh for bumping into
|
||||||
CreateGeomMesh();
|
ret = CreateGeomMesh();
|
||||||
ret = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1089,7 +1151,9 @@ public sealed class BSPrim : BSPhysObject
|
||||||
}
|
}
|
||||||
|
|
||||||
// No locking here because this is done when we know physics is not simulating
|
// No locking here because this is done when we know physics is not simulating
|
||||||
private void CreateGeomMesh()
|
// Returns 'true' of a mesh was actually rebuild (we could also have one of these specs).
|
||||||
|
// Called at taint-time!
|
||||||
|
private bool CreateGeomMesh()
|
||||||
{
|
{
|
||||||
// level of detail based on size and type of the object
|
// level of detail based on size and type of the object
|
||||||
float lod = _scene.MeshLOD;
|
float lod = _scene.MeshLOD;
|
||||||
|
@ -1103,7 +1167,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
// m_log.DebugFormat("{0}: CreateGeomMesh: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _meshKey, newMeshKey);
|
// m_log.DebugFormat("{0}: CreateGeomMesh: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _meshKey, newMeshKey);
|
||||||
|
|
||||||
// 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 (_meshKey == newMeshKey) return;
|
if (_meshKey == newMeshKey) return false;
|
||||||
|
|
||||||
DetailLog("{0},BSPrim.CreateGeomMesh,create,key={1}", LocalID, newMeshKey);
|
DetailLog("{0},BSPrim.CreateGeomMesh,create,key={1}", LocalID, newMeshKey);
|
||||||
// Since we're recreating new, get rid of any previously generated shape
|
// Since we're recreating new, get rid of any previously generated shape
|
||||||
|
@ -1140,19 +1204,19 @@ public sealed class BSPrim : BSPhysObject
|
||||||
_shapeType = ShapeData.PhysicsShapeType.SHAPE_MESH;
|
_shapeType = ShapeData.PhysicsShapeType.SHAPE_MESH;
|
||||||
// meshes are already scaled by the meshmerizer
|
// meshes are already scaled by the meshmerizer
|
||||||
_scale = new OMV.Vector3(1f, 1f, 1f);
|
_scale = new OMV.Vector3(1f, 1f, 1f);
|
||||||
DetailLog("{0},BSPrim.CreateGeomMesh,done", LocalID);
|
return true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No locking here because this is done when we know physics is not simulating
|
// No locking here because this is done when we know physics is not simulating
|
||||||
private void CreateGeomHull()
|
// Returns 'true' of a mesh was actually rebuild (we could also have one of these specs).
|
||||||
|
private bool CreateGeomHull()
|
||||||
{
|
{
|
||||||
float lod = _pbs.SculptEntry ? _scene.SculptLOD : _scene.MeshLOD;
|
float lod = _pbs.SculptEntry ? _scene.SculptLOD : _scene.MeshLOD;
|
||||||
ulong newHullKey = (ulong)_pbs.GetMeshKey(_size, lod);
|
ulong newHullKey = (ulong)_pbs.GetMeshKey(_size, lod);
|
||||||
// m_log.DebugFormat("{0}: CreateGeomHull: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _hullKey, newHullKey);
|
// m_log.DebugFormat("{0}: CreateGeomHull: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _hullKey, newHullKey);
|
||||||
|
|
||||||
// if the hull hasn't changed, don't rebuild it
|
// if the hull hasn't changed, don't rebuild it
|
||||||
if (newHullKey == _hullKey) return;
|
if (newHullKey == _hullKey) return false;
|
||||||
|
|
||||||
DetailLog("{0},BSPrim.CreateGeomHull,create,oldKey={1},newKey={2}", LocalID, _hullKey, newHullKey);
|
DetailLog("{0},BSPrim.CreateGeomHull,create,oldKey={1},newKey={2}", LocalID, _hullKey, newHullKey);
|
||||||
|
|
||||||
|
@ -1255,7 +1319,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
// meshes are already scaled by the meshmerizer
|
// meshes are already scaled by the meshmerizer
|
||||||
_scale = new OMV.Vector3(1f, 1f, 1f);
|
_scale = new OMV.Vector3(1f, 1f, 1f);
|
||||||
DetailLog("{0},BSPrim.CreateGeomHull,done", LocalID);
|
DetailLog("{0},BSPrim.CreateGeomHull,done", LocalID);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback from convex hull creater with a newly created hull.
|
// Callback from convex hull creater with a newly created hull.
|
||||||
|
@ -1268,20 +1332,12 @@ public sealed class BSPrim : BSPhysObject
|
||||||
|
|
||||||
private void VerifyCorrectPhysicalShape()
|
private void VerifyCorrectPhysicalShape()
|
||||||
{
|
{
|
||||||
if (IsStatic)
|
if (!IsStatic)
|
||||||
{
|
|
||||||
// if static, we don't need a hull so, if there is one, rebuild without it
|
|
||||||
if (_hullKey != 0)
|
|
||||||
{
|
|
||||||
RecreateGeomAndObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// if not static, it will need a hull to efficiently collide with things
|
// if not static, it will need a hull to efficiently collide with things
|
||||||
if (_hullKey == 0)
|
if (_hullKey == 0)
|
||||||
{
|
{
|
||||||
RecreateGeomAndObject();
|
CreateGeomAndObject(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1300,8 +1356,9 @@ public sealed class BSPrim : BSPhysObject
|
||||||
// m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type);
|
// m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type);
|
||||||
bool ret = BulletSimAPI.CreateObject(_scene.WorldID, shape);
|
bool ret = BulletSimAPI.CreateObject(_scene.WorldID, shape);
|
||||||
|
|
||||||
// the CreateObject() may have recreated the rigid body. Make sure we have the latest.
|
// the CreateObject() may have recreated the rigid body. Make sure we have the latest address.
|
||||||
BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID));
|
BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID));
|
||||||
|
BSShape = new BulletShape(BulletSimAPI.GetCollisionShape2(BSBody.Ptr));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1325,15 +1382,20 @@ public sealed class BSPrim : BSPhysObject
|
||||||
shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue;
|
shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Rebuild the geometry and object.
|
// Rebuild the geometry and object.
|
||||||
// This is called when the shape changes so we need to recreate the mesh/hull.
|
// This is called when the shape changes so we need to recreate the mesh/hull.
|
||||||
// No locking here because this is done when the physics engine is not simulating
|
// No locking here because this is done when the physics engine is not simulating
|
||||||
private void RecreateGeomAndObject()
|
private void CreateGeomAndObject(bool forceRebuild)
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("{0}: RecreateGeomAndObject. lID={1}", LogHeader, _localID);
|
// m_log.DebugFormat("{0}: CreateGeomAndObject. lID={1}, force={2}", LogHeader, _localID, forceRebuild);
|
||||||
if (CreateGeom(true))
|
// Create the geometry that will make up the object
|
||||||
|
if (CreateGeom(forceRebuild))
|
||||||
|
{
|
||||||
|
// Create the object and place it into the world
|
||||||
CreateObject();
|
CreateObject();
|
||||||
|
// Make sure the properties are set on the new object
|
||||||
|
UpdatePhysicalParameters();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1414,12 +1476,14 @@ public sealed class BSPrim : BSPhysObject
|
||||||
DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
|
DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
|
||||||
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
|
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
|
||||||
|
|
||||||
|
// BulletSimAPI.DumpRigidBody2(Scene.World.Ptr, BSBody.Ptr);
|
||||||
|
|
||||||
base.RequestPhysicsterseUpdate();
|
base.RequestPhysicsterseUpdate();
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// For debugging, we also report the movement of children
|
// For debugging, we can also report the movement of children
|
||||||
DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
|
DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
|
||||||
LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
|
LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
|
||||||
entprop.Acceleration, entprop.RotationalVelocity);
|
entprop.Acceleration, entprop.RotationalVelocity);
|
||||||
|
@ -1430,15 +1494,15 @@ public sealed class BSPrim : BSPhysObject
|
||||||
// I've collided with something
|
// I've collided with something
|
||||||
// Called at taint time from within the Step() function
|
// Called at taint time from within the Step() function
|
||||||
CollisionEventUpdate collisionCollection;
|
CollisionEventUpdate collisionCollection;
|
||||||
public override void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
|
public override bool Collide(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith);
|
bool ret = false;
|
||||||
|
|
||||||
// The following lines make IsColliding() and IsCollidingGround() work
|
// The following lines make IsColliding() and IsCollidingGround() work
|
||||||
_collidingStep = _scene.SimulationStep;
|
_collidingStep = Scene.SimulationStep;
|
||||||
if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID)
|
if (collidingWith <= Scene.TerrainManager.HighestTerrainID)
|
||||||
{
|
{
|
||||||
_collidingGroundStep = _scene.SimulationStep;
|
_collidingGroundStep = Scene.SimulationStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DetailLog("{0},BSPrim.Collison,call,with={1}", LocalID, collidingWith);
|
// DetailLog("{0},BSPrim.Collison,call,with={1}", LocalID, collidingWith);
|
||||||
|
@ -1446,21 +1510,23 @@ public sealed class BSPrim : BSPhysObject
|
||||||
// prims in the same linkset cannot collide with each other
|
// prims in the same linkset cannot collide with each other
|
||||||
if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
|
if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
|
||||||
{
|
{
|
||||||
return;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if someone has subscribed for collision events....
|
// if someone has subscribed for collision events....
|
||||||
if (SubscribedEvents()) {
|
if (SubscribedEvents()) {
|
||||||
// throttle the collisions to the number of milliseconds specified in the subscription
|
// throttle the collisions to the number of milliseconds specified in the subscription
|
||||||
int nowTime = _scene.SimulationNowTime;
|
int nowTime = Scene.SimulationNowTime;
|
||||||
if (nowTime >= _nextCollisionOkTime) {
|
if (nowTime >= _nextCollisionOkTime) {
|
||||||
_nextCollisionOkTime = nowTime + _subscribedEventsMs;
|
_nextCollisionOkTime = nowTime + _subscribedEventsMs;
|
||||||
|
|
||||||
if (collisionCollection == null)
|
if (collisionCollection == null)
|
||||||
collisionCollection = new CollisionEventUpdate();
|
collisionCollection = new CollisionEventUpdate();
|
||||||
collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
|
collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
|
||||||
|
ret = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The scene is telling us it's time to pass our collected collisions into the simulator
|
// The scene is telling us it's time to pass our collected collisions into the simulator
|
||||||
|
|
|
@ -56,7 +56,6 @@ using OpenMetaverse;
|
||||||
// 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)
|
// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
|
||||||
// Does NeedsMeshing() really need to exclude all the different shapes?
|
|
||||||
// Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet.
|
// 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?
|
||||||
|
@ -79,7 +78,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
private HashSet<BSPhysObject> m_objectsWithCollisions = new HashSet<BSPhysObject>();
|
private HashSet<BSPhysObject> m_objectsWithCollisions = new HashSet<BSPhysObject>();
|
||||||
// Following is a kludge and can be removed when avatar animation updating is
|
// Following is a kludge and can be removed when avatar animation updating is
|
||||||
// moved to a better place.
|
// moved to a better place.
|
||||||
private HashSet<BSCharacter> m_avatarsWithCollisions = new HashSet<BSCharacter>();
|
private HashSet<BSPhysObject> m_avatarsWithCollisions = new HashSet<BSPhysObject>();
|
||||||
|
|
||||||
// List of all the objects that have vehicle properties and should be called
|
// List of all the objects that have vehicle properties and should be called
|
||||||
// to update each physics step.
|
// to update each physics step.
|
||||||
|
@ -111,11 +110,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
private long m_simulationStep = 0;
|
private long m_simulationStep = 0;
|
||||||
public long SimulationStep { get { return m_simulationStep; } }
|
public long SimulationStep { get { return m_simulationStep; } }
|
||||||
|
|
||||||
// The length of the last timestep we were asked to simulate.
|
|
||||||
// This is used by the vehicle code. Since the vehicle code is called
|
|
||||||
// once per simulation step, its constants need to be scaled by this.
|
|
||||||
public float LastSimulatedTimestep { get; private set; }
|
|
||||||
|
|
||||||
// A value of the time now so all the collision and update routines do not have to get their own
|
// A value of the time now so all the collision and update routines do not have to get their own
|
||||||
// Set to 'now' just before all the prims and actors are called for collisions and updates
|
// Set to 'now' just before all the prims and actors are called for collisions and updates
|
||||||
public int SimulationNowTime { get; private set; }
|
public int SimulationNowTime { get; private set; }
|
||||||
|
@ -132,8 +126,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
private EntityProperties[] m_updateArray;
|
private EntityProperties[] m_updateArray;
|
||||||
private GCHandle m_updateArrayPinnedHandle;
|
private GCHandle m_updateArrayPinnedHandle;
|
||||||
|
|
||||||
private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed
|
public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
|
||||||
private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes
|
public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
|
||||||
|
|
||||||
public float PID_D { get; private set; } // derivative
|
public float PID_D { get; private set; } // derivative
|
||||||
public float PID_P { get; private set; } // proportional
|
public float PID_P { get; private set; } // proportional
|
||||||
|
@ -153,6 +147,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
{
|
{
|
||||||
get { return new Vector3(0f, 0f, Params.gravity); }
|
get { return new Vector3(0f, 0f, Params.gravity); }
|
||||||
}
|
}
|
||||||
|
// Just the Z value of the gravity
|
||||||
|
public float DefaultGravityZ
|
||||||
|
{
|
||||||
|
get { return Params.gravity; }
|
||||||
|
}
|
||||||
|
|
||||||
public float MaximumObjectMass { get; private set; }
|
public float MaximumObjectMass { get; private set; }
|
||||||
|
|
||||||
|
@ -171,8 +170,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
callback = c;
|
callback = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private Object _taintLock = new Object(); // lock for using the next object
|
||||||
private List<TaintCallbackEntry> _taintedObjects;
|
private List<TaintCallbackEntry> _taintedObjects;
|
||||||
private Object _taintLock = new Object();
|
|
||||||
|
|
||||||
// A pointer to an instance if this structure is passed to the C++ code
|
// A pointer to an instance if this structure is passed to the C++ code
|
||||||
// Used to pass basic configuration values to the unmanaged code.
|
// Used to pass basic configuration values to the unmanaged code.
|
||||||
|
@ -465,12 +464,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
int collidersCount = 0;
|
int collidersCount = 0;
|
||||||
IntPtr collidersPtr;
|
IntPtr collidersPtr;
|
||||||
|
|
||||||
LastSimulatedTimestep = timeStep;
|
|
||||||
|
|
||||||
// prevent simulation until we've been initialized
|
// prevent simulation until we've been initialized
|
||||||
if (!m_initialized) return 10.0f;
|
if (!m_initialized) return 5.0f;
|
||||||
|
|
||||||
int simulateStartTime = Util.EnvironmentTickCount();
|
|
||||||
|
|
||||||
// update the prim states while we know the physics engine is not busy
|
// update the prim states while we know the physics engine is not busy
|
||||||
int numTaints = _taintedObjects.Count;
|
int numTaints = _taintedObjects.Count;
|
||||||
|
@ -478,6 +473,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
|
|
||||||
// Some of the prims operate with special vehicle properties
|
// Some of the prims operate with special vehicle properties
|
||||||
ProcessVehicles(timeStep);
|
ProcessVehicles(timeStep);
|
||||||
|
numTaints += _taintedObjects.Count;
|
||||||
ProcessTaints(); // the vehicles might have added taints
|
ProcessTaints(); // the vehicles might have added taints
|
||||||
|
|
||||||
// step the physical world one interval
|
// step the physical world one interval
|
||||||
|
@ -506,6 +502,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
// 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();
|
||||||
|
|
||||||
|
// This is a kludge to get avatar movement updates.
|
||||||
|
// ODE sends collisions for avatars even if there are have been no collisions. This updates
|
||||||
|
// avatar animations and stuff.
|
||||||
|
// If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
|
||||||
|
m_objectsWithCollisions = new HashSet<BSPhysObject>(m_avatarsWithCollisions);
|
||||||
|
|
||||||
// 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.
|
||||||
// Collisions must be processed before updates.
|
// Collisions must be processed before updates.
|
||||||
if (collidersCount > 0)
|
if (collidersCount > 0)
|
||||||
|
@ -527,13 +529,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
bsp.SendCollisions();
|
bsp.SendCollisions();
|
||||||
m_objectsWithCollisions.Clear();
|
m_objectsWithCollisions.Clear();
|
||||||
|
|
||||||
// This is a kludge to get avatar movement updated.
|
|
||||||
// ODE sends collisions even if there are none and this is used to update
|
|
||||||
// avatar animations and stuff.
|
|
||||||
foreach (BSPhysObject bpo in m_avatarsWithCollisions)
|
|
||||||
bpo.SendCollisions();
|
|
||||||
// m_avatarsWithCollisions.Clear();
|
|
||||||
|
|
||||||
// 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)
|
||||||
{
|
{
|
||||||
|
@ -544,7 +539,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
if (PhysObjects.TryGetValue(entprop.ID, out pobj))
|
if (PhysObjects.TryGetValue(entprop.ID, out pobj))
|
||||||
{
|
{
|
||||||
pobj.UpdateProperties(entprop);
|
pobj.UpdateProperties(entprop);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -558,18 +552,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is a waste since the outside routine also calcuates the physics simulation
|
// The physics engine returns the number of milliseconds it simulated this call.
|
||||||
// period. TODO: There should be a way of computing physics frames from simulator computation.
|
// These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
|
||||||
// long simulateTotalTime = Util.EnvironmentTickCountSubtract(simulateStartTime);
|
// Since Bullet normally does 5 or 6 substeps, this will normally sum to about 60 FPS.
|
||||||
// return (timeStep * (float)simulateTotalTime);
|
return numSubSteps * m_fixedTimeStep;
|
||||||
|
|
||||||
// TODO: FIX THIS: fps calculation possibly wrong.
|
|
||||||
// This calculation says 1/timeStep is the ideal frame rate. Any time added to
|
|
||||||
// that by the physics simulation gives a slower frame rate.
|
|
||||||
long totalSimulationTime = Util.EnvironmentTickCountSubtract(simulateStartTime);
|
|
||||||
if (totalSimulationTime >= timeStep)
|
|
||||||
return 0;
|
|
||||||
return 1f / (timeStep + totalSimulationTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Something has collided
|
// Something has collided
|
||||||
|
@ -580,28 +566,25 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
return; // don't send collisions to the terrain
|
return; // don't send collisions to the terrain
|
||||||
}
|
}
|
||||||
|
|
||||||
BSPhysObject collider = PhysObjects[localID];
|
BSPhysObject collider;
|
||||||
// TODO: as of this code, terrain was not in the physical object list.
|
if (!PhysObjects.TryGetValue(localID, out collider))
|
||||||
// When BSTerrain is created and it will be in the list, we can remove
|
{
|
||||||
// the possibility that it's not there and just fetch the collidee.
|
// If the object that is colliding cannot be found, just ignore the collision.
|
||||||
BSPhysObject collidee = null;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ActorTypes type = ActorTypes.Prim;
|
// The terrain is not in the physical object list so 'collidee'
|
||||||
if (collidingWith <= TerrainManager.HighestTerrainID)
|
// can be null when Collide() is called.
|
||||||
{
|
BSPhysObject collidee = null;
|
||||||
type = ActorTypes.Ground;
|
PhysObjects.TryGetValue(collidingWith, out collidee);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
collidee = PhysObjects[collidingWith];
|
|
||||||
if (collidee is BSCharacter)
|
|
||||||
type = ActorTypes.Agent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
|
// DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
|
||||||
|
|
||||||
collider.Collide(collidingWith, collidee, type, collidePoint, collideNormal, penetration);
|
if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
|
||||||
m_objectsWithCollisions.Add(collider);
|
{
|
||||||
|
// If a collision was posted, remember to send it to the simulator
|
||||||
|
m_objectsWithCollisions.Add(collider);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -619,9 +602,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
public override void SetWaterLevel(float baseheight)
|
public override void SetWaterLevel(float baseheight)
|
||||||
{
|
{
|
||||||
m_waterLevel = baseheight;
|
m_waterLevel = baseheight;
|
||||||
// TODO: pass to physics engine so things will float?
|
|
||||||
}
|
}
|
||||||
public float GetWaterLevel()
|
// Someday....
|
||||||
|
public float GetWaterLevelAtXYZ(Vector3 loc)
|
||||||
{
|
{
|
||||||
return m_waterLevel;
|
return m_waterLevel;
|
||||||
}
|
}
|
||||||
|
@ -659,121 +642,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
|
|
||||||
public override bool IsThreaded { get { return false; } }
|
public override bool IsThreaded { get { return false; } }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Routine to figure out if we need to mesh this prim with our mesher
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pbs"></param>
|
|
||||||
/// <returns>true if the prim needs meshing</returns>
|
|
||||||
public bool NeedsMeshing(PrimitiveBaseShape pbs)
|
|
||||||
{
|
|
||||||
// most of this is redundant now as the mesher will return null if it cant mesh a prim
|
|
||||||
// but we still need to check for sculptie meshing being enabled so this is the most
|
|
||||||
// convenient place to do it for now...
|
|
||||||
|
|
||||||
// int iPropertiesNotSupportedDefault = 0;
|
|
||||||
|
|
||||||
if (pbs.SculptEntry && !_meshSculptedPrim)
|
|
||||||
{
|
|
||||||
// Render sculpties as boxes
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet
|
|
||||||
// can use an internal representation for the prim
|
|
||||||
if (!_forceSimplePrimMeshing)
|
|
||||||
{
|
|
||||||
if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
|
|
||||||
|| (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
|
|
||||||
&& pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z))
|
|
||||||
{
|
|
||||||
|
|
||||||
if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
|
|
||||||
&& pbs.ProfileHollow == 0
|
|
||||||
&& pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
|
|
||||||
&& pbs.PathBegin == 0 && pbs.PathEnd == 0
|
|
||||||
&& pbs.PathTaperX == 0 && pbs.PathTaperY == 0
|
|
||||||
&& pbs.PathScaleX == 100 && pbs.PathScaleY == 100
|
|
||||||
&& pbs.PathShearX == 0 && pbs.PathShearY == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: verify that the mesher will now do all these shapes
|
|
||||||
if (pbs.ProfileHollow != 0)
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
|
|
||||||
if ((pbs.PathBegin != 0) || pbs.PathEnd != 0)
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
|
|
||||||
if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0))
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
|
|
||||||
if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0)
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
|
|
||||||
if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100))
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
|
|
||||||
if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0))
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
|
|
||||||
if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight)
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
|
|
||||||
if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 && (pbs.Scale.X != pbs.Scale.Y || pbs.Scale.Y != pbs.Scale.Z || pbs.Scale.Z != pbs.Scale.X))
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
|
|
||||||
if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1)
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
|
|
||||||
// test for torus
|
|
||||||
if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square)
|
|
||||||
{
|
|
||||||
if (pbs.PathCurve == (byte)Extrusion.Curve1)
|
|
||||||
{
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
|
|
||||||
{
|
|
||||||
if (pbs.PathCurve == (byte)Extrusion.Straight)
|
|
||||||
{
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
}
|
|
||||||
// ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits
|
|
||||||
else if (pbs.PathCurve == (byte)Extrusion.Curve1)
|
|
||||||
{
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
|
|
||||||
{
|
|
||||||
if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2)
|
|
||||||
{
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
|
|
||||||
{
|
|
||||||
if (pbs.PathCurve == (byte)Extrusion.Straight)
|
|
||||||
{
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
}
|
|
||||||
else if (pbs.PathCurve == (byte)Extrusion.Curve1)
|
|
||||||
{
|
|
||||||
iPropertiesNotSupportedDefault++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (iPropertiesNotSupportedDefault == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -782,7 +650,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
if (!m_initialized) return;
|
if (!m_initialized) return;
|
||||||
|
|
||||||
lock (_taintLock)
|
lock (_taintLock)
|
||||||
|
{
|
||||||
_taintedObjects.Add(new TaintCallbackEntry(ident, callback));
|
_taintedObjects.Add(new TaintCallbackEntry(ident, callback));
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -919,14 +790,14 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
{
|
{
|
||||||
new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
|
new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
|
||||||
ConfigurationParameters.numericTrue,
|
ConfigurationParameters.numericTrue,
|
||||||
(s,cf,p,v) => { s._meshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); },
|
(s,cf,p,v) => { s.ShouldMeshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); },
|
||||||
(s) => { return s.NumericBool(s._meshSculptedPrim); },
|
(s) => { return s.NumericBool(s.ShouldMeshSculptedPrim); },
|
||||||
(s,p,l,v) => { s._meshSculptedPrim = s.BoolNumeric(v); } ),
|
(s,p,l,v) => { s.ShouldMeshSculptedPrim = s.BoolNumeric(v); } ),
|
||||||
new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
|
new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
|
||||||
ConfigurationParameters.numericFalse,
|
ConfigurationParameters.numericFalse,
|
||||||
(s,cf,p,v) => { s._forceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); },
|
(s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); },
|
||||||
(s) => { return s.NumericBool(s._forceSimplePrimMeshing); },
|
(s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); },
|
||||||
(s,p,l,v) => { s._forceSimplePrimMeshing = s.BoolNumeric(v); } ),
|
(s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ),
|
||||||
|
|
||||||
new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
|
new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
|
||||||
8f,
|
8f,
|
||||||
|
@ -1162,8 +1033,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
(s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
|
(s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
|
||||||
(s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; },
|
(s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; },
|
||||||
(s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ),
|
(s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ),
|
||||||
new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=none, 1=all. Default=0",
|
new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
|
||||||
0.0f,
|
0.1f,
|
||||||
(s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
|
(s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
|
||||||
(s) => { return s.m_params[0].linkConstraintCFM; },
|
(s) => { return s.m_params[0].linkConstraintCFM; },
|
||||||
(s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ),
|
(s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ),
|
||||||
|
@ -1172,6 +1043,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
(s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
|
(s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
|
||||||
(s) => { return s.m_params[0].linkConstraintERP; },
|
(s) => { return s.m_params[0].linkConstraintERP; },
|
||||||
(s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ),
|
(s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ),
|
||||||
|
new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
|
||||||
|
40,
|
||||||
|
(s,cf,p,v) => { s.m_params[0].linkConstraintSolverIterations = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return s.m_params[0].linkConstraintSolverIterations; },
|
||||||
|
(s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ),
|
||||||
|
|
||||||
new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)",
|
new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)",
|
||||||
0f,
|
0f,
|
||||||
|
|
|
@ -154,27 +154,31 @@ public class BSTerrainManager
|
||||||
|
|
||||||
// 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) {
|
||||||
if (m_worldOffset != Vector3.Zero && m_parentScene != null)
|
float[] localHeightMap = heightMap;
|
||||||
|
m_physicsScene.TaintedObject("TerrainManager.SetTerrain", delegate()
|
||||||
{
|
{
|
||||||
// If a child of a mega-region, we shouldn't have any terrain allocated for us
|
if (m_worldOffset != Vector3.Zero && m_parentScene != null)
|
||||||
ReleaseGroundPlaneAndTerrain();
|
|
||||||
// If doing the mega-prim stuff and we are the child of the zero region,
|
|
||||||
// the terrain is added to our parent
|
|
||||||
if (m_parentScene is BSScene)
|
|
||||||
{
|
{
|
||||||
DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
|
// If a child of a mega-region, we shouldn't have any terrain allocated for us
|
||||||
BSScene.DetailLogZero, m_worldOffset, m_worldMax);
|
ReleaseGroundPlaneAndTerrain();
|
||||||
((BSScene)m_parentScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID,
|
// If doing the mega-prim stuff and we are the child of the zero region,
|
||||||
heightMap, m_worldOffset, m_worldOffset+DefaultRegionSize, false);
|
// the terrain is added to our parent
|
||||||
|
if (m_parentScene is BSScene)
|
||||||
|
{
|
||||||
|
DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
|
||||||
|
BSScene.DetailLogZero, m_worldOffset, m_worldMax);
|
||||||
|
((BSScene)m_parentScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID,
|
||||||
|
localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
// 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, heightMap, m_worldOffset, m_worldOffset+DefaultRegionSize, false);
|
UpdateOrCreateTerrain(BSScene.TERRAIN_ID, localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If called with no mapInfo for the terrain, this will create a new mapInfo and terrain
|
// If called with no mapInfo for the terrain, this will create a new mapInfo and terrain
|
||||||
|
@ -319,6 +323,8 @@ public class BSTerrainManager
|
||||||
|
|
||||||
// Make sure the new shape is processed.
|
// Make sure the new shape is processed.
|
||||||
BulletSimAPI.Activate2(mapInfo.terrainBody.Ptr, true);
|
BulletSimAPI.Activate2(mapInfo.terrainBody.Ptr, true);
|
||||||
|
|
||||||
|
m_terrainModified = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// There is the option to do the changes now (we're already in 'taint time'), or
|
// There is the option to do the changes now (we're already in 'taint time'), or
|
||||||
|
@ -357,6 +363,8 @@ public class BSTerrainManager
|
||||||
m_heightMaps.Add(terrainRegionBase, mapInfo);
|
m_heightMaps.Add(terrainRegionBase, mapInfo);
|
||||||
// Build the terrain
|
// Build the terrain
|
||||||
UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true);
|
UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true);
|
||||||
|
|
||||||
|
m_terrainModified = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time.
|
// If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time.
|
||||||
|
@ -383,7 +391,7 @@ public 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;
|
||||||
public float GetTerrainHeightAtXY(float tX, float tY)
|
private float GetTerrainHeightAtXY(float tX, float tY)
|
||||||
{
|
{
|
||||||
// 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.
|
||||||
|
@ -403,11 +411,18 @@ public class BSTerrainManager
|
||||||
{
|
{
|
||||||
float regionX = tX - offsetX;
|
float regionX = tX - offsetX;
|
||||||
float regionY = tY - offsetY;
|
float regionY = tY - offsetY;
|
||||||
if (regionX >= mapInfo.sizeX || regionX < 0f) regionX = 0;
|
|
||||||
if (regionY >= mapInfo.sizeY || regionY < 0f) regionY = 0;
|
|
||||||
int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX;
|
int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX;
|
||||||
ret = mapInfo.heightMap[mapIndex];
|
try
|
||||||
m_terrainModified = false;
|
{
|
||||||
|
ret = mapInfo.heightMap[mapIndex];
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Sometimes they give us wonky values of X and Y. Give a warning and return something.
|
||||||
|
m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, x={2}, y={3}",
|
||||||
|
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}",
|
// 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);
|
// BSScene.DetailLogZero, offsetX, offsetY, mapInfo.sizeX, mapInfo.sizeY, regionX, regionY, mapIndex, ret);
|
||||||
}
|
}
|
||||||
|
@ -416,6 +431,7 @@ public class BSTerrainManager
|
||||||
m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
|
m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
|
||||||
LogHeader, m_physicsScene.RegionName, tX, tY);
|
LogHeader, m_physicsScene.RegionName, tX, tY);
|
||||||
}
|
}
|
||||||
|
m_terrainModified = false;
|
||||||
lastHeight = ret;
|
lastHeight = ret;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,6 +213,7 @@ public struct ConfigurationParameters
|
||||||
public float linkConstraintTransMotorMaxForce;
|
public float linkConstraintTransMotorMaxForce;
|
||||||
public float linkConstraintERP;
|
public float linkConstraintERP;
|
||||||
public float linkConstraintCFM;
|
public float linkConstraintCFM;
|
||||||
|
public float linkConstraintSolverIterations;
|
||||||
|
|
||||||
public const float numericTrue = 1f;
|
public const float numericTrue = 1f;
|
||||||
public const float numericFalse = 0f;
|
public const float numericFalse = 0f;
|
||||||
|
@ -395,23 +396,6 @@ public static extern bool DestroyMesh(uint worldID, System.UInt64 meshKey);
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern bool CreateObject(uint worldID, ShapeData shapeData);
|
public static extern bool CreateObject(uint worldID, ShapeData shapeData);
|
||||||
|
|
||||||
/* Remove old functionality
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
|
||||||
public static extern void CreateLinkset(uint worldID, int objectCount, ShapeData[] shapeDatas);
|
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
|
||||||
public static extern void AddConstraint(uint worldID, uint id1, uint id2,
|
|
||||||
Vector3 frame1, Quaternion frame1rot,
|
|
||||||
Vector3 frame2, Quaternion frame2rot,
|
|
||||||
Vector3 lowLinear, Vector3 hiLinear, Vector3 lowAngular, Vector3 hiAngular);
|
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
|
||||||
public static extern bool RemoveConstraintByID(uint worldID, uint id1);
|
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
|
||||||
public static extern bool RemoveConstraint(uint worldID, uint id1, uint id2);
|
|
||||||
*/
|
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern Vector3 GetObjectPosition(uint WorldID, uint id);
|
public static extern Vector3 GetObjectPosition(uint WorldID, uint id);
|
||||||
|
|
||||||
|
@ -538,15 +522,27 @@ public static extern IntPtr BuildHullShape2(IntPtr world, IntPtr meshShape);
|
||||||
public static extern IntPtr BuildNativeShape2(IntPtr world,
|
public static extern IntPtr BuildNativeShape2(IntPtr world,
|
||||||
float shapeType, float collisionMargin, Vector3 scale);
|
float shapeType, float collisionMargin, Vector3 scale);
|
||||||
|
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern bool IsNativeShape2(IntPtr shape);
|
||||||
|
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern IntPtr CreateCompoundShape2(IntPtr sim);
|
||||||
|
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern void AddChildToCompoundShape2(IntPtr cShape, IntPtr addShape, Vector3 pos, Quaternion rot);
|
||||||
|
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern void RemoveChildFromCompoundShape2(IntPtr cShape, IntPtr removeShape);
|
||||||
|
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern IntPtr CreateBodyFromShapeAndInfo2(IntPtr sim, IntPtr shape, IntPtr constructionInfo);
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern bool DeleteCollisionShape2(IntPtr world, IntPtr shape);
|
public static extern bool DeleteCollisionShape2(IntPtr world, IntPtr shape);
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, Vector3 pos, Quaternion rot);
|
public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, Vector3 pos, Quaternion rot);
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
|
||||||
public static extern IntPtr CreateBodyFromShapeAndInfo2(IntPtr sim, IntPtr shape, IntPtr constructionInfo);
|
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern IntPtr CreateBodyWithDefaultMotionState2(IntPtr shape, Vector3 pos, Quaternion rot);
|
public static extern IntPtr CreateBodyWithDefaultMotionState2(IntPtr shape, Vector3 pos, Quaternion rot);
|
||||||
|
|
||||||
|
@ -561,9 +557,6 @@ public static extern void DestroyObject2(IntPtr sim, IntPtr obj);
|
||||||
|
|
||||||
// =====================================================================================
|
// =====================================================================================
|
||||||
// Terrain creation and helper routines
|
// Terrain creation and helper routines
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
|
||||||
public static extern void DumpMapInfo(IntPtr sim, IntPtr manInfo);
|
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern IntPtr CreateHeightMapInfo2(IntPtr sim, uint id, Vector3 minCoords, Vector3 maxCoords,
|
public static extern IntPtr CreateHeightMapInfo2(IntPtr sim, uint id, Vector3 minCoords, Vector3 maxCoords,
|
||||||
[MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin);
|
[MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin);
|
||||||
|
@ -1010,7 +1003,7 @@ public static extern void SetLocalScaling2(IntPtr shape, Vector3 scale);
|
||||||
public static extern Vector3 GetLocalScaling2(IntPtr shape);
|
public static extern Vector3 GetLocalScaling2(IntPtr shape);
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern void CalculateLocalInertia2(IntPtr shape, float mass, Vector3 inertia);
|
public static extern Vector3 CalculateLocalInertia2(IntPtr shape, float mass);
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern int GetShapeType2(IntPtr shape);
|
public static extern int GetShapeType2(IntPtr shape);
|
||||||
|
@ -1026,6 +1019,12 @@ public static extern void SetCollisionFilterMask(IntPtr shape, uint filter, uint
|
||||||
|
|
||||||
// =====================================================================================
|
// =====================================================================================
|
||||||
// Debugging
|
// Debugging
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject);
|
||||||
|
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo);
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern void DumpPhysicsStatistics2(IntPtr sim);
|
public static extern void DumpPhysicsStatistics2(IntPtr sim);
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,8 @@ namespace OpenSim.Server.Handlers.Avatar
|
||||||
if (!UUID.TryParse(request["UserID"].ToString(), out user))
|
if (!UUID.TryParse(request["UserID"].ToString(), out user))
|
||||||
return FailureResult();
|
return FailureResult();
|
||||||
|
|
||||||
|
RemoveRequestParamsNotForStorage(request);
|
||||||
|
|
||||||
AvatarData avatar = new AvatarData(request);
|
AvatarData avatar = new AvatarData(request);
|
||||||
if (m_AvatarService.SetAvatar(user, avatar))
|
if (m_AvatarService.SetAvatar(user, avatar))
|
||||||
return SuccessResult();
|
return SuccessResult();
|
||||||
|
@ -153,11 +155,25 @@ namespace OpenSim.Server.Handlers.Avatar
|
||||||
if (!UUID.TryParse(request["UserID"].ToString(), out user))
|
if (!UUID.TryParse(request["UserID"].ToString(), out user))
|
||||||
return FailureResult();
|
return FailureResult();
|
||||||
|
|
||||||
|
RemoveRequestParamsNotForStorage(request);
|
||||||
|
|
||||||
if (m_AvatarService.ResetAvatar(user))
|
if (m_AvatarService.ResetAvatar(user))
|
||||||
return SuccessResult();
|
return SuccessResult();
|
||||||
|
|
||||||
return FailureResult();
|
return FailureResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove parameters that were used to invoke the method and should not in themselves be persisted.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name='request'></param>
|
||||||
|
private void RemoveRequestParamsNotForStorage(Dictionary<string, object> request)
|
||||||
|
{
|
||||||
|
request.Remove("VERSIONMAX");
|
||||||
|
request.Remove("VERSIONMIN");
|
||||||
|
request.Remove("METHOD");
|
||||||
|
request.Remove("UserID");
|
||||||
|
}
|
||||||
|
|
||||||
byte[] SetItems(Dictionary<string, object> request)
|
byte[] SetItems(Dictionary<string, object> request)
|
||||||
{
|
{
|
||||||
|
@ -173,6 +189,8 @@ namespace OpenSim.Server.Handlers.Avatar
|
||||||
if (!(request["Names"] is List<string> || request["Values"] is List<string>))
|
if (!(request["Names"] is List<string> || request["Values"] is List<string>))
|
||||||
return FailureResult();
|
return FailureResult();
|
||||||
|
|
||||||
|
RemoveRequestParamsNotForStorage(request);
|
||||||
|
|
||||||
List<string> _names = (List<string>)request["Names"];
|
List<string> _names = (List<string>)request["Names"];
|
||||||
names = _names.ToArray();
|
names = _names.ToArray();
|
||||||
List<string> _values = (List<string>)request["Values"];
|
List<string> _values = (List<string>)request["Values"];
|
||||||
|
|
|
@ -84,7 +84,7 @@ namespace OpenSim.Services.AssetService
|
||||||
|
|
||||||
m_Database = LoadPlugin<IAssetDataPlugin>(dllName);
|
m_Database = LoadPlugin<IAssetDataPlugin>(dllName);
|
||||||
if (m_Database == null)
|
if (m_Database == null)
|
||||||
throw new Exception("Could not find a storage interface in the given module");
|
throw new Exception(string.Format("Could not find a storage interface in the module {0}", dllName));
|
||||||
|
|
||||||
m_Database.Initialise(connString);
|
m_Database.Initialise(connString);
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ namespace OpenSim.Services.AssetService
|
||||||
m_AssetLoader = LoadPlugin<IAssetLoader>(loaderName);
|
m_AssetLoader = LoadPlugin<IAssetLoader>(loaderName);
|
||||||
|
|
||||||
if (m_AssetLoader == null)
|
if (m_AssetLoader == null)
|
||||||
throw new Exception("Asset loader could not be loaded");
|
throw new Exception(string.Format("Asset loader could not be loaded from {0}", loaderName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,10 +54,3 @@
|
||||||
|
|
||||||
; Warning level for cache directory size
|
; Warning level for cache directory size
|
||||||
;CacheWarnAt = 30000
|
;CacheWarnAt = 30000
|
||||||
|
|
||||||
; Perform a deep scan of all assets within all regions, looking for all assets
|
|
||||||
; present or referenced. Mark all assets found that are already present in the
|
|
||||||
; cache, and request all assets that are found that are not already cached (this
|
|
||||||
; will cause those assets to be cached)
|
|
||||||
;
|
|
||||||
DeepScanBeforePurge = true
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -2970,6 +2970,7 @@
|
||||||
<Match path="World/Media/Moap/Tests" pattern="*.cs" recurse="true"/>
|
<Match path="World/Media/Moap/Tests" pattern="*.cs" recurse="true"/>
|
||||||
<Match path="World/Serialiser/Tests" pattern="*.cs" recurse="true"/>
|
<Match path="World/Serialiser/Tests" pattern="*.cs" recurse="true"/>
|
||||||
<Match path="World/Terrain/Tests" pattern="*.cs" recurse="true"/>
|
<Match path="World/Terrain/Tests" pattern="*.cs" recurse="true"/>
|
||||||
|
<Match path="ServiceConnectorsOut/Asset/Tests" pattern="*.cs" recurse="true"/>
|
||||||
<Match path="ServiceConnectorsOut/Grid/Tests" pattern="*.cs" recurse="true"/>
|
<Match path="ServiceConnectorsOut/Grid/Tests" pattern="*.cs" recurse="true"/>
|
||||||
<Match path="ServiceConnectorsOut/Presence/Tests" pattern="*.cs" recurse="true"/>
|
<Match path="ServiceConnectorsOut/Presence/Tests" pattern="*.cs" recurse="true"/>
|
||||||
</Files>
|
</Files>
|
||||||
|
|
Loading…
Reference in New Issue