aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region')
-rw-r--r--OpenSim/Region/Application/OpenSim.cs3
-rw-r--r--OpenSim/Region/ClientStack/RegionApplicationBase.cs2
-rw-r--r--OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs37
-rw-r--r--OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs7
-rw-r--r--OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/Tests/AssetConnectorTests.cs136
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs365
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs176
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs634
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs153
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs438
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs5
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs6
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/DearchiveScenesGroup.cs232
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs397
-rw-r--r--OpenSim/Region/Framework/Interfaces/IEstateModule.cs5
-rw-r--r--OpenSim/Region/Framework/Scenes/EventManager.cs6
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneManager.cs6
-rw-r--r--OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs2
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs21
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs16
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs225
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs61
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs2
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs250
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs230
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs60
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs45
27 files changed, 2289 insertions, 1231 deletions
diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs
index ed339fd..c3c612f 100644
--- a/OpenSim/Region/Application/OpenSim.cs
+++ b/OpenSim/Region/Application/OpenSim.cs
@@ -292,7 +292,7 @@ namespace OpenSim
292 292
293 m_console.Commands.AddCommand("Archiving", false, "save oar", 293 m_console.Commands.AddCommand("Archiving", false, "save oar",
294 //"save oar [-v|--version=<N>] [-p|--profile=<url>] [<OAR path>]", 294 //"save oar [-v|--version=<N>] [-p|--profile=<url>] [<OAR path>]",
295 "save oar [-h|--home=<url>] [--noassets] [--publish] [--perm=<permissions>] [<OAR path>]", 295 "save oar [-h|--home=<url>] [--noassets] [--publish] [--perm=<permissions>] [--all] [<OAR path>]",
296 "Save a region's data to an OAR archive.", 296 "Save a region's data to an OAR archive.",
297// "-v|--version=<N> generates scene objects as per older versions of the serialization (e.g. -v=0)" + Environment.NewLine 297// "-v|--version=<N> generates scene objects as per older versions of the serialization (e.g. -v=0)" + Environment.NewLine
298 "-h|--home=<url> adds the url of the profile service to the saved user information.\n" 298 "-h|--home=<url> adds the url of the profile service to the saved user information.\n"
@@ -302,6 +302,7 @@ namespace OpenSim
302 + " this is useful if you're making oars generally available that might be reloaded to the same grid from which you published\n" 302 + " this is useful if you're making oars generally available that might be reloaded to the same grid from which you published\n"
303 + "--perm=<permissions> stops objects with insufficient permissions from being saved to the OAR.\n" 303 + "--perm=<permissions> stops objects with insufficient permissions from being saved to the OAR.\n"
304 + " <permissions> can contain one or more of these characters: \"C\" = Copy, \"T\" = Transfer\n" 304 + " <permissions> can contain one or more of these characters: \"C\" = Copy, \"T\" = Transfer\n"
305 + "--all saves all the regions in the simulator, instead of just the current region.\n"
305 + "The OAR path must be a filesystem path." 306 + "The OAR path must be a filesystem path."
306 + " If this is not given then the oar is saved to region.oar in the current directory.", 307 + " If this is not given then the oar is saved to region.oar in the current directory.",
307 SaveOar); 308 SaveOar);
diff --git a/OpenSim/Region/ClientStack/RegionApplicationBase.cs b/OpenSim/Region/ClientStack/RegionApplicationBase.cs
index 4672f8a..853b72d 100644
--- a/OpenSim/Region/ClientStack/RegionApplicationBase.cs
+++ b/OpenSim/Region/ClientStack/RegionApplicationBase.cs
@@ -76,7 +76,7 @@ namespace OpenSim.Region.ClientStack
76 76
77 protected override void StartupSpecific() 77 protected override void StartupSpecific()
78 { 78 {
79 SceneManager = new SceneManager(); 79 SceneManager = SceneManager.Instance;
80 m_clientStackManager = CreateClientStackManager(); 80 m_clientStackManager = CreateClientStackManager();
81 81
82 Initialize(); 82 Initialize();
diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
index 5d8a278..8aa173a 100644
--- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
+++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
@@ -107,8 +107,6 @@ namespace OpenSim.Region.CoreModules.Asset
107 private IAssetService m_AssetService; 107 private IAssetService m_AssetService;
108 private List<Scene> m_Scenes = new List<Scene>(); 108 private List<Scene> m_Scenes = new List<Scene>();
109 109
110 private bool m_DeepScanBeforePurge;
111
112 public FlotsamAssetCache() 110 public FlotsamAssetCache()
113 { 111 {
114 m_InvalidChars.AddRange(Path.GetInvalidPathChars()); 112 m_InvalidChars.AddRange(Path.GetInvalidPathChars());
@@ -170,8 +168,6 @@ namespace OpenSim.Region.CoreModules.Asset
170 m_CacheDirectoryTierLen = assetConfig.GetInt("CacheDirectoryTierLength", m_CacheDirectoryTierLen); 168 m_CacheDirectoryTierLen = assetConfig.GetInt("CacheDirectoryTierLength", m_CacheDirectoryTierLen);
171 169
172 m_CacheWarnAt = assetConfig.GetInt("CacheWarnAt", m_CacheWarnAt); 170 m_CacheWarnAt = assetConfig.GetInt("CacheWarnAt", m_CacheWarnAt);
173
174 m_DeepScanBeforePurge = assetConfig.GetBoolean("DeepScanBeforePurge", m_DeepScanBeforePurge);
175 } 171 }
176 172
177 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory); 173 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory);
@@ -506,13 +502,10 @@ namespace OpenSim.Region.CoreModules.Asset
506 // Purge all files last accessed prior to this point 502 // Purge all files last accessed prior to this point
507 DateTime purgeLine = DateTime.Now - m_FileExpiration; 503 DateTime purgeLine = DateTime.Now - m_FileExpiration;
508 504
509 // An optional deep scan at this point will ensure assets present in scenes, 505 // An asset cache may contain local non-temporary assets that are not in the asset service. Therefore,
510 // or referenced by objects in the scene, but not recently accessed 506 // before cleaning up expired files we must scan the objects in the scene to make sure that we retain
511 // are not purged. 507 // such local assets if they have not been recently accessed.
512 if (m_DeepScanBeforePurge) 508 TouchAllSceneAssets(false);
513 {
514 CacheScenes();
515 }
516 509
517 foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) 510 foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
518 { 511 {
@@ -705,11 +698,14 @@ namespace OpenSim.Region.CoreModules.Asset
705 698
706 /// <summary> 699 /// <summary>
707 /// Iterates through all Scenes, doing a deep scan through assets 700 /// Iterates through all Scenes, doing a deep scan through assets
708 /// to cache all assets present in the scene or referenced by assets 701 /// to update the access time of all assets present in the scene or referenced by assets
709 /// in the scene 702 /// in the scene.
710 /// </summary> 703 /// </summary>
711 /// <returns></returns> 704 /// <param name="storeUncached">
712 private int CacheScenes() 705 /// If true, then assets scanned which are not found in cache are added to the cache.
706 /// </param>
707 /// <returns>Number of distinct asset references found in the scene.</returns>
708 private int TouchAllSceneAssets(bool storeUncached)
713 { 709 {
714 UuidGatherer gatherer = new UuidGatherer(m_AssetService); 710 UuidGatherer gatherer = new UuidGatherer(m_AssetService);
715 711
@@ -732,7 +728,7 @@ namespace OpenSim.Region.CoreModules.Asset
732 { 728 {
733 File.SetLastAccessTime(filename, DateTime.Now); 729 File.SetLastAccessTime(filename, DateTime.Now);
734 } 730 }
735 else 731 else if (storeUncached)
736 { 732 {
737 m_AssetService.Get(assetID.ToString()); 733 m_AssetService.Get(assetID.ToString());
738 } 734 }
@@ -860,13 +856,14 @@ namespace OpenSim.Region.CoreModules.Asset
860 856
861 break; 857 break;
862 858
863
864 case "assets": 859 case "assets":
865 m_log.Info("[FLOTSAM ASSET CACHE]: Caching all assets, in all scenes."); 860 m_log.Info("[FLOTSAM ASSET CACHE]: Ensuring assets are cached for all scenes.");
866 861
867 Util.FireAndForget(delegate { 862 Util.FireAndForget(delegate {
868 int assetsCached = CacheScenes(); 863 int assetReferenceTotal = TouchAllSceneAssets(true);
869 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Completed Scene Caching, {0} assets found.", assetsCached); 864 m_log.InfoFormat(
865 "[FLOTSAM ASSET CACHE]: Completed check with {0} assets.",
866 assetReferenceTotal);
870 }); 867 });
871 868
872 break; 869 break;
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs
index c78915f..449c1f1 100644
--- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs
@@ -204,8 +204,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
204 public byte[] GetData(string id) 204 public byte[] GetData(string id)
205 { 205 {
206// m_log.DebugFormat("[LOCAL ASSET SERVICES CONNECTOR]: Requesting data for asset {0}", id); 206// m_log.DebugFormat("[LOCAL ASSET SERVICES CONNECTOR]: Requesting data for asset {0}", id);
207 207
208 AssetBase asset = m_Cache.Get(id); 208 AssetBase asset = null;
209
210 if (m_Cache != null)
211 asset = m_Cache.Get(id);
209 212
210 if (asset != null) 213 if (asset != null)
211 return asset.Data; 214 return asset.Data;
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/Tests/AssetConnectorTests.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/Tests/AssetConnectorTests.cs
new file mode 100644
index 0000000..1982473
--- /dev/null
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/Tests/AssetConnectorTests.cs
@@ -0,0 +1,136 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Reflection;
32using System.Threading;
33using log4net.Config;
34using Nini.Config;
35using NUnit.Framework;
36using OpenMetaverse;
37using OpenSim.Framework;
38using OpenSim.Region.Framework.Scenes;
39using OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset;
40using OpenSim.Tests.Common;
41
42namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset.Tests
43{
44 [TestFixture]
45 public class AssetConnectorsTests : OpenSimTestCase
46 {
47 [Test]
48 public void TestAddAsset()
49 {
50 TestHelpers.InMethod();
51// TestHelpers.EnableLogging();
52
53 IConfigSource config = new IniConfigSource();
54 config.AddConfig("Modules");
55 config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
56 config.AddConfig("AssetService");
57 config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
58 config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
59
60 LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
61 lasc.Initialise(config);
62
63 AssetBase a1 = AssetHelpers.CreateNotecardAsset();
64 lasc.Store(a1);
65
66 AssetBase retreivedA1 = lasc.Get(a1.ID);
67 Assert.That(retreivedA1.ID, Is.EqualTo(a1.ID));
68 Assert.That(retreivedA1.Metadata.ID, Is.EqualTo(a1.Metadata.ID));
69 Assert.That(retreivedA1.Data.Length, Is.EqualTo(a1.Data.Length));
70
71 AssetMetadata retrievedA1Metadata = lasc.GetMetadata(a1.ID);
72 Assert.That(retrievedA1Metadata.ID, Is.EqualTo(a1.ID));
73
74 byte[] retrievedA1Data = lasc.GetData(a1.ID);
75 Assert.That(retrievedA1Data.Length, Is.EqualTo(a1.Data.Length));
76
77 // TODO: Add cache and check that this does receive a copy of the asset
78 }
79
80 [Test]
81 public void TestAddTemporaryAsset()
82 {
83 TestHelpers.InMethod();
84// TestHelpers.EnableLogging();
85
86 IConfigSource config = new IniConfigSource();
87 config.AddConfig("Modules");
88 config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
89 config.AddConfig("AssetService");
90 config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
91 config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
92
93 LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
94 lasc.Initialise(config);
95
96 AssetBase a1 = AssetHelpers.CreateNotecardAsset();
97 a1.Temporary = true;
98
99 lasc.Store(a1);
100
101 Assert.That(lasc.Get(a1.ID), Is.Null);
102 Assert.That(lasc.GetData(a1.ID), Is.Null);
103 Assert.That(lasc.GetMetadata(a1.ID), Is.Null);
104
105 // TODO: Add cache and check that this does receive a copy of the asset
106 }
107
108 [Test]
109 public void TestAddLocalAsset()
110 {
111 TestHelpers.InMethod();
112// TestHelpers.EnableLogging();
113
114 IConfigSource config = new IniConfigSource();
115 config.AddConfig("Modules");
116 config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
117 config.AddConfig("AssetService");
118 config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
119 config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
120
121 LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
122 lasc.Initialise(config);
123
124 AssetBase a1 = AssetHelpers.CreateNotecardAsset();
125 a1.Local = true;
126
127 lasc.Store(a1);
128
129 Assert.That(lasc.Get(a1.ID), Is.Null);
130 Assert.That(lasc.GetData(a1.ID), Is.Null);
131 Assert.That(lasc.GetMetadata(a1.ID), Is.Null);
132
133 // TODO: Add cache and check that this does receive a copy of the asset
134 }
135 }
136} \ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
index 433166d..a6923ef 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
@@ -43,6 +43,7 @@ using OpenSim.Region.Framework.Interfaces;
43using OpenSim.Region.Framework.Scenes; 43using OpenSim.Region.Framework.Scenes;
44using OpenSim.Region.Framework.Scenes.Serialization; 44using OpenSim.Region.Framework.Scenes.Serialization;
45using OpenSim.Services.Interfaces; 45using OpenSim.Services.Interfaces;
46using System.Threading;
46 47
47namespace OpenSim.Region.CoreModules.World.Archiver 48namespace OpenSim.Region.CoreModules.World.Archiver
48{ 49{
@@ -52,7 +53,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver
52 public class ArchiveReadRequest 53 public class ArchiveReadRequest
53 { 54 {
54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 55 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
56
57 /// <summary>
58 /// Contains data used while dearchiving a single scene.
59 /// </summary>
60 private class DearchiveContext
61 {
62 public Scene Scene { get; set; }
63
64 public List<string> SerialisedSceneObjects { get; set; }
65
66 public List<string> SerialisedParcels { get; set; }
67
68 public List<SceneObjectGroup> SceneObjects { get; set; }
69
70 public DearchiveContext(Scene scene)
71 {
72 Scene = scene;
73 SerialisedSceneObjects = new List<string>();
74 SerialisedParcels = new List<string>();
75 SceneObjects = new List<SceneObjectGroup>();
76 }
77 }
55 78
79
56 /// <summary> 80 /// <summary>
57 /// The maximum major version of OAR that we can read. Minor versions shouldn't need a max number since version 81 /// The maximum major version of OAR that we can read. Minor versions shouldn't need a max number since version
58 /// bumps here should be compatible. 82 /// bumps here should be compatible.
@@ -62,9 +86,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver
62 /// <summary> 86 /// <summary>
63 /// Has the control file been loaded for this archive? 87 /// Has the control file been loaded for this archive?
64 /// </summary> 88 /// </summary>
65 public bool ControlFileLoaded { get; private set; } 89 public bool ControlFileLoaded { get; private set; }
66 90
67 protected Scene m_scene; 91 protected string m_loadPath;
92 protected Scene m_rootScene;
68 protected Stream m_loadStream; 93 protected Stream m_loadStream;
69 protected Guid m_requestId; 94 protected Guid m_requestId;
70 protected string m_errorMessage; 95 protected string m_errorMessage;
@@ -91,7 +116,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
91 { 116 {
92 if (m_UserMan == null) 117 if (m_UserMan == null)
93 { 118 {
94 m_UserMan = m_scene.RequestModuleInterface<IUserManagement>(); 119 m_UserMan = m_rootScene.RequestModuleInterface<IUserManagement>();
95 } 120 }
96 return m_UserMan; 121 return m_UserMan;
97 } 122 }
@@ -104,10 +129,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
104 129
105 private IGroupsModule m_groupsModule; 130 private IGroupsModule m_groupsModule;
106 131
132 private IAssetService m_assetService = null;
133
134
107 public ArchiveReadRequest(Scene scene, string loadPath, bool merge, bool skipAssets, Guid requestId) 135 public ArchiveReadRequest(Scene scene, string loadPath, bool merge, bool skipAssets, Guid requestId)
108 { 136 {
109 m_scene = scene; 137 m_rootScene = scene;
110 138
139 m_loadPath = loadPath;
111 try 140 try
112 { 141 {
113 m_loadStream = new GZipStream(ArchiveHelpers.GetStream(loadPath), CompressionMode.Decompress); 142 m_loadStream = new GZipStream(ArchiveHelpers.GetStream(loadPath), CompressionMode.Decompress);
@@ -128,12 +157,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
128 // Zero can never be a valid user id 157 // Zero can never be a valid user id
129 m_validUserUuids[UUID.Zero] = false; 158 m_validUserUuids[UUID.Zero] = false;
130 159
131 m_groupsModule = m_scene.RequestModuleInterface<IGroupsModule>(); 160 m_groupsModule = m_rootScene.RequestModuleInterface<IGroupsModule>();
161 m_assetService = m_rootScene.AssetService;
132 } 162 }
133 163
134 public ArchiveReadRequest(Scene scene, Stream loadStream, bool merge, bool skipAssets, Guid requestId) 164 public ArchiveReadRequest(Scene scene, Stream loadStream, bool merge, bool skipAssets, Guid requestId)
135 { 165 {
136 m_scene = scene; 166 m_rootScene = scene;
167 m_loadPath = null;
137 m_loadStream = loadStream; 168 m_loadStream = loadStream;
138 m_merge = merge; 169 m_merge = merge;
139 m_skipAssets = skipAssets; 170 m_skipAssets = skipAssets;
@@ -142,7 +173,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
142 // Zero can never be a valid user id 173 // Zero can never be a valid user id
143 m_validUserUuids[UUID.Zero] = false; 174 m_validUserUuids[UUID.Zero] = false;
144 175
145 m_groupsModule = m_scene.RequestModuleInterface<IGroupsModule>(); 176 m_groupsModule = m_rootScene.RequestModuleInterface<IGroupsModule>();
177 m_assetService = m_rootScene.AssetService;
146 } 178 }
147 179
148 /// <summary> 180 /// <summary>
@@ -150,25 +182,25 @@ namespace OpenSim.Region.CoreModules.World.Archiver
150 /// </summary> 182 /// </summary>
151 public void DearchiveRegion() 183 public void DearchiveRegion()
152 { 184 {
153 // The same code can handle dearchiving 0.1 and 0.2 OpenSim Archive versions
154 DearchiveRegion0DotStar();
155 }
156
157 private void DearchiveRegion0DotStar()
158 {
159 int successfulAssetRestores = 0; 185 int successfulAssetRestores = 0;
160 int failedAssetRestores = 0; 186 int failedAssetRestores = 0;
161 List<string> serialisedSceneObjects = new List<string>();
162 List<string> serialisedParcels = new List<string>();
163 string filePath = "NONE";
164 187
165 TarArchiveReader archive = new TarArchiveReader(m_loadStream); 188 DearchiveScenesInfo dearchivedScenes;
189
190 // We dearchive all the scenes at once, because the files in the TAR archive might be mixed.
191 // Therefore, we have to keep track of the dearchive context of all the scenes.
192 Dictionary<UUID, DearchiveContext> sceneContexts = new Dictionary<UUID, DearchiveContext>();
193
194 string fullPath = "NONE";
195 TarArchiveReader archive = null;
166 byte[] data; 196 byte[] data;
167 TarArchiveReader.TarEntryType entryType; 197 TarArchiveReader.TarEntryType entryType;
168 198
169 try 199 try
170 { 200 {
171 while ((data = archive.ReadEntry(out filePath, out entryType)) != null) 201 FindAndLoadControlFile(out archive, out dearchivedScenes);
202
203 while ((data = archive.ReadEntry(out fullPath, out entryType)) != null)
172 { 204 {
173 //m_log.DebugFormat( 205 //m_log.DebugFormat(
174 // "[ARCHIVER]: Successfully read {0} ({1} bytes)", filePath, data.Length); 206 // "[ARCHIVER]: Successfully read {0} ({1} bytes)", filePath, data.Length);
@@ -176,9 +208,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver
176 if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType) 208 if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType)
177 continue; 209 continue;
178 210
211
212 // Find the scene that this file belongs to
213
214 Scene scene;
215 string filePath;
216 if (!dearchivedScenes.GetRegionFromPath(fullPath, out scene, out filePath))
217 continue; // this file belongs to a region that we're not loading
218
219 DearchiveContext sceneContext = null;
220 if (scene != null)
221 {
222 if (!sceneContexts.TryGetValue(scene.RegionInfo.RegionID, out sceneContext))
223 {
224 sceneContext = new DearchiveContext(scene);
225 sceneContexts.Add(scene.RegionInfo.RegionID, sceneContext);
226 }
227 }
228
229
230 // Process the file
231
179 if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH)) 232 if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
180 { 233 {
181 serialisedSceneObjects.Add(Encoding.UTF8.GetString(data)); 234 sceneContext.SerialisedSceneObjects.Add(Encoding.UTF8.GetString(data));
182 } 235 }
183 else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH) && !m_skipAssets) 236 else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH) && !m_skipAssets)
184 { 237 {
@@ -192,19 +245,19 @@ namespace OpenSim.Region.CoreModules.World.Archiver
192 } 245 }
193 else if (!m_merge && filePath.StartsWith(ArchiveConstants.TERRAINS_PATH)) 246 else if (!m_merge && filePath.StartsWith(ArchiveConstants.TERRAINS_PATH))
194 { 247 {
195 LoadTerrain(filePath, data); 248 LoadTerrain(scene, filePath, data);
196 } 249 }
197 else if (!m_merge && filePath.StartsWith(ArchiveConstants.SETTINGS_PATH)) 250 else if (!m_merge && filePath.StartsWith(ArchiveConstants.SETTINGS_PATH))
198 { 251 {
199 LoadRegionSettings(filePath, data); 252 LoadRegionSettings(scene, filePath, data, dearchivedScenes);
200 } 253 }
201 else if (!m_merge && filePath.StartsWith(ArchiveConstants.LANDDATA_PATH)) 254 else if (!m_merge && filePath.StartsWith(ArchiveConstants.LANDDATA_PATH))
202 { 255 {
203 serialisedParcels.Add(Encoding.UTF8.GetString(data)); 256 sceneContext.SerialisedParcels.Add(Encoding.UTF8.GetString(data));
204 } 257 }
205 else if (filePath == ArchiveConstants.CONTROL_FILE_PATH) 258 else if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
206 { 259 {
207 LoadControlFile(filePath, data); 260 // Ignore, because we already read the control file
208 } 261 }
209 } 262 }
210 263
@@ -212,15 +265,16 @@ namespace OpenSim.Region.CoreModules.World.Archiver
212 } 265 }
213 catch (Exception e) 266 catch (Exception e)
214 { 267 {
215 m_log.ErrorFormat( 268 m_log.Error(
216 "[ARCHIVER]: Aborting load with error in archive file {0}. {1}", filePath, e); 269 String.Format("[ARCHIVER]: Aborting load with error in archive file {0} ", fullPath), e);
217 m_errorMessage += e.ToString(); 270 m_errorMessage += e.ToString();
218 m_scene.EventManager.TriggerOarFileLoaded(m_requestId, m_errorMessage); 271 m_rootScene.EventManager.TriggerOarFileLoaded(m_requestId, new List<UUID>(), m_errorMessage);
219 return; 272 return;
220 } 273 }
221 finally 274 finally
222 { 275 {
223 archive.Close(); 276 if (archive != null)
277 archive.Close();
224 } 278 }
225 279
226 if (!m_skipAssets) 280 if (!m_skipAssets)
@@ -234,32 +288,143 @@ namespace OpenSim.Region.CoreModules.World.Archiver
234 } 288 }
235 } 289 }
236 290
237 if (!m_merge) 291 foreach (DearchiveContext sceneContext in sceneContexts.Values)
238 { 292 {
239 m_log.Info("[ARCHIVER]: Clearing all existing scene objects"); 293 m_log.InfoFormat("[ARCHIVER:] Loading region {0}", sceneContext.Scene.RegionInfo.RegionName);
240 m_scene.DeleteAllSceneObjects(); 294
295 if (!m_merge)
296 {
297 m_log.Info("[ARCHIVER]: Clearing all existing scene objects");
298 sceneContext.Scene.DeleteAllSceneObjects();
299 }
300
301 try
302 {
303 LoadParcels(sceneContext.Scene, sceneContext.SerialisedParcels);
304 LoadObjects(sceneContext.Scene, sceneContext.SerialisedSceneObjects, sceneContext.SceneObjects);
305
306 // Inform any interested parties that the region has changed. We waited until now so that all
307 // of the region's objects will be loaded when we send this notification.
308 IEstateModule estateModule = sceneContext.Scene.RequestModuleInterface<IEstateModule>();
309 if (estateModule != null)
310 estateModule.TriggerRegionInfoChange();
311 }
312 catch (Exception e)
313 {
314 m_log.Error("[ARCHIVER]: Error loading parcels or objects ", e);
315 m_errorMessage += e.ToString();
316 m_rootScene.EventManager.TriggerOarFileLoaded(m_requestId, new List<UUID>(), m_errorMessage);
317 return;
318 }
241 } 319 }
242 320
243 LoadParcels(serialisedParcels); 321 // Start the scripts. We delayed this because we want the OAR to finish loading ASAP, so
244 LoadObjects(serialisedSceneObjects); 322 // that users can enter the scene. If we allow the scripts to start in the loop above
323 // then they significantly increase the time until the OAR finishes loading.
324 Util.FireAndForget(delegate(object o)
325 {
326 Thread.Sleep(15000);
327 m_log.Info("Starting scripts in scene objects");
328
329 foreach (DearchiveContext sceneContext in sceneContexts.Values)
330 {
331 foreach (SceneObjectGroup sceneObject in sceneContext.SceneObjects)
332 {
333 sceneObject.CreateScriptInstances(0, false, sceneContext.Scene.DefaultScriptEngine, 0); // StateSource.RegionStart
334 sceneObject.ResumeScripts();
335 }
336
337 sceneContext.SceneObjects.Clear();
338 }
339 });
245 340
246 m_log.InfoFormat("[ARCHIVER]: Successfully loaded archive"); 341 m_log.InfoFormat("[ARCHIVER]: Successfully loaded archive");
247 342
248 m_scene.EventManager.TriggerOarFileLoaded(m_requestId, m_errorMessage); 343 m_rootScene.EventManager.TriggerOarFileLoaded(m_requestId, dearchivedScenes.GetLoadedScenes(), m_errorMessage);
344 }
345
346 /// <summary>
347 /// Searches through the files in the archive for the control file, and reads it.
348 /// We must read the control file first, in order to know which regions are available.
349 /// </summary>
350 /// <remarks>
351 /// In most cases the control file *is* first, since that's how we create archives. However,
352 /// it's possible that someone rewrote the archive externally so we can't rely on this fact.
353 /// </remarks>
354 /// <param name="archive"></param>
355 /// <param name="dearchivedScenes"></param>
356 private void FindAndLoadControlFile(out TarArchiveReader archive, out DearchiveScenesInfo dearchivedScenes)
357 {
358 archive = new TarArchiveReader(m_loadStream);
359 dearchivedScenes = new DearchiveScenesInfo();
360
361 string filePath;
362 byte[] data;
363 TarArchiveReader.TarEntryType entryType;
364 bool firstFile = true;
365
366 while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
367 {
368 if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType)
369 continue;
370
371 if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
372 {
373 LoadControlFile(filePath, data, dearchivedScenes);
374
375 // Find which scenes are available in the simulator
376 ArchiveScenesGroup simulatorScenes = new ArchiveScenesGroup();
377 SceneManager.Instance.ForEachScene(delegate(Scene scene2)
378 {
379 simulatorScenes.AddScene(scene2);
380 });
381 simulatorScenes.CalcSceneLocations();
382 dearchivedScenes.SetSimulatorScenes(m_rootScene, simulatorScenes);
383
384 // If the control file wasn't the first file then reset the read pointer
385 if (!firstFile)
386 {
387 m_log.Warn("Control file wasn't the first file in the archive");
388 if (m_loadStream.CanSeek)
389 {
390 m_loadStream.Seek(0, SeekOrigin.Begin);
391 }
392 else if (m_loadPath != null)
393 {
394 archive.Close();
395 archive = null;
396 m_loadStream.Close();
397 m_loadStream = null;
398 m_loadStream = new GZipStream(ArchiveHelpers.GetStream(m_loadPath), CompressionMode.Decompress);
399 archive = new TarArchiveReader(m_loadStream);
400 }
401 else
402 {
403 // There isn't currently a scenario where this happens, but it's best to add a check just in case
404 throw new Exception("Error reading archive: control file wasn't the first file, and the input stream doesn't allow seeking");
405 }
406 }
407
408 return;
409 }
410
411 firstFile = false;
412 }
413
414 throw new Exception("Control file not found");
249 } 415 }
250 416
251 /// <summary> 417 /// <summary>
252 /// Load serialized scene objects. 418 /// Load serialized scene objects.
253 /// </summary> 419 /// </summary>
254 /// <param name="serialisedSceneObjects"></param> 420 protected void LoadObjects(Scene scene, List<string> serialisedSceneObjects, List<SceneObjectGroup> sceneObjects)
255 protected void LoadObjects(List<string> serialisedSceneObjects)
256 { 421 {
257 // Reload serialized prims 422 // Reload serialized prims
258 m_log.InfoFormat("[ARCHIVER]: Loading {0} scene objects. Please wait.", serialisedSceneObjects.Count); 423 m_log.InfoFormat("[ARCHIVER]: Loading {0} scene objects. Please wait.", serialisedSceneObjects.Count);
259 424
260 UUID oldTelehubUUID = m_scene.RegionInfo.RegionSettings.TelehubObject; 425 UUID oldTelehubUUID = scene.RegionInfo.RegionSettings.TelehubObject;
261 426
262 IRegionSerialiserModule serialiser = m_scene.RequestModuleInterface<IRegionSerialiserModule>(); 427 IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>();
263 int sceneObjectsLoadedCount = 0; 428 int sceneObjectsLoadedCount = 0;
264 429
265 foreach (string serialisedSceneObject in serialisedSceneObjects) 430 foreach (string serialisedSceneObject in serialisedSceneObjects)
@@ -280,7 +445,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
280 445
281 SceneObjectGroup sceneObject = serialiser.DeserializeGroupFromXml2(serialisedSceneObject); 446 SceneObjectGroup sceneObject = serialiser.DeserializeGroupFromXml2(serialisedSceneObject);
282 447
283 bool isTelehub = (sceneObject.UUID == oldTelehubUUID); 448 bool isTelehub = (sceneObject.UUID == oldTelehubUUID) && (oldTelehubUUID != UUID.Zero);
284 449
285 // For now, give all incoming scene objects new uuids. This will allow scenes to be cloned 450 // For now, give all incoming scene objects new uuids. This will allow scenes to be cloned
286 // on the same region server and multiple examples a single object archive to be imported 451 // 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
290 if (isTelehub) 455 if (isTelehub)
291 { 456 {
292 // Change the Telehub Object to the new UUID 457 // Change the Telehub Object to the new UUID
293 m_scene.RegionInfo.RegionSettings.TelehubObject = sceneObject.UUID; 458 scene.RegionInfo.RegionSettings.TelehubObject = sceneObject.UUID;
294 m_scene.RegionInfo.RegionSettings.Save(); 459 scene.RegionInfo.RegionSettings.Save();
295 oldTelehubUUID = UUID.Zero; 460 oldTelehubUUID = UUID.Zero;
296 } 461 }
297 462
@@ -301,17 +466,17 @@ namespace OpenSim.Region.CoreModules.World.Archiver
301 { 466 {
302 if (part.CreatorData == null || part.CreatorData == string.Empty) 467 if (part.CreatorData == null || part.CreatorData == string.Empty)
303 { 468 {
304 if (!ResolveUserUuid(part.CreatorID)) 469 if (!ResolveUserUuid(scene, part.CreatorID))
305 part.CreatorID = m_scene.RegionInfo.EstateSettings.EstateOwner; 470 part.CreatorID = scene.RegionInfo.EstateSettings.EstateOwner;
306 } 471 }
307 if (UserManager != null) 472 if (UserManager != null)
308 UserManager.AddUser(part.CreatorID, part.CreatorData); 473 UserManager.AddUser(part.CreatorID, part.CreatorData);
309 474
310 if (!ResolveUserUuid(part.OwnerID)) 475 if (!ResolveUserUuid(scene, part.OwnerID))
311 part.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner; 476 part.OwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
312 477
313 if (!ResolveUserUuid(part.LastOwnerID)) 478 if (!ResolveUserUuid(scene, part.LastOwnerID))
314 part.LastOwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner; 479 part.LastOwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
315 480
316 if (!ResolveGroupUuid(part.GroupID)) 481 if (!ResolveGroupUuid(part.GroupID))
317 part.GroupID = UUID.Zero; 482 part.GroupID = UUID.Zero;
@@ -328,15 +493,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
328 TaskInventoryDictionary inv = part.TaskInventory; 493 TaskInventoryDictionary inv = part.TaskInventory;
329 foreach (KeyValuePair<UUID, TaskInventoryItem> kvp in inv) 494 foreach (KeyValuePair<UUID, TaskInventoryItem> kvp in inv)
330 { 495 {
331 if (!ResolveUserUuid(kvp.Value.OwnerID)) 496 if (!ResolveUserUuid(scene, kvp.Value.OwnerID))
332 { 497 {
333 kvp.Value.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner; 498 kvp.Value.OwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
334 } 499 }
335 500
336 if (kvp.Value.CreatorData == null || kvp.Value.CreatorData == string.Empty) 501 if (kvp.Value.CreatorData == null || kvp.Value.CreatorData == string.Empty)
337 { 502 {
338 if (!ResolveUserUuid(kvp.Value.CreatorID)) 503 if (!ResolveUserUuid(scene, kvp.Value.CreatorID))
339 kvp.Value.CreatorID = m_scene.RegionInfo.EstateSettings.EstateOwner; 504 kvp.Value.CreatorID = scene.RegionInfo.EstateSettings.EstateOwner;
340 } 505 }
341 506
342 if (UserManager != null) 507 if (UserManager != null)
@@ -348,10 +513,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver
348 } 513 }
349 } 514 }
350 515
351 if (m_scene.AddRestoredSceneObject(sceneObject, true, false)) 516 if (scene.AddRestoredSceneObject(sceneObject, true, false))
352 { 517 {
353 sceneObjectsLoadedCount++; 518 sceneObjectsLoadedCount++;
354 sceneObject.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, 0); 519 sceneObject.CreateScriptInstances(0, false, scene.DefaultScriptEngine, 0);
355 sceneObject.ResumeScripts(); 520 sceneObject.ResumeScripts();
356 } 521 }
357 } 522 }
@@ -366,16 +531,17 @@ namespace OpenSim.Region.CoreModules.World.Archiver
366 if (oldTelehubUUID != UUID.Zero) 531 if (oldTelehubUUID != UUID.Zero)
367 { 532 {
368 m_log.WarnFormat("Telehub object not found: {0}", oldTelehubUUID); 533 m_log.WarnFormat("Telehub object not found: {0}", oldTelehubUUID);
369 m_scene.RegionInfo.RegionSettings.TelehubObject = UUID.Zero; 534 scene.RegionInfo.RegionSettings.TelehubObject = UUID.Zero;
370 m_scene.RegionInfo.RegionSettings.ClearSpawnPoints(); 535 scene.RegionInfo.RegionSettings.ClearSpawnPoints();
371 } 536 }
372 } 537 }
373 538
374 /// <summary> 539 /// <summary>
375 /// Load serialized parcels. 540 /// Load serialized parcels.
376 /// </summary> 541 /// </summary>
542 /// <param name="scene"></param>
377 /// <param name="serialisedParcels"></param> 543 /// <param name="serialisedParcels"></param>
378 protected void LoadParcels(List<string> serialisedParcels) 544 protected void LoadParcels(Scene scene, List<string> serialisedParcels)
379 { 545 {
380 // Reload serialized parcels 546 // Reload serialized parcels
381 m_log.InfoFormat("[ARCHIVER]: Loading {0} parcels. Please wait.", serialisedParcels.Count); 547 m_log.InfoFormat("[ARCHIVER]: Loading {0} parcels. Please wait.", serialisedParcels.Count);
@@ -386,8 +552,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
386 552
387 // Validate User and Group UUID's 553 // Validate User and Group UUID's
388 554
389 if (!ResolveUserUuid(parcel.OwnerID)) 555 if (!ResolveUserUuid(scene, parcel.OwnerID))
390 parcel.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner; 556 parcel.OwnerID = m_rootScene.RegionInfo.EstateSettings.EstateOwner;
391 557
392 if (!ResolveGroupUuid(parcel.GroupID)) 558 if (!ResolveGroupUuid(parcel.GroupID))
393 { 559 {
@@ -398,7 +564,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
398 List<LandAccessEntry> accessList = new List<LandAccessEntry>(); 564 List<LandAccessEntry> accessList = new List<LandAccessEntry>();
399 foreach (LandAccessEntry entry in parcel.ParcelAccessList) 565 foreach (LandAccessEntry entry in parcel.ParcelAccessList)
400 { 566 {
401 if (ResolveUserUuid(entry.AgentID)) 567 if (ResolveUserUuid(scene, entry.AgentID))
402 accessList.Add(entry); 568 accessList.Add(entry);
403 // else, drop this access rule 569 // else, drop this access rule
404 } 570 }
@@ -414,23 +580,24 @@ namespace OpenSim.Region.CoreModules.World.Archiver
414 if (!m_merge) 580 if (!m_merge)
415 { 581 {
416 bool setupDefaultParcel = (landData.Count == 0); 582 bool setupDefaultParcel = (landData.Count == 0);
417 m_scene.LandChannel.Clear(setupDefaultParcel); 583 scene.LandChannel.Clear(setupDefaultParcel);
418 } 584 }
419 585
420 m_scene.EventManager.TriggerIncomingLandDataFromStorage(landData); 586 scene.EventManager.TriggerIncomingLandDataFromStorage(landData);
421 m_log.InfoFormat("[ARCHIVER]: Restored {0} parcels.", landData.Count); 587 m_log.InfoFormat("[ARCHIVER]: Restored {0} parcels.", landData.Count);
422 } 588 }
423 589
424 /// <summary> 590 /// <summary>
425 /// Look up the given user id to check whether it's one that is valid for this grid. 591 /// Look up the given user id to check whether it's one that is valid for this grid.
426 /// </summary> 592 /// </summary>
593 /// <param name="scene"></param>
427 /// <param name="uuid"></param> 594 /// <param name="uuid"></param>
428 /// <returns></returns> 595 /// <returns></returns>
429 private bool ResolveUserUuid(UUID uuid) 596 private bool ResolveUserUuid(Scene scene, UUID uuid)
430 { 597 {
431 if (!m_validUserUuids.ContainsKey(uuid)) 598 if (!m_validUserUuids.ContainsKey(uuid))
432 { 599 {
433 UserAccount account = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid); 600 UserAccount account = scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, uuid);
434 m_validUserUuids.Add(uuid, account != null); 601 m_validUserUuids.Add(uuid, account != null);
435 } 602 }
436 603
@@ -485,7 +652,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
485 string extension = filename.Substring(i); 652 string extension = filename.Substring(i);
486 string uuid = filename.Remove(filename.Length - extension.Length); 653 string uuid = filename.Remove(filename.Length - extension.Length);
487 654
488 if (m_scene.AssetService.GetMetadata(uuid) != null) 655 if (m_assetService.GetMetadata(uuid) != null)
489 { 656 {
490 // m_log.DebugFormat("[ARCHIVER]: found existing asset {0}",uuid); 657 // m_log.DebugFormat("[ARCHIVER]: found existing asset {0}",uuid);
491 return true; 658 return true;
@@ -505,7 +672,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
505 672
506 // We're relying on the asset service to do the sensible thing and not store the asset if it already 673 // We're relying on the asset service to do the sensible thing and not store the asset if it already
507 // exists. 674 // exists.
508 m_scene.AssetService.Store(asset); 675 m_assetService.Store(asset);
509 676
510 /** 677 /**
511 * Create layers on decode for image assets. This is likely to significantly increase the time to load archives so 678 * 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
533 /// <summary> 700 /// <summary>
534 /// Load region settings data 701 /// Load region settings data
535 /// </summary> 702 /// </summary>
703 /// <param name="scene"></param>
536 /// <param name="settingsPath"></param> 704 /// <param name="settingsPath"></param>
537 /// <param name="data"></param> 705 /// <param name="data"></param>
706 /// <param name="dearchivedScenes"></param>
538 /// <returns> 707 /// <returns>
539 /// true if settings were loaded successfully, false otherwise 708 /// true if settings were loaded successfully, false otherwise
540 /// </returns> 709 /// </returns>
541 private bool LoadRegionSettings(string settingsPath, byte[] data) 710 private bool LoadRegionSettings(Scene scene, string settingsPath, byte[] data, DearchiveScenesInfo dearchivedScenes)
542 { 711 {
543 RegionSettings loadedRegionSettings; 712 RegionSettings loadedRegionSettings;
544 713
@@ -554,7 +723,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
554 return false; 723 return false;
555 } 724 }
556 725
557 RegionSettings currentRegionSettings = m_scene.RegionInfo.RegionSettings; 726 RegionSettings currentRegionSettings = scene.RegionInfo.RegionSettings;
558 727
559 currentRegionSettings.AgentLimit = loadedRegionSettings.AgentLimit; 728 currentRegionSettings.AgentLimit = loadedRegionSettings.AgentLimit;
560 currentRegionSettings.AllowDamage = loadedRegionSettings.AllowDamage; 729 currentRegionSettings.AllowDamage = loadedRegionSettings.AllowDamage;
@@ -591,12 +760,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
591 foreach (SpawnPoint sp in loadedRegionSettings.SpawnPoints()) 760 foreach (SpawnPoint sp in loadedRegionSettings.SpawnPoints())
592 currentRegionSettings.AddSpawnPoint(sp); 761 currentRegionSettings.AddSpawnPoint(sp);
593 762
763 currentRegionSettings.LoadedCreationDateTime = dearchivedScenes.LoadedCreationDateTime;
764 currentRegionSettings.LoadedCreationID = dearchivedScenes.GetOriginalRegionID(scene.RegionInfo.RegionID).ToString();
765
594 currentRegionSettings.Save(); 766 currentRegionSettings.Save();
595 767
596 m_scene.TriggerEstateSunUpdate(); 768 scene.TriggerEstateSunUpdate();
597 769
598 IEstateModule estateModule = m_scene.RequestModuleInterface<IEstateModule>(); 770 IEstateModule estateModule = scene.RequestModuleInterface<IEstateModule>();
599
600 if (estateModule != null) 771 if (estateModule != null)
601 estateModule.sendRegionHandshakeToAll(); 772 estateModule.sendRegionHandshakeToAll();
602 773
@@ -606,14 +777,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
606 /// <summary> 777 /// <summary>
607 /// Load terrain data 778 /// Load terrain data
608 /// </summary> 779 /// </summary>
780 /// <param name="scene"></param>
609 /// <param name="terrainPath"></param> 781 /// <param name="terrainPath"></param>
610 /// <param name="data"></param> 782 /// <param name="data"></param>
611 /// <returns> 783 /// <returns>
612 /// true if terrain was resolved successfully, false otherwise. 784 /// true if terrain was resolved successfully, false otherwise.
613 /// </returns> 785 /// </returns>
614 private bool LoadTerrain(string terrainPath, byte[] data) 786 private bool LoadTerrain(Scene scene, string terrainPath, byte[] data)
615 { 787 {
616 ITerrainModule terrainModule = m_scene.RequestModuleInterface<ITerrainModule>(); 788 ITerrainModule terrainModule = scene.RequestModuleInterface<ITerrainModule>();
617 789
618 MemoryStream ms = new MemoryStream(data); 790 MemoryStream ms = new MemoryStream(data);
619 terrainModule.LoadFromStream(terrainPath, ms); 791 terrainModule.LoadFromStream(terrainPath, ms);
@@ -629,17 +801,18 @@ namespace OpenSim.Region.CoreModules.World.Archiver
629 /// </summary> 801 /// </summary>
630 /// <param name="path"></param> 802 /// <param name="path"></param>
631 /// <param name="data"></param> 803 /// <param name="data"></param>
632 public void LoadControlFile(string path, byte[] data) 804 /// <param name="dearchivedScenes"></param>
805 public DearchiveScenesInfo LoadControlFile(string path, byte[] data, DearchiveScenesInfo dearchivedScenes)
633 { 806 {
634 XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable()); 807 XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
635 XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None); 808 XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
636 XmlTextReader xtr = new XmlTextReader(Encoding.ASCII.GetString(data), XmlNodeType.Document, context); 809 XmlTextReader xtr = new XmlTextReader(Encoding.ASCII.GetString(data), XmlNodeType.Document, context);
637 810
638 RegionSettings currentRegionSettings = m_scene.RegionInfo.RegionSettings; 811 // Loaded metadata will be empty if no information exists in the archive
812 dearchivedScenes.LoadedCreationDateTime = 0;
813 dearchivedScenes.DefaultOriginalID = "";
639 814
640 // Loaded metadata will empty if no information exists in the archive 815 bool multiRegion = false;
641 currentRegionSettings.LoadedCreationDateTime = 0;
642 currentRegionSettings.LoadedCreationID = "";
643 816
644 while (xtr.Read()) 817 while (xtr.Read())
645 { 818 {
@@ -665,18 +838,44 @@ namespace OpenSim.Region.CoreModules.World.Archiver
665 { 838 {
666 int value; 839 int value;
667 if (Int32.TryParse(xtr.ReadElementContentAsString(), out value)) 840 if (Int32.TryParse(xtr.ReadElementContentAsString(), out value))
668 currentRegionSettings.LoadedCreationDateTime = value; 841 dearchivedScenes.LoadedCreationDateTime = value;
669 } 842 }
670 else if (xtr.Name.ToString() == "id") 843 else if (xtr.Name.ToString() == "row")
844 {
845 multiRegion = true;
846 dearchivedScenes.StartRow();
847 }
848 else if (xtr.Name.ToString() == "region")
671 { 849 {
672 currentRegionSettings.LoadedCreationID = xtr.ReadElementContentAsString(); 850 dearchivedScenes.StartRegion();
851 }
852 else if (xtr.Name.ToString() == "id")
853 {
854 string id = xtr.ReadElementContentAsString();
855 dearchivedScenes.DefaultOriginalID = id;
856 if (multiRegion)
857 dearchivedScenes.SetRegionOriginalID(id);
858 }
859 else if (xtr.Name.ToString() == "dir")
860 {
861 dearchivedScenes.SetRegionDirectory(xtr.ReadElementContentAsString());
673 } 862 }
674 } 863 }
675 } 864 }
676 865
677 currentRegionSettings.Save(); 866 dearchivedScenes.MultiRegionFormat = multiRegion;
678 867 if (!multiRegion)
868 {
869 // Add the single scene
870 dearchivedScenes.StartRow();
871 dearchivedScenes.StartRegion();
872 dearchivedScenes.SetRegionOriginalID(dearchivedScenes.DefaultOriginalID);
873 dearchivedScenes.SetRegionDirectory("");
874 }
875
679 ControlFileLoaded = true; 876 ControlFileLoaded = true;
877
878 return dearchivedScenes;
680 } 879 }
681 } 880 }
682} \ No newline at end of file 881} \ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs
new file mode 100644
index 0000000..a66ed88
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs
@@ -0,0 +1,176 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Linq;
31using System.Text;
32using OpenSim.Region.Framework.Scenes;
33using OpenMetaverse;
34using System.Drawing;
35
36namespace OpenSim.Region.CoreModules.World.Archiver
37{
38 /// <summary>
39 /// A group of regions arranged in a rectangle, possibly with holes.
40 /// </summary>
41 /// <remarks>
42 /// The regions usually (but not necessarily) belong to an archive file, in which case we
43 /// store additional information used to create the archive (e.g., each region's
44 /// directory within the archive).
45 /// </remarks>
46 public class ArchiveScenesGroup
47 {
48 /// <summary>
49 /// All the regions. The outer dictionary contains rows (key: Y coordinate).
50 /// The inner dictionaries contain each row's regions (key: X coordinate).
51 /// </summary>
52 public SortedDictionary<uint, SortedDictionary<uint, Scene>> Regions { get; set; }
53
54 /// <summary>
55 /// The subdirectory where each region is stored in the archive.
56 /// </summary>
57 protected Dictionary<UUID, string> m_regionDirs;
58
59 /// <summary>
60 /// The grid coordinates of the regions' bounding box.
61 /// </summary>
62 public Rectangle Rect { get; set; }
63
64
65 public ArchiveScenesGroup()
66 {
67 Regions = new SortedDictionary<uint, SortedDictionary<uint, Scene>>();
68 m_regionDirs = new Dictionary<UUID, string>();
69 Rect = new Rectangle(0, 0, 0, 0);
70 }
71
72 public void AddScene(Scene scene)
73 {
74 uint x = scene.RegionInfo.RegionLocX;
75 uint y = scene.RegionInfo.RegionLocY;
76
77 SortedDictionary<uint, Scene> row;
78 if (!Regions.TryGetValue(y, out row))
79 {
80 row = new SortedDictionary<uint, Scene>();
81 Regions[y] = row;
82 }
83
84 row[x] = scene;
85 }
86
87 /// <summary>
88 /// Called after all the scenes have been added. Performs calculations that require
89 /// knowledge of all the scenes.
90 /// </summary>
91 public void CalcSceneLocations()
92 {
93 if (Regions.Count == 0)
94 return;
95
96 // Find the bounding rectangle
97
98 uint firstY = Regions.First().Key;
99 uint lastY = Regions.Last().Key;
100
101 uint? firstX = null;
102 uint? lastX = null;
103
104 foreach (SortedDictionary<uint, Scene> row in Regions.Values)
105 {
106 uint curFirstX = row.First().Key;
107 uint curLastX = row.Last().Key;
108
109 firstX = (firstX == null) ? curFirstX : (firstX < curFirstX) ? firstX : curFirstX;
110 lastX = (lastX == null) ? curLastX : (lastX > curLastX) ? lastX : curLastX;
111 }
112
113 Rect = new Rectangle((int)firstX, (int)firstY, (int)(lastY - firstY + 1), (int)(lastX - firstX + 1));
114
115
116 // Calculate the subdirectory in which each region will be stored in the archive
117
118 m_regionDirs.Clear();
119 ForEachScene(delegate(Scene scene)
120 {
121 // We add the region's coordinates to ensure uniqueness even if multiple regions have the same name
122 string path = string.Format("{0}_{1}_{2}",
123 scene.RegionInfo.RegionLocX - Rect.X + 1,
124 scene.RegionInfo.RegionLocY - Rect.Y + 1,
125 scene.RegionInfo.RegionName.Replace(' ', '_'));
126 m_regionDirs[scene.RegionInfo.RegionID] = path;
127 });
128 }
129
130 /// <summary>
131 /// Returns the subdirectory where the region is stored.
132 /// </summary>
133 /// <param name="regionID"></param>
134 /// <returns></returns>
135 public string GetRegionDir(UUID regionID)
136 {
137 return m_regionDirs[regionID];
138 }
139
140 /// <summary>
141 /// Performs an action on all the scenes in this order: rows from South to North,
142 /// and within each row West to East.
143 /// </summary>
144 /// <param name="action"></param>
145 public void ForEachScene(Action<Scene> action)
146 {
147 foreach (SortedDictionary<uint, Scene> row in Regions.Values)
148 {
149 foreach (Scene scene in row.Values)
150 {
151 action(scene);
152 }
153 }
154 }
155
156 /// <summary>
157 /// Returns the scene at position 'location'.
158 /// </summary>
159 /// <param name="location">A location in the grid</param>
160 /// <param name="scene">The scene at this location</param>
161 /// <returns>Whether the scene was found</returns>
162 public bool TryGetScene(Point location, out Scene scene)
163 {
164 SortedDictionary<uint, Scene> row;
165 if (Regions.TryGetValue((uint)location.Y, out row))
166 {
167 if (row.TryGetValue((uint)location.X, out scene))
168 return true;
169 }
170
171 scene = null;
172 return false;
173 }
174
175 }
176}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs
new file mode 100644
index 0000000..d751b1c
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs
@@ -0,0 +1,634 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.IO.Compression;
32using System.Reflection;
33using System.Text.RegularExpressions;
34using System.Threading;
35using System.Xml;
36using log4net;
37using OpenMetaverse;
38using OpenSim.Framework;
39using OpenSim.Framework.Serialization;
40using OpenSim.Region.CoreModules.World.Terrain;
41using OpenSim.Region.Framework.Interfaces;
42using OpenSim.Region.Framework.Scenes;
43using Ionic.Zlib;
44using GZipStream = Ionic.Zlib.GZipStream;
45using CompressionMode = Ionic.Zlib.CompressionMode;
46using OpenSim.Framework.Serialization.External;
47
48namespace OpenSim.Region.CoreModules.World.Archiver
49{
50 /// <summary>
51 /// Prepare to write out an archive.
52 /// </summary>
53 public class ArchiveWriteRequest
54 {
55 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
56
57 /// <summary>
58 /// The minimum major version of OAR that we can write.
59 /// </summary>
60 public static int MIN_MAJOR_VERSION = 0;
61
62 /// <summary>
63 /// The maximum major version of OAR that we can write.
64 /// </summary>
65 public static int MAX_MAJOR_VERSION = 1;
66
67 /// <summary>
68 /// Whether we're saving a multi-region archive.
69 /// </summary>
70 public bool MultiRegionFormat { get; set; }
71
72 /// <summary>
73 /// Determine whether this archive will save assets. Default is true.
74 /// </summary>
75 public bool SaveAssets { get; set; }
76
77 /// <summary>
78 /// Determines which objects will be included in the archive, according to their permissions.
79 /// Default is null, meaning no permission checks.
80 /// </summary>
81 public string CheckPermissions { get; set; }
82
83 protected Scene m_rootScene;
84 protected Stream m_saveStream;
85 protected TarArchiveWriter m_archiveWriter;
86 protected Guid m_requestId;
87 protected Dictionary<string, object> m_options;
88
89 /// <summary>
90 /// Constructor
91 /// </summary>
92 /// <param name="module">Calling module</param>
93 /// <param name="savePath">The path to which to save data.</param>
94 /// <param name="requestId">The id associated with this request</param>
95 /// <exception cref="System.IO.IOException">
96 /// If there was a problem opening a stream for the file specified by the savePath
97 /// </exception>
98 public ArchiveWriteRequest(Scene scene, string savePath, Guid requestId) : this(scene, requestId)
99 {
100 try
101 {
102 m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress, CompressionLevel.BestCompression);
103 }
104 catch (EntryPointNotFoundException e)
105 {
106 m_log.ErrorFormat(
107 "[ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
108 + "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
109 m_log.ErrorFormat("{0} {1}", e.Message, e.StackTrace);
110 }
111 }
112
113 /// <summary>
114 /// Constructor.
115 /// </summary>
116 /// <param name="scene">The root scene to archive</param>
117 /// <param name="saveStream">The stream to which to save data.</param>
118 /// <param name="requestId">The id associated with this request</param>
119 public ArchiveWriteRequest(Scene scene, Stream saveStream, Guid requestId) : this(scene, requestId)
120 {
121 m_saveStream = saveStream;
122 }
123
124 protected ArchiveWriteRequest(Scene scene, Guid requestId)
125 {
126 m_rootScene = scene;
127 m_requestId = requestId;
128 m_archiveWriter = null;
129
130 MultiRegionFormat = false;
131 SaveAssets = true;
132 CheckPermissions = null;
133 }
134
135 /// <summary>
136 /// Archive the region requested.
137 /// </summary>
138 /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception>
139 public void ArchiveRegion(Dictionary<string, object> options)
140 {
141 m_options = options;
142
143 if (options.ContainsKey("all") && (bool)options["all"])
144 MultiRegionFormat = true;
145
146 if (options.ContainsKey("noassets") && (bool)options["noassets"])
147 SaveAssets = false;
148
149 Object temp;
150 if (options.TryGetValue("checkPermissions", out temp))
151 CheckPermissions = (string)temp;
152
153
154 // Find the regions to archive
155 ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();
156 if (MultiRegionFormat)
157 {
158 m_log.InfoFormat("[ARCHIVER]: Saving {0} regions", SceneManager.Instance.Scenes.Count);
159 SceneManager.Instance.ForEachScene(delegate(Scene scene)
160 {
161 scenesGroup.AddScene(scene);
162 });
163 }
164 else
165 {
166 scenesGroup.AddScene(m_rootScene);
167 }
168 scenesGroup.CalcSceneLocations();
169
170
171 m_archiveWriter = new TarArchiveWriter(m_saveStream);
172
173 try
174 {
175 // Write out control file. It should be first so that it will be found ASAP when loading the file.
176 m_archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(scenesGroup));
177 m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");
178
179 // Archive the regions
180
181 Dictionary<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>();
182
183 scenesGroup.ForEachScene(delegate(Scene scene)
184 {
185 string regionDir = MultiRegionFormat ? scenesGroup.GetRegionDir(scene.RegionInfo.RegionID) : "";
186 ArchiveOneRegion(scene, regionDir, assetUuids);
187 });
188
189 // Archive the assets
190
191 if (SaveAssets)
192 {
193 m_log.DebugFormat("[ARCHIVER]: Saving {0} assets", assetUuids.Count);
194
195 // Asynchronously request all the assets required to perform this archive operation
196 AssetsRequest ar
197 = new AssetsRequest(
198 new AssetsArchiver(m_archiveWriter), assetUuids,
199 m_rootScene.AssetService, m_rootScene.UserAccountService,
200 m_rootScene.RegionInfo.ScopeID, options, ReceivedAllAssets);
201
202 Util.FireAndForget(o => ar.Execute());
203
204 // CloseArchive() will be called from ReceivedAllAssets()
205 }
206 else
207 {
208 m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified");
209 CloseArchive(string.Empty);
210 }
211 }
212 catch (Exception e)
213 {
214 CloseArchive(e.Message);
215 throw;
216 }
217 }
218
219
220 private void ArchiveOneRegion(Scene scene, string regionDir, Dictionary<UUID, AssetType> assetUuids)
221 {
222 m_log.InfoFormat("[ARCHIVER]: Writing region {0}", scene.RegionInfo.RegionName);
223
224 EntityBase[] entities = scene.GetEntities();
225 List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>();
226
227 int numObjectsSkippedPermissions = 0;
228
229 // Filter entities so that we only have scene objects.
230 // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods
231 // end up having to do this
232 IPermissionsModule permissionsModule = scene.RequestModuleInterface<IPermissionsModule>();
233 foreach (EntityBase entity in entities)
234 {
235 if (entity is SceneObjectGroup)
236 {
237 SceneObjectGroup sceneObject = (SceneObjectGroup)entity;
238
239 if (!sceneObject.IsDeleted && !sceneObject.IsAttachment)
240 {
241 if (!CanUserArchiveObject(scene.RegionInfo.EstateSettings.EstateOwner, sceneObject, CheckPermissions, permissionsModule))
242 {
243 // The user isn't allowed to copy/transfer this object, so it will not be included in the OAR.
244 ++numObjectsSkippedPermissions;
245 }
246 else
247 {
248 sceneObjects.Add(sceneObject);
249 }
250 }
251 }
252 }
253
254 if (SaveAssets)
255 {
256 UuidGatherer assetGatherer = new UuidGatherer(scene.AssetService);
257 int prevAssets = assetUuids.Count;
258
259 foreach (SceneObjectGroup sceneObject in sceneObjects)
260 {
261 assetGatherer.GatherAssetUuids(sceneObject, assetUuids);
262 }
263
264 m_log.DebugFormat(
265 "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets",
266 sceneObjects.Count, assetUuids.Count - prevAssets);
267 }
268
269 if (numObjectsSkippedPermissions > 0)
270 {
271 m_log.DebugFormat(
272 "[ARCHIVER]: {0} scene objects skipped due to lack of permissions",
273 numObjectsSkippedPermissions);
274 }
275
276 // Make sure that we also request terrain texture assets
277 RegionSettings regionSettings = scene.RegionInfo.RegionSettings;
278
279 if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1)
280 assetUuids[regionSettings.TerrainTexture1] = AssetType.Texture;
281
282 if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2)
283 assetUuids[regionSettings.TerrainTexture2] = AssetType.Texture;
284
285 if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3)
286 assetUuids[regionSettings.TerrainTexture3] = AssetType.Texture;
287
288 if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4)
289 assetUuids[regionSettings.TerrainTexture4] = AssetType.Texture;
290
291 Save(scene, sceneObjects, regionDir);
292 }
293
294 /// <summary>
295 /// Checks whether the user has permission to export an object group to an OAR.
296 /// </summary>
297 /// <param name="user">The user</param>
298 /// <param name="objGroup">The object group</param>
299 /// <param name="checkPermissions">Which permissions to check: "C" = Copy, "T" = Transfer</param>
300 /// <param name="permissionsModule">The scene's permissions module</param>
301 /// <returns>Whether the user is allowed to export the object to an OAR</returns>
302 private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions, IPermissionsModule permissionsModule)
303 {
304 if (checkPermissions == null)
305 return true;
306
307 if (permissionsModule == null)
308 return true; // this shouldn't happen
309
310 // Check whether the user is permitted to export all of the parts in the SOG. If any
311 // part can't be exported then the entire SOG can't be exported.
312
313 bool permitted = true;
314 //int primNumber = 1;
315
316 foreach (SceneObjectPart obj in objGroup.Parts)
317 {
318 uint perm;
319 PermissionClass permissionClass = permissionsModule.GetPermissionClass(user, obj);
320 switch (permissionClass)
321 {
322 case PermissionClass.Owner:
323 perm = obj.BaseMask;
324 break;
325 case PermissionClass.Group:
326 perm = obj.GroupMask | obj.EveryoneMask;
327 break;
328 case PermissionClass.Everyone:
329 default:
330 perm = obj.EveryoneMask;
331 break;
332 }
333
334 bool canCopy = (perm & (uint)PermissionMask.Copy) != 0;
335 bool canTransfer = (perm & (uint)PermissionMask.Transfer) != 0;
336
337 // Special case: if Everyone can copy the object then this implies it can also be
338 // Transferred.
339 // However, if the user is the Owner then we don't check EveryoneMask, because it seems that the mask
340 // always (incorrectly) includes the Copy bit set in this case. But that's a mistake: the viewer
341 // does NOT show that the object has Everyone-Copy permissions, and doesn't allow it to be copied.
342 if (permissionClass != PermissionClass.Owner)
343 canTransfer |= (obj.EveryoneMask & (uint)PermissionMask.Copy) != 0;
344
345 bool partPermitted = true;
346 if (checkPermissions.Contains("C") && !canCopy)
347 partPermitted = false;
348 if (checkPermissions.Contains("T") && !canTransfer)
349 partPermitted = false;
350
351 // If the user is the Creator of the object then it can always be included in the OAR
352 bool creator = (obj.CreatorID.Guid == user.Guid);
353 if (creator)
354 partPermitted = true;
355
356 //string name = (objGroup.PrimCount == 1) ? objGroup.Name : string.Format("{0} ({1}/{2})", obj.Name, primNumber, objGroup.PrimCount);
357 //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}",
358 // name, obj.BaseMask, obj.OwnerMask, obj.EveryoneMask,
359 // permissionClass, checkPermissions, canCopy, canTransfer, creator, partPermitted);
360
361 if (!partPermitted)
362 {
363 permitted = false;
364 break;
365 }
366
367 //++primNumber;
368 }
369
370 return permitted;
371 }
372
373 /// <summary>
374 /// Create the control file.
375 /// </summary>
376 /// <returns></returns>
377 public string CreateControlFile(ArchiveScenesGroup scenesGroup)
378 {
379 int majorVersion;
380 int minorVersion;
381
382 if (MultiRegionFormat)
383 {
384 majorVersion = MAX_MAJOR_VERSION;
385 minorVersion = 0;
386 }
387 else
388 {
389 // To support older versions of OpenSim, we continue to create single-region OARs
390 // using the old file format. In the future this format will be discontinued.
391 majorVersion = 0;
392 minorVersion = 8;
393 }
394//
395// if (m_options.ContainsKey("version"))
396// {
397// string[] parts = m_options["version"].ToString().Split('.');
398// if (parts.Length >= 1)
399// {
400// majorVersion = Int32.Parse(parts[0]);
401//
402// if (parts.Length >= 2)
403// minorVersion = Int32.Parse(parts[1]);
404// }
405// }
406//
407// if (majorVersion < MIN_MAJOR_VERSION || majorVersion > MAX_MAJOR_VERSION)
408// {
409// throw new Exception(
410// string.Format(
411// "OAR version number for save must be between {0} and {1}",
412// MIN_MAJOR_VERSION, MAX_MAJOR_VERSION));
413// }
414// else if (majorVersion == MAX_MAJOR_VERSION)
415// {
416// // Force 1.0
417// minorVersion = 0;
418// }
419// else if (majorVersion == MIN_MAJOR_VERSION)
420// {
421// // Force 0.4
422// minorVersion = 4;
423// }
424
425 m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion);
426 if (majorVersion == 1)
427 {
428 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");
429 }
430
431 String s;
432
433 using (StringWriter sw = new StringWriter())
434 {
435 using (XmlTextWriter xtw = new XmlTextWriter(sw))
436 {
437 xtw.Formatting = Formatting.Indented;
438 xtw.WriteStartDocument();
439 xtw.WriteStartElement("archive");
440 xtw.WriteAttributeString("major_version", majorVersion.ToString());
441 xtw.WriteAttributeString("minor_version", minorVersion.ToString());
442
443 xtw.WriteStartElement("creation_info");
444 DateTime now = DateTime.UtcNow;
445 TimeSpan t = now - new DateTime(1970, 1, 1);
446 xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString());
447 if (!MultiRegionFormat)
448 xtw.WriteElementString("id", m_rootScene.RegionInfo.RegionID.ToString());
449 xtw.WriteEndElement();
450
451 xtw.WriteElementString("assets_included", SaveAssets.ToString());
452
453 if (MultiRegionFormat)
454 {
455 WriteRegionsManifest(scenesGroup, xtw);
456 }
457 else
458 {
459 xtw.WriteStartElement("region_info");
460 WriteRegionInfo(m_rootScene, xtw);
461 xtw.WriteEndElement();
462 }
463
464 xtw.WriteEndElement();
465
466 xtw.Flush();
467 }
468
469 s = sw.ToString();
470 }
471
472 return s;
473 }
474
475 /// <summary>
476 /// Writes the list of regions included in a multi-region OAR.
477 /// </summary>
478 private static void WriteRegionsManifest(ArchiveScenesGroup scenesGroup, XmlTextWriter xtw)
479 {
480 xtw.WriteStartElement("regions");
481
482 // Write the regions in order: rows from South to North, then regions from West to East.
483 // The list of regions can have "holes"; we write empty elements in their position.
484
485 for (uint y = (uint)scenesGroup.Rect.Top; y < scenesGroup.Rect.Bottom; ++y)
486 {
487 SortedDictionary<uint, Scene> row;
488 if (scenesGroup.Regions.TryGetValue(y, out row))
489 {
490 xtw.WriteStartElement("row");
491
492 for (uint x = (uint)scenesGroup.Rect.Left; x < scenesGroup.Rect.Right; ++x)
493 {
494 Scene scene;
495 if (row.TryGetValue(x, out scene))
496 {
497 xtw.WriteStartElement("region");
498 xtw.WriteElementString("id", scene.RegionInfo.RegionID.ToString());
499 xtw.WriteElementString("dir", scenesGroup.GetRegionDir(scene.RegionInfo.RegionID));
500 WriteRegionInfo(scene, xtw);
501 xtw.WriteEndElement();
502 }
503 else
504 {
505 // Write a placeholder for a missing region
506 xtw.WriteElementString("region", "");
507 }
508 }
509
510 xtw.WriteEndElement();
511 }
512 else
513 {
514 // Write a placeholder for a missing row
515 xtw.WriteElementString("row", "");
516 }
517 }
518
519 xtw.WriteEndElement(); // "regions"
520 }
521
522 protected static void WriteRegionInfo(Scene scene, XmlTextWriter xtw)
523 {
524 bool isMegaregion;
525 Vector2 size;
526
527 IRegionCombinerModule rcMod = scene.RequestModuleInterface<IRegionCombinerModule>();
528
529 if (rcMod != null)
530 isMegaregion = rcMod.IsRootForMegaregion(scene.RegionInfo.RegionID);
531 else
532 isMegaregion = false;
533
534 if (isMegaregion)
535 size = rcMod.GetSizeOfMegaregion(scene.RegionInfo.RegionID);
536 else
537 size = new Vector2((float)Constants.RegionSize, (float)Constants.RegionSize);
538
539 xtw.WriteElementString("is_megaregion", isMegaregion.ToString());
540 xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y));
541 }
542
543
544 protected void Save(Scene scene, List<SceneObjectGroup> sceneObjects, string regionDir)
545 {
546 if (regionDir != string.Empty)
547 regionDir = ArchiveConstants.REGIONS_PATH + regionDir + "/";
548
549 m_log.InfoFormat("[ARCHIVER]: Adding region settings to archive.");
550
551 // Write out region settings
552 string settingsPath = String.Format("{0}{1}{2}.xml",
553 regionDir, ArchiveConstants.SETTINGS_PATH, scene.RegionInfo.RegionName);
554 m_archiveWriter.WriteFile(settingsPath, RegionSettingsSerializer.Serialize(scene.RegionInfo.RegionSettings));
555
556 m_log.InfoFormat("[ARCHIVER]: Adding parcel settings to archive.");
557
558 // Write out land data (aka parcel) settings
559 List<ILandObject> landObjects = scene.LandChannel.AllParcels();
560 foreach (ILandObject lo in landObjects)
561 {
562 LandData landData = lo.LandData;
563 string landDataPath = String.Format("{0}{1}{2}.xml",
564 regionDir, ArchiveConstants.LANDDATA_PATH, landData.GlobalID.ToString());
565 m_archiveWriter.WriteFile(landDataPath, LandDataSerializer.Serialize(landData, m_options));
566 }
567
568 m_log.InfoFormat("[ARCHIVER]: Adding terrain information to archive.");
569
570 // Write out terrain
571 string terrainPath = String.Format("{0}{1}{2}.r32",
572 regionDir, ArchiveConstants.TERRAINS_PATH, scene.RegionInfo.RegionName);
573
574 MemoryStream ms = new MemoryStream();
575 scene.RequestModuleInterface<ITerrainModule>().SaveToStream(terrainPath, ms);
576 m_archiveWriter.WriteFile(terrainPath, ms.ToArray());
577 ms.Close();
578
579 m_log.InfoFormat("[ARCHIVER]: Adding scene objects to archive.");
580
581 // Write out scene object metadata
582 IRegionSerialiserModule serializer = scene.RequestModuleInterface<IRegionSerialiserModule>();
583 foreach (SceneObjectGroup sceneObject in sceneObjects)
584 {
585 //m_log.DebugFormat("[ARCHIVER]: Saving {0} {1}, {2}", entity.Name, entity.UUID, entity.GetType());
586
587 string serializedObject = serializer.SerializeGroupToXml2(sceneObject, m_options);
588 string objectPath = string.Format("{0}{1}", regionDir, ArchiveHelpers.CreateObjectPath(sceneObject));
589 m_archiveWriter.WriteFile(objectPath, serializedObject);
590 }
591 }
592
593 protected void ReceivedAllAssets(
594 ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids)
595 {
596 foreach (UUID uuid in assetsNotFoundUuids)
597 {
598 m_log.DebugFormat("[ARCHIVER]: Could not find asset {0}", uuid);
599 }
600
601 // m_log.InfoFormat(
602 // "[ARCHIVER]: Received {0} of {1} assets requested",
603 // assetsFoundUuids.Count, assetsFoundUuids.Count + assetsNotFoundUuids.Count);
604
605 CloseArchive(String.Empty);
606 }
607
608
609 /// <summary>
610 /// Closes the archive and notifies that we're done.
611 /// </summary>
612 /// <param name="errorMessage">The error that occurred, or empty for success</param>
613 protected void CloseArchive(string errorMessage)
614 {
615 try
616 {
617 if (m_archiveWriter != null)
618 m_archiveWriter.Close();
619 m_saveStream.Close();
620 }
621 catch (Exception e)
622 {
623 m_log.Error(string.Format("[ARCHIVER]: Error closing archive: {0} ", e.Message), e);
624 if (errorMessage == string.Empty)
625 errorMessage = e.Message;
626 }
627
628 m_log.InfoFormat("[ARCHIVER]: Finished writing out OAR for {0}", m_rootScene.RegionInfo.RegionName);
629
630 m_rootScene.EventManager.TriggerOarFileSaved(m_requestId, errorMessage);
631 }
632
633 }
634}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs
deleted file mode 100644
index 0780d86..0000000
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs
+++ /dev/null
@@ -1,153 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Reflection;
32using System.Xml;
33using log4net;
34using OpenMetaverse;
35using OpenSim.Framework;
36using OpenSim.Framework.Serialization;
37using OpenSim.Framework.Serialization.External;
38using OpenSim.Region.CoreModules.World.Terrain;
39using OpenSim.Region.Framework.Interfaces;
40using OpenSim.Region.Framework.Scenes;
41
42namespace OpenSim.Region.CoreModules.World.Archiver
43{
44 /// <summary>
45 /// Method called when all the necessary assets for an archive request have been received.
46 /// </summary>
47 public delegate void AssetsRequestCallback(
48 ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids);
49
50 /// <summary>
51 /// Execute the write of an archive once we have received all the necessary data
52 /// </summary>
53 public class ArchiveWriteRequestExecution
54 {
55 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
56
57 protected ITerrainModule m_terrainModule;
58 protected IRegionSerialiserModule m_serialiser;
59 protected List<SceneObjectGroup> m_sceneObjects;
60 protected Scene m_scene;
61 protected TarArchiveWriter m_archiveWriter;
62 protected Guid m_requestId;
63 protected Dictionary<string, object> m_options;
64
65 public ArchiveWriteRequestExecution(
66 List<SceneObjectGroup> sceneObjects,
67 ITerrainModule terrainModule,
68 IRegionSerialiserModule serialiser,
69 Scene scene,
70 TarArchiveWriter archiveWriter,
71 Guid requestId,
72 Dictionary<string, object> options)
73 {
74 m_sceneObjects = sceneObjects;
75 m_terrainModule = terrainModule;
76 m_serialiser = serialiser;
77 m_scene = scene;
78 m_archiveWriter = archiveWriter;
79 m_requestId = requestId;
80 m_options = options;
81 }
82
83 protected internal void ReceivedAllAssets(
84 ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids)
85 {
86 try
87 {
88 Save(assetsFoundUuids, assetsNotFoundUuids);
89 }
90 finally
91 {
92 m_archiveWriter.Close();
93 }
94
95 m_log.InfoFormat("[ARCHIVER]: Finished writing out OAR for {0}", m_scene.RegionInfo.RegionName);
96
97 m_scene.EventManager.TriggerOarFileSaved(m_requestId, String.Empty);
98 }
99
100 protected internal void Save(ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids)
101 {
102 foreach (UUID uuid in assetsNotFoundUuids)
103 {
104 m_log.DebugFormat("[ARCHIVER]: Could not find asset {0}", uuid);
105 }
106
107// m_log.InfoFormat(
108// "[ARCHIVER]: Received {0} of {1} assets requested",
109// assetsFoundUuids.Count, assetsFoundUuids.Count + assetsNotFoundUuids.Count);
110
111 m_log.InfoFormat("[ARCHIVER]: Adding region settings to archive.");
112
113 // Write out region settings
114 string settingsPath
115 = String.Format("{0}{1}.xml", ArchiveConstants.SETTINGS_PATH, m_scene.RegionInfo.RegionName);
116 m_archiveWriter.WriteFile(settingsPath, RegionSettingsSerializer.Serialize(m_scene.RegionInfo.RegionSettings));
117
118 m_log.InfoFormat("[ARCHIVER]: Adding parcel settings to archive.");
119
120 // Write out land data (aka parcel) settings
121 List<ILandObject>landObjects = m_scene.LandChannel.AllParcels();
122 foreach (ILandObject lo in landObjects)
123 {
124 LandData landData = lo.LandData;
125 string landDataPath = String.Format("{0}{1}.xml", ArchiveConstants.LANDDATA_PATH,
126 landData.GlobalID.ToString());
127 m_archiveWriter.WriteFile(landDataPath, LandDataSerializer.Serialize(landData, m_options));
128 }
129
130 m_log.InfoFormat("[ARCHIVER]: Adding terrain information to archive.");
131
132 // Write out terrain
133 string terrainPath
134 = String.Format("{0}{1}.r32", ArchiveConstants.TERRAINS_PATH, m_scene.RegionInfo.RegionName);
135
136 MemoryStream ms = new MemoryStream();
137 m_terrainModule.SaveToStream(terrainPath, ms);
138 m_archiveWriter.WriteFile(terrainPath, ms.ToArray());
139 ms.Close();
140
141 m_log.InfoFormat("[ARCHIVER]: Adding scene objects to archive.");
142
143 // Write out scene object metadata
144 foreach (SceneObjectGroup sceneObject in m_sceneObjects)
145 {
146 //m_log.DebugFormat("[ARCHIVER]: Saving {0} {1}, {2}", entity.Name, entity.UUID, entity.GetType());
147
148 string serializedObject = m_serialiser.SerializeGroupToXml2(sceneObject, m_options);
149 m_archiveWriter.WriteFile(ArchiveHelpers.CreateObjectPath(sceneObject), serializedObject);
150 }
151 }
152 }
153} \ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs
deleted file mode 100644
index 4edaaca..0000000
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs
+++ /dev/null
@@ -1,438 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.IO.Compression;
32using System.Reflection;
33using System.Text.RegularExpressions;
34using System.Threading;
35using System.Xml;
36using log4net;
37using OpenMetaverse;
38using OpenSim.Framework;
39using OpenSim.Framework.Serialization;
40using OpenSim.Region.CoreModules.World.Terrain;
41using OpenSim.Region.Framework.Interfaces;
42using OpenSim.Region.Framework.Scenes;
43using Ionic.Zlib;
44using GZipStream = Ionic.Zlib.GZipStream;
45using CompressionMode = Ionic.Zlib.CompressionMode;
46
47namespace OpenSim.Region.CoreModules.World.Archiver
48{
49 /// <summary>
50 /// Prepare to write out an archive.
51 /// </summary>
52 public class ArchiveWriteRequestPreparation
53 {
54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
55
56 /// <summary>
57 /// The minimum major version of OAR that we can write.
58 /// </summary>
59 public static int MIN_MAJOR_VERSION = 0;
60
61 /// <summary>
62 /// The maximum major version of OAR that we can write.
63 /// </summary>
64 public static int MAX_MAJOR_VERSION = 0;
65
66 /// <summary>
67 /// Determine whether this archive will save assets. Default is true.
68 /// </summary>
69 public bool SaveAssets { get; set; }
70
71 protected ArchiverModule m_module;
72 protected Scene m_scene;
73 protected Stream m_saveStream;
74 protected Guid m_requestId;
75
76 /// <summary>
77 /// Constructor
78 /// </summary>
79 /// <param name="module">Calling module</param>
80 /// <param name="savePath">The path to which to save data.</param>
81 /// <param name="requestId">The id associated with this request</param>
82 /// <exception cref="System.IO.IOException">
83 /// If there was a problem opening a stream for the file specified by the savePath
84 /// </exception>
85 public ArchiveWriteRequestPreparation(ArchiverModule module, string savePath, Guid requestId) : this(module, requestId)
86 {
87 try
88 {
89 m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress, CompressionLevel.BestCompression);
90 }
91 catch (EntryPointNotFoundException e)
92 {
93 m_log.ErrorFormat(
94 "[ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
95 + "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
96 m_log.ErrorFormat("{0} {1}", e.Message, e.StackTrace);
97 }
98 }
99
100 /// <summary>
101 /// Constructor.
102 /// </summary>
103 /// <param name="module">Calling module</param>
104 /// <param name="saveStream">The stream to which to save data.</param>
105 /// <param name="requestId">The id associated with this request</param>
106 public ArchiveWriteRequestPreparation(ArchiverModule module, Stream saveStream, Guid requestId) : this(module, requestId)
107 {
108 m_saveStream = saveStream;
109 }
110
111 protected ArchiveWriteRequestPreparation(ArchiverModule module, Guid requestId)
112 {
113 m_module = module;
114
115 // FIXME: This is only here for regression test purposes since they do not supply a module. Need to fix
116 // this.
117 if (m_module != null)
118 m_scene = m_module.Scene;
119
120 m_requestId = requestId;
121
122 SaveAssets = true;
123 }
124
125 /// <summary>
126 /// Archive the region requested.
127 /// </summary>
128 /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception>
129 public void ArchiveRegion(Dictionary<string, object> options)
130 {
131 if (options.ContainsKey("noassets") && (bool)options["noassets"])
132 SaveAssets = false;
133
134 try
135 {
136 Dictionary<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>();
137
138 EntityBase[] entities = m_scene.GetEntities();
139 List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>();
140
141 string checkPermissions = null;
142 int numObjectsSkippedPermissions = 0;
143 Object temp;
144 if (options.TryGetValue("checkPermissions", out temp))
145 checkPermissions = (string)temp;
146
147 // Filter entities so that we only have scene objects.
148 // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods
149 // end up having to do this
150 foreach (EntityBase entity in entities)
151 {
152 if (entity is SceneObjectGroup)
153 {
154 SceneObjectGroup sceneObject = (SceneObjectGroup)entity;
155
156 if (!sceneObject.IsDeleted && !sceneObject.IsAttachment)
157 {
158 if (!CanUserArchiveObject(m_scene.RegionInfo.EstateSettings.EstateOwner, sceneObject, checkPermissions))
159 {
160 // The user isn't allowed to copy/transfer this object, so it will not be included in the OAR.
161 ++numObjectsSkippedPermissions;
162 }
163 else
164 {
165 sceneObjects.Add(sceneObject);
166 }
167 }
168 }
169 }
170
171 if (SaveAssets)
172 {
173 UuidGatherer assetGatherer = new UuidGatherer(m_scene.AssetService);
174
175 foreach (SceneObjectGroup sceneObject in sceneObjects)
176 {
177 assetGatherer.GatherAssetUuids(sceneObject, assetUuids);
178 }
179
180 m_log.DebugFormat(
181 "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets",
182 sceneObjects.Count, assetUuids.Count);
183 }
184 else
185 {
186 m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified");
187 }
188
189 if (numObjectsSkippedPermissions > 0)
190 {
191 m_log.DebugFormat(
192 "[ARCHIVER]: {0} scene objects skipped due to lack of permissions",
193 numObjectsSkippedPermissions);
194 }
195
196 // Make sure that we also request terrain texture assets
197 RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings;
198
199 if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1)
200 assetUuids[regionSettings.TerrainTexture1] = AssetType.Texture;
201
202 if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2)
203 assetUuids[regionSettings.TerrainTexture2] = AssetType.Texture;
204
205 if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3)
206 assetUuids[regionSettings.TerrainTexture3] = AssetType.Texture;
207
208 if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4)
209 assetUuids[regionSettings.TerrainTexture4] = AssetType.Texture;
210
211 TarArchiveWriter archiveWriter = new TarArchiveWriter(m_saveStream);
212
213 // Asynchronously request all the assets required to perform this archive operation
214 ArchiveWriteRequestExecution awre
215 = new ArchiveWriteRequestExecution(
216 sceneObjects,
217 m_scene.RequestModuleInterface<ITerrainModule>(),
218 m_scene.RequestModuleInterface<IRegionSerialiserModule>(),
219 m_scene,
220 archiveWriter,
221 m_requestId,
222 options);
223
224 m_log.InfoFormat("[ARCHIVER]: Creating archive file. This may take some time.");
225
226 // Write out control file. This has to be done first so that subsequent loaders will see this file first
227 // XXX: I know this is a weak way of doing it since external non-OAR aware tar executables will not do this
228 archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(options));
229 m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");
230
231 if (SaveAssets)
232 {
233 AssetsRequest ar
234 = new AssetsRequest(
235 new AssetsArchiver(archiveWriter), assetUuids,
236 m_scene.AssetService, m_scene.UserAccountService,
237 m_scene.RegionInfo.ScopeID, options, awre.ReceivedAllAssets);
238
239 Util.FireAndForget(o => ar.Execute());
240 }
241 else
242 {
243 awre.ReceivedAllAssets(new List<UUID>(), new List<UUID>());
244 }
245 }
246 catch (Exception)
247 {
248 m_saveStream.Close();
249 throw;
250 }
251 }
252
253 /// <summary>
254 /// Checks whether the user has permission to export an object group to an OAR.
255 /// </summary>
256 /// <param name="user">The user</param>
257 /// <param name="objGroup">The object group</param>
258 /// <param name="checkPermissions">Which permissions to check: "C" = Copy, "T" = Transfer</param>
259 /// <returns>Whether the user is allowed to export the object to an OAR</returns>
260 private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions)
261 {
262 if (checkPermissions == null)
263 return true;
264
265 IPermissionsModule module = m_scene.RequestModuleInterface<IPermissionsModule>();
266 if (module == null)
267 return true; // this shouldn't happen
268
269 // Check whether the user is permitted to export all of the parts in the SOG. If any
270 // part can't be exported then the entire SOG can't be exported.
271
272 bool permitted = true;
273 //int primNumber = 1;
274
275 foreach (SceneObjectPart obj in objGroup.Parts)
276 {
277 uint perm;
278 PermissionClass permissionClass = module.GetPermissionClass(user, obj);
279 switch (permissionClass)
280 {
281 case PermissionClass.Owner:
282 perm = obj.BaseMask;
283 break;
284 case PermissionClass.Group:
285 perm = obj.GroupMask | obj.EveryoneMask;
286 break;
287 case PermissionClass.Everyone:
288 default:
289 perm = obj.EveryoneMask;
290 break;
291 }
292
293 bool canCopy = (perm & (uint)PermissionMask.Copy) != 0;
294 bool canTransfer = (perm & (uint)PermissionMask.Transfer) != 0;
295
296 // Special case: if Everyone can copy the object then this implies it can also be
297 // Transferred.
298 // However, if the user is the Owner then we don't check EveryoneMask, because it seems that the mask
299 // always (incorrectly) includes the Copy bit set in this case. But that's a mistake: the viewer
300 // does NOT show that the object has Everyone-Copy permissions, and doesn't allow it to be copied.
301 if (permissionClass != PermissionClass.Owner)
302 canTransfer |= (obj.EveryoneMask & (uint)PermissionMask.Copy) != 0;
303
304 bool partPermitted = true;
305 if (checkPermissions.Contains("C") && !canCopy)
306 partPermitted = false;
307 if (checkPermissions.Contains("T") && !canTransfer)
308 partPermitted = false;
309
310 // If the user is the Creator of the object then it can always be included in the OAR
311 bool creator = (obj.CreatorID.Guid == user.Guid);
312 if (creator)
313 partPermitted = true;
314
315 //string name = (objGroup.PrimCount == 1) ? objGroup.Name : string.Format("{0} ({1}/{2})", obj.Name, primNumber, objGroup.PrimCount);
316 //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}",
317 // name, obj.BaseMask, obj.OwnerMask, obj.EveryoneMask,
318 // permissionClass, checkPermissions, canCopy, canTransfer, creator, partPermitted);
319
320 if (!partPermitted)
321 {
322 permitted = false;
323 break;
324 }
325
326 //++primNumber;
327 }
328
329 return permitted;
330 }
331
332 /// <summary>
333 /// Create the control file for the most up to date archive
334 /// </summary>
335 /// <returns></returns>
336 public string CreateControlFile(Dictionary<string, object> options)
337 {
338 int majorVersion = MAX_MAJOR_VERSION, minorVersion = 8;
339//
340// if (options.ContainsKey("version"))
341// {
342// string[] parts = options["version"].ToString().Split('.');
343// if (parts.Length >= 1)
344// {
345// majorVersion = Int32.Parse(parts[0]);
346//
347// if (parts.Length >= 2)
348// minorVersion = Int32.Parse(parts[1]);
349// }
350// }
351//
352// if (majorVersion < MIN_MAJOR_VERSION || majorVersion > MAX_MAJOR_VERSION)
353// {
354// throw new Exception(
355// string.Format(
356// "OAR version number for save must be between {0} and {1}",
357// MIN_MAJOR_VERSION, MAX_MAJOR_VERSION));
358// }
359// else if (majorVersion == MAX_MAJOR_VERSION)
360// {
361// // Force 1.0
362// minorVersion = 0;
363// }
364// else if (majorVersion == MIN_MAJOR_VERSION)
365// {
366// // Force 0.4
367// minorVersion = 4;
368// }
369
370 m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion);
371 //if (majorVersion == 1)
372 //{
373 // 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");
374 //}
375
376 String s;
377
378 using (StringWriter sw = new StringWriter())
379 {
380 using (XmlTextWriter xtw = new XmlTextWriter(sw))
381 {
382 xtw.Formatting = Formatting.Indented;
383 xtw.WriteStartDocument();
384 xtw.WriteStartElement("archive");
385 xtw.WriteAttributeString("major_version", majorVersion.ToString());
386 xtw.WriteAttributeString("minor_version", minorVersion.ToString());
387
388 xtw.WriteStartElement("creation_info");
389 DateTime now = DateTime.UtcNow;
390 TimeSpan t = now - new DateTime(1970, 1, 1);
391 xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString());
392 xtw.WriteElementString("id", UUID.Random().ToString());
393 xtw.WriteEndElement();
394
395 xtw.WriteStartElement("region_info");
396
397 bool isMegaregion;
398 Vector2 size;
399 IRegionCombinerModule rcMod = null;
400
401 // FIXME: This is only here for regression test purposes since they do not supply a module. Need to fix
402 // this, possibly by doing control file creation somewhere else.
403 if (m_module != null)
404 rcMod = m_module.RegionCombinerModule;
405
406 if (rcMod != null)
407 isMegaregion = rcMod.IsRootForMegaregion(m_scene.RegionInfo.RegionID);
408 else
409 isMegaregion = false;
410
411 if (isMegaregion)
412 size = rcMod.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID);
413 else
414 size = new Vector2((float)Constants.RegionSize, (float)Constants.RegionSize);
415
416 xtw.WriteElementString("is_megaregion", isMegaregion.ToString());
417 xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y));
418
419 xtw.WriteEndElement();
420
421 xtw.WriteElementString("assets_included", SaveAssets.ToString());
422
423 xtw.WriteEndElement();
424
425 xtw.Flush();
426 }
427
428 s = sw.ToString();
429 }
430
431// if (m_scene != null)
432// Console.WriteLine(
433// "[ARCHIVE WRITE REQUEST PREPARATION]: Control file for {0} is: {1}", m_scene.RegionInfo.RegionName, s);
434
435 return s;
436 }
437 }
438}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs
index bf3b124..2a87dc2 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs
@@ -146,6 +146,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
146 ops.Add("noassets", delegate(string v) { options["noassets"] = v != null; }); 146 ops.Add("noassets", delegate(string v) { options["noassets"] = v != null; });
147 ops.Add("publish", v => options["wipe-owners"] = v != null); 147 ops.Add("publish", v => options["wipe-owners"] = v != null);
148 ops.Add("perm=", delegate(string v) { options["checkPermissions"] = v; }); 148 ops.Add("perm=", delegate(string v) { options["checkPermissions"] = v; });
149 ops.Add("all", delegate(string v) { options["all"] = v != null; });
149 150
150 List<string> mainParams = ops.Parse(cmdparams); 151 List<string> mainParams = ops.Parse(cmdparams);
151 152
@@ -169,7 +170,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
169 m_log.InfoFormat( 170 m_log.InfoFormat(
170 "[ARCHIVER]: Writing archive for region {0} to {1}", Scene.RegionInfo.RegionName, savePath); 171 "[ARCHIVER]: Writing archive for region {0} to {1}", Scene.RegionInfo.RegionName, savePath);
171 172
172 new ArchiveWriteRequestPreparation(this, savePath, requestId).ArchiveRegion(options); 173 new ArchiveWriteRequest(Scene, savePath, requestId).ArchiveRegion(options);
173 } 174 }
174 175
175 public void ArchiveRegion(Stream saveStream) 176 public void ArchiveRegion(Stream saveStream)
@@ -184,7 +185,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
184 185
185 public void ArchiveRegion(Stream saveStream, Guid requestId, Dictionary<string, object> options) 186 public void ArchiveRegion(Stream saveStream, Guid requestId, Dictionary<string, object> options)
186 { 187 {
187 new ArchiveWriteRequestPreparation(this, saveStream, requestId).ArchiveRegion(options); 188 new ArchiveWriteRequest(Scene, saveStream, requestId).ArchiveRegion(options);
188 } 189 }
189 190
190 public void DearchiveRegion(string loadPath) 191 public void DearchiveRegion(string loadPath)
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs
index a073cb9..5787279 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs
@@ -46,6 +46,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver
46 { 46 {
47 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 47 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
48 48
49 /// <summary>
50 /// Method called when all the necessary assets for an archive request have been received.
51 /// </summary>
52 public delegate void AssetsRequestCallback(
53 ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids);
54
49 enum RequestState 55 enum RequestState
50 { 56 {
51 Initial, 57 Initial,
diff --git a/OpenSim/Region/CoreModules/World/Archiver/DearchiveScenesGroup.cs b/OpenSim/Region/CoreModules/World/Archiver/DearchiveScenesGroup.cs
new file mode 100644
index 0000000..3dcc020
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/DearchiveScenesGroup.cs
@@ -0,0 +1,232 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Linq;
31using System.Text;
32using OpenSim.Region.Framework.Scenes;
33using OpenMetaverse;
34using System.Drawing;
35using log4net;
36using System.Reflection;
37using OpenSim.Framework.Serialization;
38
39namespace OpenSim.Region.CoreModules.World.Archiver
40{
41 /// <summary>
42 /// The regions included in an OAR file.
43 /// </summary>
44 public class DearchiveScenesInfo
45 {
46 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
47
48 /// <summary>
49 /// One region in the archive.
50 /// </summary>
51 public class RegionInfo
52 {
53 /// <summary>
54 /// The subdirectory in which the region is stored.
55 /// </summary>
56 public string Directory { get; set; }
57
58 /// <summary>
59 /// The region's coordinates (relative to the South-West corner of the block).
60 /// </summary>
61 public Point Location { get; set; }
62
63 /// <summary>
64 /// The UUID of the original scene from which this archived region was saved.
65 /// </summary>
66 public string OriginalID { get; set; }
67
68 /// <summary>
69 /// The scene in the current simulator into which this region is loaded.
70 /// If null then the region doesn't have a corresponding scene, and it won't be loaded.
71 /// </summary>
72 public Scene Scene { get; set; }
73 }
74
75 /// <summary>
76 /// Whether this archive uses the multi-region format.
77 /// </summary>
78 public Boolean MultiRegionFormat { get; set; }
79
80 /// <summary>
81 /// Maps (Region directory -> region)
82 /// </summary>
83 protected Dictionary<string, RegionInfo> m_directory2region = new Dictionary<string, RegionInfo>();
84
85 /// <summary>
86 /// Maps (UUID of the scene in the simulator where the region will be loaded -> region)
87 /// </summary>
88 protected Dictionary<UUID, RegionInfo> m_newId2region = new Dictionary<UUID, RegionInfo>();
89
90 public int LoadedCreationDateTime { get; set; }
91 public string DefaultOriginalID { get; set; }
92
93 // These variables are used while reading the archive control file
94 protected int? m_curY = null;
95 protected int? m_curX = null;
96 protected RegionInfo m_curRegion;
97
98
99 public DearchiveScenesInfo()
100 {
101 MultiRegionFormat = false;
102 }
103
104
105 // The following methods are used while reading the archive control file
106
107 public void StartRow()
108 {
109 m_curY = (m_curY == null) ? 0 : m_curY + 1;
110 m_curX = null;
111 }
112
113 public void StartRegion()
114 {
115 m_curX = (m_curX == null) ? 0 : m_curX + 1;
116 // Note: this doesn't mean we have a real region in this location; this could just be a "hole"
117 }
118
119 public void SetRegionOriginalID(string id)
120 {
121 m_curRegion = new RegionInfo();
122 m_curRegion.Location = new Point((int)m_curX, (int)m_curY);
123 m_curRegion.OriginalID = id;
124 // 'curRegion' will be saved in 'm_directory2region' when SetRegionDir() is called
125 }
126
127 public void SetRegionDirectory(string directory)
128 {
129 m_curRegion.Directory = directory;
130 m_directory2region[directory] = m_curRegion;
131 }
132
133
134 /// <summary>
135 /// Sets all the scenes present in the simulator.
136 /// </summary>
137 /// <remarks>
138 /// This method matches regions in the archive to scenes in the simulator according to
139 /// their relative position. We only load regions if there's an existing Scene in the
140 /// grid location where the region should be loaded.
141 /// </remarks>
142 /// <param name="rootScene">The scene where the Load OAR operation was run</param>
143 /// <param name="simulatorScenes">All the scenes in the simulator</param>
144 public void SetSimulatorScenes(Scene rootScene, ArchiveScenesGroup simulatorScenes)
145 {
146 foreach (RegionInfo archivedRegion in m_directory2region.Values)
147 {
148 Point location = new Point((int)rootScene.RegionInfo.RegionLocX, (int)rootScene.RegionInfo.RegionLocY);
149 location.Offset(archivedRegion.Location);
150
151 Scene scene;
152 if (simulatorScenes.TryGetScene(location, out scene))
153 {
154 archivedRegion.Scene = scene;
155 m_newId2region[scene.RegionInfo.RegionID] = archivedRegion;
156 }
157 else
158 {
159 m_log.WarnFormat("[ARCHIVER]: Not loading archived region {0} because there's no existing region at location {1},{2}",
160 archivedRegion.Directory, location.X, location.Y);
161 }
162 }
163 }
164
165 /// <summary>
166 /// Returns the archived region according to the path of a file in the archive.
167 /// Also, converts the full path into a path that is relative to the region's directory.
168 /// </summary>
169 /// <param name="fullPath">The path of a file in the archive</param>
170 /// <param name="scene">The corresponding Scene, or null if none</param>
171 /// <param name="relativePath">The path relative to the region's directory. (Or the original
172 /// path, if this file doesn't belong to a region.)</param>
173 /// <returns>True: use this file; False: skip it</returns>
174 public bool GetRegionFromPath(string fullPath, out Scene scene, out string relativePath)
175 {
176 scene = null;
177 relativePath = fullPath;
178
179 if (!MultiRegionFormat)
180 {
181 if (m_newId2region.Count > 0)
182 scene = m_newId2region.First().Value.Scene;
183 return true;
184 }
185
186 if (!fullPath.StartsWith(ArchiveConstants.REGIONS_PATH))
187 return true; // this file doesn't belong to a region
188
189 string[] parts = fullPath.Split(new Char[] { '/' }, 3);
190 if (parts.Length != 3)
191 return false;
192 string regionDirectory = parts[1];
193 relativePath = parts[2];
194
195 RegionInfo region;
196 if (m_directory2region.TryGetValue(regionDirectory, out region))
197 {
198 scene = region.Scene;
199 return (scene != null);
200 }
201 else
202 {
203 return false;
204 }
205 }
206
207 /// <summary>
208 /// Returns the original UUID of a region (from the simulator where the OAR was saved),
209 /// given the UUID of the scene it was loaded into in the current simulator.
210 /// </summary>
211 /// <param name="newID"></param>
212 /// <returns></returns>
213 public string GetOriginalRegionID(UUID newID)
214 {
215 RegionInfo region;
216 if (m_newId2region.TryGetValue(newID, out region))
217 return region.OriginalID;
218 else
219 return DefaultOriginalID;
220 }
221
222 /// <summary>
223 /// Returns the scenes that have been (or will be) loaded.
224 /// </summary>
225 /// <returns></returns>
226 public List<UUID> GetLoadedScenes()
227 {
228 return m_newId2region.Keys.ToList();
229 }
230
231 }
232}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
index 904110e..0a30905 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
@@ -47,6 +47,7 @@ using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants;
47using TarArchiveReader = OpenSim.Framework.Serialization.TarArchiveReader; 47using TarArchiveReader = OpenSim.Framework.Serialization.TarArchiveReader;
48using TarArchiveWriter = OpenSim.Framework.Serialization.TarArchiveWriter; 48using TarArchiveWriter = OpenSim.Framework.Serialization.TarArchiveWriter;
49using RegionSettings = OpenSim.Framework.RegionSettings; 49using RegionSettings = OpenSim.Framework.RegionSettings;
50using OpenSim.Region.Framework.Interfaces;
50 51
51namespace OpenSim.Region.CoreModules.World.Archiver.Tests 52namespace OpenSim.Region.CoreModules.World.Archiver.Tests
52{ 53{
@@ -56,23 +57,28 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
56 private Guid m_lastRequestId; 57 private Guid m_lastRequestId;
57 private string m_lastErrorMessage; 58 private string m_lastErrorMessage;
58 59
60 protected SceneHelpers m_sceneHelpers;
59 protected TestScene m_scene; 61 protected TestScene m_scene;
60 protected ArchiverModule m_archiverModule; 62 protected ArchiverModule m_archiverModule;
63 protected SerialiserModule m_serialiserModule;
61 64
62 protected TaskInventoryItem m_soundItem; 65 protected TaskInventoryItem m_soundItem;
63 66
64 [SetUp] 67 [SetUp]
65 public void SetUp() 68 public void SetUp()
66 { 69 {
70 new SceneManager();
71
67 m_archiverModule = new ArchiverModule(); 72 m_archiverModule = new ArchiverModule();
68 SerialiserModule serialiserModule = new SerialiserModule(); 73 m_serialiserModule = new SerialiserModule();
69 TerrainModule terrainModule = new TerrainModule(); 74 TerrainModule terrainModule = new TerrainModule();
70 75
71 m_scene = new SceneHelpers().SetupScene(); 76 m_sceneHelpers = new SceneHelpers();
72 SceneHelpers.SetupSceneModules(m_scene, m_archiverModule, serialiserModule, terrainModule); 77 m_scene = m_sceneHelpers.SetupScene();
78 SceneHelpers.SetupSceneModules(m_scene, m_archiverModule, m_serialiserModule, terrainModule);
73 } 79 }
74 80
75 private void LoadCompleted(Guid requestId, string errorMessage) 81 private void LoadCompleted(Guid requestId, List<UUID> loadedScenes, string errorMessage)
76 { 82 {
77 lock (this) 83 lock (this)
78 { 84 {
@@ -128,26 +134,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
128 TestHelpers.InMethod(); 134 TestHelpers.InMethod();
129// log4net.Config.XmlConfigurator.Configure(); 135// log4net.Config.XmlConfigurator.Configure();
130 136
131 SceneObjectPart part1 = CreateSceneObjectPart1(); 137 SceneObjectGroup sog1;
132 SceneObjectGroup sog1 = new SceneObjectGroup(part1); 138 SceneObjectGroup sog2;
133 m_scene.AddNewSceneObject(sog1, false); 139 UUID ncAssetUuid;
134 140 CreateTestObjects(m_scene, out sog1, out sog2, out ncAssetUuid);
135 SceneObjectPart part2 = CreateSceneObjectPart2();
136
137 AssetNotecard nc = new AssetNotecard();
138 nc.BodyText = "Hello World!";
139 nc.Encode();
140 UUID ncAssetUuid = new UUID("00000000-0000-0000-1000-000000000000");
141 UUID ncItemUuid = new UUID("00000000-0000-0000-1100-000000000000");
142 AssetBase ncAsset
143 = AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero);
144 m_scene.AssetService.Store(ncAsset);
145 SceneObjectGroup sog2 = new SceneObjectGroup(part2);
146 TaskInventoryItem ncItem
147 = new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid };
148 part2.Inventory.AddInventoryItem(ncItem, true);
149
150 m_scene.AddNewSceneObject(sog2, false);
151 141
152 MemoryStream archiveWriteStream = new MemoryStream(); 142 MemoryStream archiveWriteStream = new MemoryStream();
153 m_scene.EventManager.OnOarFileSaved += SaveCompleted; 143 m_scene.EventManager.OnOarFileSaved += SaveCompleted;
@@ -186,7 +176,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
186 Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH)); 176 Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
187 177
188 ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty); 178 ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
189 arr.LoadControlFile(filePath, data); 179 arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
190 180
191 Assert.That(arr.ControlFileLoaded, Is.True); 181 Assert.That(arr.ControlFileLoaded, Is.True);
192 182
@@ -211,6 +201,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
211 // TODO: Test presence of more files and contents of files. 201 // TODO: Test presence of more files and contents of files.
212 } 202 }
213 203
204 private void CreateTestObjects(Scene scene, out SceneObjectGroup sog1, out SceneObjectGroup sog2, out UUID ncAssetUuid)
205 {
206 SceneObjectPart part1 = CreateSceneObjectPart1();
207 sog1 = new SceneObjectGroup(part1);
208 scene.AddNewSceneObject(sog1, false);
209
210 AssetNotecard nc = new AssetNotecard();
211 nc.BodyText = "Hello World!";
212 nc.Encode();
213 ncAssetUuid = UUID.Random();
214 UUID ncItemUuid = UUID.Random();
215 AssetBase ncAsset
216 = AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero);
217 m_scene.AssetService.Store(ncAsset);
218
219 TaskInventoryItem ncItem
220 = new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid };
221 SceneObjectPart part2 = CreateSceneObjectPart2();
222 sog2 = new SceneObjectGroup(part2);
223 part2.Inventory.AddInventoryItem(ncItem, true);
224
225 scene.AddNewSceneObject(sog2, false);
226 }
227
214 /// <summary> 228 /// <summary>
215 /// Test saving an OpenSim Region Archive with the no assets option 229 /// Test saving an OpenSim Region Archive with the no assets option
216 /// </summary> 230 /// </summary>
@@ -270,7 +284,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
270 Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH)); 284 Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
271 285
272 ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty); 286 ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
273 arr.LoadControlFile(filePath, data); 287 arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
274 288
275 Assert.That(arr.ControlFileLoaded, Is.True); 289 Assert.That(arr.ControlFileLoaded, Is.True);
276 290
@@ -307,7 +321,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
307 321
308 tar.WriteFile( 322 tar.WriteFile(
309 ArchiveConstants.CONTROL_FILE_PATH, 323 ArchiveConstants.CONTROL_FILE_PATH,
310 new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary<string, Object>())); 324 new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
311 325
312 SceneObjectGroup sog1 = SceneHelpers.CreateSceneObject(1, ownerId, "obj1-", 0x11); 326 SceneObjectGroup sog1 = SceneHelpers.CreateSceneObject(1, ownerId, "obj1-", 0x11);
313 SceneObjectPart sop2 327 SceneObjectPart sop2
@@ -362,11 +376,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
362 // Also check that direct entries which will also have a file entry containing that directory doesn't 376 // Also check that direct entries which will also have a file entry containing that directory doesn't
363 // upset load 377 // upset load
364 tar.WriteDir(ArchiveConstants.TERRAINS_PATH); 378 tar.WriteDir(ArchiveConstants.TERRAINS_PATH);
365 379
366 tar.WriteFile( 380 tar.WriteFile(
367 ArchiveConstants.CONTROL_FILE_PATH, 381 ArchiveConstants.CONTROL_FILE_PATH,
368 new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary<string, Object>())); 382 new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
369
370 SceneObjectPart part1 = CreateSceneObjectPart1(); 383 SceneObjectPart part1 = CreateSceneObjectPart1();
371 384
372 part1.SitTargetOrientation = new Quaternion(0.2f, 0.3f, 0.4f, 0.5f); 385 part1.SitTargetOrientation = new Quaternion(0.2f, 0.3f, 0.4f, 0.5f);
@@ -389,31 +402,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
389 Assert.That(soundDataResourceName, Is.Not.Null); 402 Assert.That(soundDataResourceName, Is.Not.Null);
390 403
391 byte[] soundData; 404 byte[] soundData;
392 Console.WriteLine("Loading " + soundDataResourceName); 405 UUID soundUuid;
393 using (Stream resource = assembly.GetManifestResourceStream(soundDataResourceName)) 406 CreateSoundAsset(tar, assembly, soundDataResourceName, out soundData, out soundUuid);
394 { 407
395 using (BinaryReader br = new BinaryReader(resource)) 408 TaskInventoryItem item1
396 { 409 = new TaskInventoryItem { AssetID = soundUuid, ItemID = soundItemUuid, Name = soundItemName };
397 // FIXME: Use the inspector instead 410 part1.Inventory.AddInventoryItem(item1, true);
398 soundData = br.ReadBytes(99999999);
399 UUID soundUuid = UUID.Parse("00000000-0000-0000-0000-000000000001");
400 string soundAssetFileName
401 = ArchiveConstants.ASSETS_PATH + soundUuid
402 + ArchiveConstants.ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV];
403 tar.WriteFile(soundAssetFileName, soundData);
404
405 /*
406 AssetBase soundAsset = AssetHelpers.CreateAsset(soundUuid, soundData);
407 scene.AssetService.Store(soundAsset);
408 asset1FileName = ArchiveConstants.ASSETS_PATH + soundUuid + ".wav";
409 */
410
411 TaskInventoryItem item1
412 = new TaskInventoryItem { AssetID = soundUuid, ItemID = soundItemUuid, Name = soundItemName };
413 part1.Inventory.AddInventoryItem(item1, true);
414 }
415 }
416
417 m_scene.AddNewSceneObject(object1, false); 411 m_scene.AddNewSceneObject(object1, false);
418 412
419 string object1FileName = string.Format( 413 string object1FileName = string.Format(
@@ -435,6 +429,34 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
435 429
436 Assert.That(m_lastErrorMessage, Is.Null); 430 Assert.That(m_lastErrorMessage, Is.Null);
437 431
432 TestLoadedRegion(part1, soundItemName, soundData);
433 }
434
435 private static void CreateSoundAsset(TarArchiveWriter tar, Assembly assembly, string soundDataResourceName, out byte[] soundData, out UUID soundUuid)
436 {
437 using (Stream resource = assembly.GetManifestResourceStream(soundDataResourceName))
438 {
439 using (BinaryReader br = new BinaryReader(resource))
440 {
441 // FIXME: Use the inspector instead
442 soundData = br.ReadBytes(99999999);
443 soundUuid = UUID.Parse("00000000-0000-0000-0000-000000000001");
444 string soundAssetFileName
445 = ArchiveConstants.ASSETS_PATH + soundUuid
446 + ArchiveConstants.ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV];
447 tar.WriteFile(soundAssetFileName, soundData);
448
449 /*
450 AssetBase soundAsset = AssetHelpers.CreateAsset(soundUuid, soundData);
451 scene.AssetService.Store(soundAsset);
452 asset1FileName = ArchiveConstants.ASSETS_PATH + soundUuid + ".wav";
453 */
454 }
455 }
456 }
457
458 private void TestLoadedRegion(SceneObjectPart part1, string soundItemName, byte[] soundData)
459 {
438 SceneObjectPart object1PartLoaded = m_scene.GetSceneObjectPart(part1.Name); 460 SceneObjectPart object1PartLoaded = m_scene.GetSceneObjectPart(part1.Name);
439 461
440 Assert.That(object1PartLoaded, Is.Not.Null, "object1 was not loaded"); 462 Assert.That(object1PartLoaded, Is.Not.Null, "object1 was not loaded");
@@ -454,9 +476,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
454 Assert.That(loadedSoundAsset.Data, Is.EqualTo(soundData), "saved and loaded sound data do not match"); 476 Assert.That(loadedSoundAsset.Data, Is.EqualTo(soundData), "saved and loaded sound data do not match");
455 477
456 Assert.Greater(m_scene.LandChannel.AllParcels().Count, 0, "incorrect number of parcels"); 478 Assert.Greater(m_scene.LandChannel.AllParcels().Count, 0, "incorrect number of parcels");
457
458 // Temporary
459 Console.WriteLine("Successfully completed {0}", MethodBase.GetCurrentMethod());
460 } 479 }
461 480
462 /// <summary> 481 /// <summary>
@@ -516,7 +535,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
516 SerialiserModule serialiserModule = new SerialiserModule(); 535 SerialiserModule serialiserModule = new SerialiserModule();
517 TerrainModule terrainModule = new TerrainModule(); 536 TerrainModule terrainModule = new TerrainModule();
518 537
519 TestScene scene2 = new SceneHelpers().SetupScene(); 538 m_sceneHelpers = new SceneHelpers();
539 TestScene scene2 = m_sceneHelpers.SetupScene();
520 SceneHelpers.SetupSceneModules(scene2, archiverModule, serialiserModule, terrainModule); 540 SceneHelpers.SetupSceneModules(scene2, archiverModule, serialiserModule, terrainModule);
521 541
522 // Make sure there's a valid owner for the owner we saved (this should have been wiped if the code is 542 // 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
554 tar.WriteDir(ArchiveConstants.TERRAINS_PATH); 574 tar.WriteDir(ArchiveConstants.TERRAINS_PATH);
555 tar.WriteFile( 575 tar.WriteFile(
556 ArchiveConstants.CONTROL_FILE_PATH, 576 ArchiveConstants.CONTROL_FILE_PATH,
557 new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary<string, Object>())); 577 new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
558 578
559 RegionSettings rs = new RegionSettings(); 579 RegionSettings rs = new RegionSettings();
560 rs.AgentLimit = 17; 580 rs.AgentLimit = 17;
@@ -664,7 +684,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
664 SerialiserModule serialiserModule = new SerialiserModule(); 684 SerialiserModule serialiserModule = new SerialiserModule();
665 TerrainModule terrainModule = new TerrainModule(); 685 TerrainModule terrainModule = new TerrainModule();
666 686
667 Scene scene = new SceneHelpers().SetupScene(); 687 Scene scene = m_sceneHelpers.SetupScene();
668 SceneHelpers.SetupSceneModules(scene, archiverModule, serialiserModule, terrainModule); 688 SceneHelpers.SetupSceneModules(scene, archiverModule, serialiserModule, terrainModule);
669 689
670 m_scene.AddNewSceneObject(new SceneObjectGroup(part2), false); 690 m_scene.AddNewSceneObject(new SceneObjectGroup(part2), false);
@@ -700,5 +720,258 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
700 Assert.That(object2PartMerged.GroupPosition, Is.EqualTo(part2.GroupPosition), "object2 group position not equal after merge"); 720 Assert.That(object2PartMerged.GroupPosition, Is.EqualTo(part2.GroupPosition), "object2 group position not equal after merge");
701 } 721 }
702 } 722 }
723
724 /// <summary>
725 /// Test saving a multi-region OAR.
726 /// </summary>
727 [Test]
728 public void TestSaveMultiRegionOar()
729 {
730 TestHelpers.InMethod();
731
732 // Create test regions
733
734 int WIDTH = 2;
735 int HEIGHT = 2;
736
737 List<Scene> scenes = new List<Scene>();
738
739 // Maps (Directory in OAR file -> scene)
740 Dictionary<string, Scene> regionPaths = new Dictionary<string, Scene>();
741
742 // Maps (Scene -> expected object paths)
743 Dictionary<UUID, List<string>> expectedPaths = new Dictionary<UUID, List<string>>();
744
745 // List of expected assets
746 List<UUID> expectedAssets = new List<UUID>();
747
748 for (uint y = 0; y < HEIGHT; y++)
749 {
750 for (uint x = 0; x < WIDTH; x++)
751 {
752 Scene scene;
753 if (x == 0 && y == 0)
754 {
755 scene = m_scene; // this scene was already created in SetUp()
756 }
757 else
758 {
759 scene = m_sceneHelpers.SetupScene(string.Format("Unit test region {0}", (y * WIDTH) + x + 1), UUID.Random(), 1000 + x, 1000 + y);
760 SceneHelpers.SetupSceneModules(scene, new ArchiverModule(), m_serialiserModule, new TerrainModule());
761 }
762 scenes.Add(scene);
763
764 string dir = String.Format("{0}_{1}_{2}", x + 1, y + 1, scene.RegionInfo.RegionName.Replace(" ", "_"));
765 regionPaths[dir] = scene;
766
767 SceneObjectGroup sog1;
768 SceneObjectGroup sog2;
769 UUID ncAssetUuid;
770
771 CreateTestObjects(scene, out sog1, out sog2, out ncAssetUuid);
772
773 expectedPaths[scene.RegionInfo.RegionID] = new List<string>();
774 expectedPaths[scene.RegionInfo.RegionID].Add(ArchiveHelpers.CreateObjectPath(sog1));
775 expectedPaths[scene.RegionInfo.RegionID].Add(ArchiveHelpers.CreateObjectPath(sog2));
776
777 expectedAssets.Add(ncAssetUuid);
778 }
779 }
780
781
782 // Save OAR
783
784 MemoryStream archiveWriteStream = new MemoryStream();
785 m_scene.EventManager.OnOarFileSaved += SaveCompleted;
786
787 Guid requestId = new Guid("00000000-0000-0000-0000-808080808080");
788
789 Dictionary<string, Object> options = new Dictionary<string, Object>();
790 options.Add("all", true);
791
792 lock (this)
793 {
794 m_archiverModule.ArchiveRegion(archiveWriteStream, requestId, options);
795 Monitor.Wait(this, 60000);
796 }
797
798
799 // Check that the OAR contains the expected data
800
801 Assert.That(m_lastRequestId, Is.EqualTo(requestId));
802
803 byte[] archive = archiveWriteStream.ToArray();
804 MemoryStream archiveReadStream = new MemoryStream(archive);
805 TarArchiveReader tar = new TarArchiveReader(archiveReadStream);
806
807 Dictionary<UUID, List<string>> foundPaths = new Dictionary<UUID, List<string>>();
808 List<UUID> foundAssets = new List<UUID>();
809
810 foreach (Scene scene in scenes)
811 {
812 foundPaths[scene.RegionInfo.RegionID] = new List<string>();
813 }
814
815 string filePath;
816 TarArchiveReader.TarEntryType tarEntryType;
817
818 byte[] data = tar.ReadEntry(out filePath, out tarEntryType);
819 Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
820
821 ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
822 arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
823
824 Assert.That(arr.ControlFileLoaded, Is.True);
825
826 while (tar.ReadEntry(out filePath, out tarEntryType) != null)
827 {
828 if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH))
829 {
830 // Assets are shared, so this file doesn't belong to any specific region.
831 string fileName = filePath.Remove(0, ArchiveConstants.ASSETS_PATH.Length);
832 if (fileName.EndsWith("_notecard.txt"))
833 foundAssets.Add(UUID.Parse(fileName.Substring(0, fileName.Length - "_notecard.txt".Length)));
834 }
835 else
836 {
837 // This file belongs to one of the regions. Find out which one.
838 Assert.IsTrue(filePath.StartsWith(ArchiveConstants.REGIONS_PATH));
839 string[] parts = filePath.Split(new Char[] { '/' }, 3);
840 Assert.AreEqual(3, parts.Length);
841 string regionDirectory = parts[1];
842 string relativePath = parts[2];
843 Scene scene = regionPaths[regionDirectory];
844
845 if (relativePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
846 {
847 foundPaths[scene.RegionInfo.RegionID].Add(relativePath);
848 }
849 }
850 }
851
852 Assert.AreEqual(scenes.Count, foundPaths.Count);
853 foreach (Scene scene in scenes)
854 {
855 Assert.That(foundPaths[scene.RegionInfo.RegionID], Is.EquivalentTo(expectedPaths[scene.RegionInfo.RegionID]));
856 }
857
858 Assert.That(foundAssets, Is.EquivalentTo(expectedAssets));
859 }
860
861 /// <summary>
862 /// Test loading a multi-region OAR.
863 /// </summary>
864 [Test]
865 public void TestLoadMultiRegionOar()
866 {
867 TestHelpers.InMethod();
868
869 // Create an ArchiveScenesGroup with the regions in the OAR. This is needed to generate the control file.
870
871 int WIDTH = 2;
872 int HEIGHT = 2;
873
874 for (uint y = 0; y < HEIGHT; y++)
875 {
876 for (uint x = 0; x < WIDTH; x++)
877 {
878 Scene scene;
879 if (x == 0 && y == 0)
880 {
881 scene = m_scene; // this scene was already created in SetUp()
882 }
883 else
884 {
885 scene = m_sceneHelpers.SetupScene(string.Format("Unit test region {0}", (y * WIDTH) + x + 1), UUID.Random(), 1000 + x, 1000 + y);
886 SceneHelpers.SetupSceneModules(scene, new ArchiverModule(), m_serialiserModule, new TerrainModule());
887 }
888 }
889 }
890
891 ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();
892 SceneManager.Instance.ForEachScene(delegate(Scene scene)
893 {
894 scenesGroup.AddScene(scene);
895 });
896 scenesGroup.CalcSceneLocations();
897
898 // Generate the OAR file
899
900 MemoryStream archiveWriteStream = new MemoryStream();
901 TarArchiveWriter tar = new TarArchiveWriter(archiveWriteStream);
902
903 ArchiveWriteRequest writeRequest = new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty);
904 writeRequest.MultiRegionFormat = true;
905 tar.WriteFile(
906 ArchiveConstants.CONTROL_FILE_PATH, writeRequest.CreateControlFile(scenesGroup));
907
908 SceneObjectPart part1 = CreateSceneObjectPart1();
909 part1.SitTargetOrientation = new Quaternion(0.2f, 0.3f, 0.4f, 0.5f);
910 part1.SitTargetPosition = new Vector3(1, 2, 3);
911
912 SceneObjectGroup object1 = new SceneObjectGroup(part1);
913
914 // Let's put some inventory items into our object
915 string soundItemName = "sound-item1";
916 UUID soundItemUuid = UUID.Parse("00000000-0000-0000-0000-000000000002");
917 Type type = GetType();
918 Assembly assembly = type.Assembly;
919 string soundDataResourceName = null;
920 string[] names = assembly.GetManifestResourceNames();
921 foreach (string name in names)
922 {
923 if (name.EndsWith(".Resources.test-sound.wav"))
924 soundDataResourceName = name;
925 }
926 Assert.That(soundDataResourceName, Is.Not.Null);
927
928 byte[] soundData;
929 UUID soundUuid;
930 CreateSoundAsset(tar, assembly, soundDataResourceName, out soundData, out soundUuid);
931
932 TaskInventoryItem item1
933 = new TaskInventoryItem { AssetID = soundUuid, ItemID = soundItemUuid, Name = soundItemName };
934 part1.Inventory.AddInventoryItem(item1, true);
935 m_scene.AddNewSceneObject(object1, false);
936
937 string object1FileName = string.Format(
938 "{0}_{1:000}-{2:000}-{3:000}__{4}.xml",
939 part1.Name,
940 Math.Round(part1.GroupPosition.X), Math.Round(part1.GroupPosition.Y), Math.Round(part1.GroupPosition.Z),
941 part1.UUID);
942 string path = "regions/1_1_Unit_test_region/" + ArchiveConstants.OBJECTS_PATH + object1FileName;
943 tar.WriteFile(path, SceneObjectSerializer.ToXml2Format(object1));
944
945 tar.Close();
946
947
948 // Delete the current objects, to test that they're loaded from the OAR and didn't
949 // just remain in the scene.
950 SceneManager.Instance.ForEachScene(delegate(Scene scene)
951 {
952 scene.DeleteAllSceneObjects();
953 });
954
955 // Create a "hole", to test that that the corresponding region isn't loaded from the OAR
956 SceneManager.Instance.CloseScene(SceneManager.Instance.Scenes[1]);
957
958
959 // Check thay the OAR file contains the expected data
960
961 MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray());
962
963 lock (this)
964 {
965 m_scene.EventManager.OnOarFileLoaded += LoadCompleted;
966 m_archiverModule.DearchiveRegion(archiveReadStream);
967 }
968
969 Assert.That(m_lastErrorMessage, Is.Null);
970
971 Assert.AreEqual(3, SceneManager.Instance.Scenes.Count);
972
973 TestLoadedRegion(part1, soundItemName, soundData);
974 }
975
703 } 976 }
704} 977}
diff --git a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs
index 15cd238..1983984 100644
--- a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs
+++ b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs
@@ -46,6 +46,11 @@ namespace OpenSim.Region.Framework.Interfaces
46 /// </summary> 46 /// </summary>
47 void sendRegionHandshakeToAll(); 47 void sendRegionHandshakeToAll();
48 48
49 /// <summary>
50 /// Fires the OnRegionInfoChange event.
51 /// </summary>
52 void TriggerRegionInfoChange();
53
49 void setEstateTerrainBaseTexture(int level, UUID texture); 54 void setEstateTerrainBaseTexture(int level, UUID texture);
50 void setEstateTerrainTextureHeights(int corner, float lowValue, float highValue); 55 void setEstateTerrainTextureHeights(int corner, float lowValue, float highValue);
51 } 56 }
diff --git a/OpenSim/Region/Framework/Scenes/EventManager.cs b/OpenSim/Region/Framework/Scenes/EventManager.cs
index 2f34785..e1c9c8e 100644
--- a/OpenSim/Region/Framework/Scenes/EventManager.cs
+++ b/OpenSim/Region/Framework/Scenes/EventManager.cs
@@ -531,7 +531,7 @@ namespace OpenSim.Region.Framework.Scenes
531 /// the scripts may not have started yet 531 /// the scripts may not have started yet
532 /// Message is non empty string if there were problems loading the oar file 532 /// Message is non empty string if there were problems loading the oar file
533 /// </summary> 533 /// </summary>
534 public delegate void OarFileLoaded(Guid guid, string message); 534 public delegate void OarFileLoaded(Guid guid, List<UUID> loadedScenes, string message);
535 public event OarFileLoaded OnOarFileLoaded; 535 public event OarFileLoaded OnOarFileLoaded;
536 536
537 /// <summary> 537 /// <summary>
@@ -2195,7 +2195,7 @@ namespace OpenSim.Region.Framework.Scenes
2195 return 6; 2195 return 6;
2196 } 2196 }
2197 2197
2198 public void TriggerOarFileLoaded(Guid requestId, string message) 2198 public void TriggerOarFileLoaded(Guid requestId, List<UUID> loadedScenes, string message)
2199 { 2199 {
2200 OarFileLoaded handlerOarFileLoaded = OnOarFileLoaded; 2200 OarFileLoaded handlerOarFileLoaded = OnOarFileLoaded;
2201 if (handlerOarFileLoaded != null) 2201 if (handlerOarFileLoaded != null)
@@ -2204,7 +2204,7 @@ namespace OpenSim.Region.Framework.Scenes
2204 { 2204 {
2205 try 2205 try
2206 { 2206 {
2207 d(requestId, message); 2207 d(requestId, loadedScenes, message);
2208 } 2208 }
2209 catch (Exception e) 2209 catch (Exception e)
2210 { 2210 {
diff --git a/OpenSim/Region/Framework/Scenes/SceneManager.cs b/OpenSim/Region/Framework/Scenes/SceneManager.cs
index c81b55d..cb5b2ba 100644
--- a/OpenSim/Region/Framework/Scenes/SceneManager.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneManager.cs
@@ -92,7 +92,11 @@ namespace OpenSim.Region.Framework.Scenes
92 private static SceneManager m_instance = null; 92 private static SceneManager m_instance = null;
93 public static SceneManager Instance 93 public static SceneManager Instance
94 { 94 {
95 get { return m_instance; } 95 get {
96 if (m_instance == null)
97 m_instance = new SceneManager();
98 return m_instance;
99 }
96 } 100 }
97 101
98 private readonly List<Scene> m_localScenes = new List<Scene>(); 102 private readonly List<Scene> m_localScenes = new List<Scene>();
diff --git a/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs b/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs
index fff3a32..bad75f7 100644
--- a/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs
+++ b/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs
@@ -181,7 +181,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady
181 } 181 }
182 } 182 }
183 183
184 void OnOarFileLoaded(Guid requestId, string message) 184 void OnOarFileLoaded(Guid requestId, List<UUID> loadedScenes, string message)
185 { 185 {
186 m_oarFileLoading = true; 186 m_oarFileLoading = true;
187 187
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
index fa22c78..526dbad 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -131,8 +131,6 @@ public class BSCharacter : BSPhysObject
131 BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy); 131 BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy);
132 132
133 BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(Scene.World.Ptr, LocalID)); 133 BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(Scene.World.Ptr, LocalID));
134 // avatars get all collisions no matter what (makes walking on ground and such work)
135 BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
136 }); 134 });
137 135
138 return; 136 return;
@@ -480,11 +478,10 @@ public class BSCharacter : BSPhysObject
480 // Stop collision events 478 // Stop collision events
481 public override void UnSubscribeEvents() { 479 public override void UnSubscribeEvents() {
482 _subscribedEventsMs = 0; 480 _subscribedEventsMs = 0;
483 // Avatars get all their collision events 481 Scene.TaintedObject("BSCharacter.UnSubscribeEvents", delegate()
484 // Scene.TaintedObject("BSCharacter.UnSubscribeEvents", delegate() 482 {
485 // { 483 BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
486 // BulletSimAPI.RemoveFromCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); 484 });
487 // });
488 } 485 }
489 // Return 'true' if someone has subscribed to events 486 // Return 'true' if someone has subscribed to events
490 public override bool SubscribedEvents() { 487 public override bool SubscribedEvents() {
@@ -532,20 +529,20 @@ public class BSCharacter : BSPhysObject
532 // The collision, if it should be reported to the character, is placed in a collection 529 // The collision, if it should be reported to the character, is placed in a collection
533 // that will later be sent to the simulator when SendCollisions() is called. 530 // that will later be sent to the simulator when SendCollisions() is called.
534 CollisionEventUpdate collisionCollection = null; 531 CollisionEventUpdate collisionCollection = null;
535 public override void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth) 532 public override bool Collide(uint collidingWith, BSPhysObject collidee, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth)
536 { 533 {
537 // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith); 534 bool ret = false;
538 535
539 // The following makes IsColliding() and IsCollidingGround() work 536 // The following makes IsColliding() and IsCollidingGround() work
540 _collidingStep = Scene.SimulationStep; 537 _collidingStep = Scene.SimulationStep;
541 if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID) 538 if (collidingWith <= Scene.TerrainManager.HighestTerrainID)
542 { 539 {
543 _collidingGroundStep = Scene.SimulationStep; 540 _collidingGroundStep = Scene.SimulationStep;
544 } 541 }
545 // DetailLog("{0},BSCharacter.Collison,call,with={1}", LocalID, collidingWith); 542 // DetailLog("{0},BSCharacter.Collison,call,with={1}", LocalID, collidingWith);
546 543
547 // throttle collisions to the rate specified in the subscription 544 // throttle collisions to the rate specified in the subscription
548 if (_subscribedEventsMs != 0) { 545 if (SubscribedEvents()) {
549 int nowTime = Scene.SimulationNowTime; 546 int nowTime = Scene.SimulationNowTime;
550 if (nowTime >= _nextCollisionOkTime) { 547 if (nowTime >= _nextCollisionOkTime) {
551 _nextCollisionOkTime = nowTime + _subscribedEventsMs; 548 _nextCollisionOkTime = nowTime + _subscribedEventsMs;
@@ -553,8 +550,10 @@ public class BSCharacter : BSPhysObject
553 if (collisionCollection == null) 550 if (collisionCollection == null)
554 collisionCollection = new CollisionEventUpdate(); 551 collisionCollection = new CollisionEventUpdate();
555 collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); 552 collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
553 ret = true;
556 } 554 }
557 } 555 }
556 return ret;
558 } 557 }
559 558
560 public override void SendCollisions() 559 public override void SendCollisions()
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
index 2e15ced..1376a29 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
@@ -74,6 +74,17 @@ public abstract class BSConstraint : IDisposable
74 return ret; 74 return ret;
75 } 75 }
76 76
77 public virtual bool SetSolverIterations(float cnt)
78 {
79 bool ret = false;
80 if (m_enabled)
81 {
82 BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.Ptr, cnt);
83 ret = true;
84 }
85 return ret;
86 }
87
77 public virtual bool CalculateTransforms() 88 public virtual bool CalculateTransforms()
78 { 89 {
79 bool ret = false; 90 bool ret = false;
@@ -96,12 +107,9 @@ public abstract class BSConstraint : IDisposable
96 ret = CalculateTransforms(); 107 ret = CalculateTransforms();
97 if (ret) 108 if (ret)
98 { 109 {
99 // m_world.scene.PhysicsLogging.Write("{0},BSConstraint.RecomputeConstraintVariables,taint,enabling,A={1},B={2}",
100 // BSScene.DetailLogZero, Body1.ID, Body2.ID);
101
102 // Setting an object's mass to zero (making it static like when it's selected) 110 // Setting an object's mass to zero (making it static like when it's selected)
103 // automatically disables the constraints. 111 // automatically disables the constraints.
104 // If enabled, be sure to set the constraint itself to enabled. 112 // If the link is enabled, be sure to set the constraint itself to enabled.
105 BulletSimAPI.SetConstraintEnable2(m_constraint.Ptr, m_world.scene.NumericBool(true)); 113 BulletSimAPI.SetConstraintEnable2(m_constraint.Ptr, m_world.scene.NumericBool(true));
106 } 114 }
107 else 115 else
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
index 8169e99..61006f0 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
@@ -80,7 +80,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
80 // Linear properties 80 // Linear properties
81 private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time 81 private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time
82 private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL 82 private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL
83 private Vector3 m_dir = Vector3.Zero; // velocity applied to body 83 private Vector3 m_newVelocity = Vector3.Zero; // velocity computed to be applied to body
84 private Vector3 m_linearFrictionTimescale = Vector3.Zero; 84 private Vector3 m_linearFrictionTimescale = Vector3.Zero;
85 private float m_linearMotorDecayTimescale = 0; 85 private float m_linearMotorDecayTimescale = 0;
86 private float m_linearMotorTimescale = 0; 86 private float m_linearMotorTimescale = 0;
@@ -131,7 +131,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
131 m_type = Vehicle.TYPE_NONE; 131 m_type = Vehicle.TYPE_NONE;
132 } 132 }
133 133
134 internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue, float timestep) 134 internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue)
135 { 135 {
136 VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue); 136 VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
137 switch (pParam) 137 switch (pParam)
@@ -230,7 +230,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
230 } 230 }
231 }//end ProcessFloatVehicleParam 231 }//end ProcessFloatVehicleParam
232 232
233 internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue, float timestep) 233 internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue)
234 { 234 {
235 VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue); 235 VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
236 switch (pParam) 236 switch (pParam)
@@ -299,7 +299,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
299 } 299 }
300 }//end ProcessVehicleFlags 300 }//end ProcessVehicleFlags
301 301
302 internal void ProcessTypeChange(Vehicle pType, float stepSize) 302 internal void ProcessTypeChange(Vehicle pType)
303 { 303 {
304 VDetailLog("{0},ProcessTypeChange,type={1}", m_prim.LocalID, pType); 304 VDetailLog("{0},ProcessTypeChange,type={1}", m_prim.LocalID, pType);
305 // Set Defaults For Type 305 // Set Defaults For Type
@@ -478,29 +478,30 @@ namespace OpenSim.Region.Physics.BulletSPlugin
478 MoveAngular(pTimestep); 478 MoveAngular(pTimestep);
479 LimitRotation(pTimestep); 479 LimitRotation(pTimestep);
480 480
481 // remember the position so next step we can limit absolute movement effects
482 m_lastPositionVector = m_prim.Position;
483
481 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", 484 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
482 m_prim.LocalID, m_prim.Position, m_prim.Force, m_prim.Velocity, m_prim.RotationalVelocity); 485 m_prim.LocalID, m_prim.Position, m_prim.Force, m_prim.Velocity, m_prim.RotationalVelocity);
483 }// end Step 486 }// end Step
484 487
485 private void MoveLinear(float pTimestep) 488 private void MoveLinear(float pTimestep)
486 { 489 {
487 // requested m_linearMotorDirection is significant 490 // m_linearMotorDirection is the direction we are moving relative to the vehicle coordinates
488 // if (!m_linearMotorDirection.ApproxEquals(Vector3.Zero, 0.01f)) 491 // m_lastLinearVelocityVector is the speed we are moving in that direction
489 if (m_linearMotorDirection.LengthSquared() > 0.0001f) 492 if (m_linearMotorDirection.LengthSquared() > 0.001f)
490 { 493 {
491 Vector3 origDir = m_linearMotorDirection; 494 Vector3 origDir = m_linearMotorDirection;
492 Vector3 origVel = m_lastLinearVelocityVector; 495 Vector3 origVel = m_lastLinearVelocityVector;
493 496
494 // add drive to body 497 // add drive to body
495 // Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale/pTimestep); 498 // Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale / pTimestep);
496 Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale); 499 Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/(m_linearMotorTimescale / pTimestep);
497 // lastLinearVelocityVector is the current body velocity vector? 500 // lastLinearVelocityVector is the current body velocity vector
498 // RA: Not sure what the *10 is for. A correction for pTimestep? 501 // RA: Not sure what the *10 is for. A correction for pTimestep?
499 // m_lastLinearVelocityVector += (addAmount*10); 502 // m_lastLinearVelocityVector += (addAmount*10);
500 m_lastLinearVelocityVector += addAmount; 503 m_lastLinearVelocityVector += addAmount;
501 504
502 // This will work temporarily, but we really need to compare speed on an axis
503 // KF: Limit body velocity to applied velocity?
504 // Limit the velocity vector to less than the last set linear motor direction 505 // Limit the velocity vector to less than the last set linear motor direction
505 if (Math.Abs(m_lastLinearVelocityVector.X) > Math.Abs(m_linearMotorDirectionLASTSET.X)) 506 if (Math.Abs(m_lastLinearVelocityVector.X) > Math.Abs(m_linearMotorDirectionLASTSET.X))
506 m_lastLinearVelocityVector.X = m_linearMotorDirectionLASTSET.X; 507 m_lastLinearVelocityVector.X = m_linearMotorDirectionLASTSET.X;
@@ -509,95 +510,58 @@ namespace OpenSim.Region.Physics.BulletSPlugin
509 if (Math.Abs(m_lastLinearVelocityVector.Z) > Math.Abs(m_linearMotorDirectionLASTSET.Z)) 510 if (Math.Abs(m_lastLinearVelocityVector.Z) > Math.Abs(m_linearMotorDirectionLASTSET.Z))
510 m_lastLinearVelocityVector.Z = m_linearMotorDirectionLASTSET.Z; 511 m_lastLinearVelocityVector.Z = m_linearMotorDirectionLASTSET.Z;
511 512
513 /*
512 // decay applied velocity 514 // decay applied velocity
513 Vector3 decayfraction = ((Vector3.One/(m_linearMotorDecayTimescale/pTimestep))); 515 Vector3 decayfraction = Vector3.One/(m_linearMotorDecayTimescale / pTimestep);
516 // (RA: do not know where the 0.5f comes from)
514 m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f; 517 m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f;
515
516 /*
517 Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/m_linearMotorTimescale;
518 m_lastLinearVelocityVector += addAmount;
519
520 float decayfraction = (1.0f - 1.0f / m_linearMotorDecayTimescale);
521 m_linearMotorDirection *= decayfraction;
522
523 */ 518 */
519 float keepfraction = 1.0f - (1.0f / (m_linearMotorDecayTimescale / pTimestep));
520 m_linearMotorDirection *= keepfraction;
524 521
525 VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},decay={4},dir={5},vel={6}", 522 VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},notDecay={4},dir={5},vel={6}",
526 m_prim.LocalID, origDir, origVel, addAmount, decayfraction, m_linearMotorDirection, m_lastLinearVelocityVector); 523 m_prim.LocalID, origDir, origVel, addAmount, keepfraction, m_linearMotorDirection, m_lastLinearVelocityVector);
527 } 524 }
528 else 525 else
529 { 526 {
530 // if what remains of applied is small, zero it. 527 // if what remains of direction is very small, zero it.
531 // if (m_lastLinearVelocityVector.ApproxEquals(Vector3.Zero, 0.01f))
532 // m_lastLinearVelocityVector = Vector3.Zero;
533 m_linearMotorDirection = Vector3.Zero; 528 m_linearMotorDirection = Vector3.Zero;
534 m_lastLinearVelocityVector = Vector3.Zero; 529 m_lastLinearVelocityVector = Vector3.Zero;
530 VDetailLog("{0},MoveLinear,zeroed", m_prim.LocalID);
535 } 531 }
536 532
537 // convert requested object velocity to object relative vector 533 // convert requested object velocity to object relative vector
538 Quaternion rotq = m_prim.Orientation; 534 Quaternion rotq = m_prim.Orientation;
539 m_dir = m_lastLinearVelocityVector * rotq; 535 m_newVelocity = m_lastLinearVelocityVector * rotq;
540 536
541 // Add the various forces into m_dir which will be our new direction vector (velocity) 537 // Add the various forces into m_dir which will be our new direction vector (velocity)
542 538
543 // add Gravity and Buoyancy 539 // add Gravity and Buoyancy
544 // KF: So far I have found no good method to combine a script-requested
545 // .Z velocity and gravity. Therefore only 0g will used script-requested
546 // .Z velocity. >0g (m_VehicleBuoyancy < 1) will used modified gravity only.
547 Vector3 grav = Vector3.Zero;
548 // There is some gravity, make a gravity force vector that is applied after object velocity. 540 // There is some gravity, make a gravity force vector that is applied after object velocity.
549 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; 541 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
550 grav.Z = m_prim.Scene.DefaultGravity.Z * m_prim.Mass * (1f - m_VehicleBuoyancy); 542 Vector3 grav = m_prim.Scene.DefaultGravity * (m_prim.Mass * (1f - m_VehicleBuoyancy));
543
544 /*
545 * RA: Not sure why one would do this
551 // Preserve the current Z velocity 546 // Preserve the current Z velocity
552 Vector3 vel_now = m_prim.Velocity; 547 Vector3 vel_now = m_prim.Velocity;
553 m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity 548 m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity
549 */
554 550
555 Vector3 pos = m_prim.Position; 551 Vector3 pos = m_prim.Position;
556 Vector3 posChange = pos;
557// 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); 552// 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);
558 double Zchange = Math.Abs(posChange.Z);
559 if (m_BlockingEndPoint != Vector3.Zero)
560 {
561 bool changed = false;
562 if (pos.X >= (m_BlockingEndPoint.X - (float)1))
563 {
564 pos.X -= posChange.X + 1;
565 changed = true;
566 }
567 if (pos.Y >= (m_BlockingEndPoint.Y - (float)1))
568 {
569 pos.Y -= posChange.Y + 1;
570 changed = true;
571 }
572 if (pos.Z >= (m_BlockingEndPoint.Z - (float)1))
573 {
574 pos.Z -= posChange.Z + 1;
575 changed = true;
576 }
577 if (pos.X <= 0)
578 {
579 pos.X += posChange.X + 1;
580 changed = true;
581 }
582 if (pos.Y <= 0)
583 {
584 pos.Y += posChange.Y + 1;
585 changed = true;
586 }
587 if (changed)
588 {
589 m_prim.Position = pos;
590 VDetailLog("{0},MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
591 m_prim.LocalID, m_BlockingEndPoint, posChange, pos);
592 }
593 }
594 553
595 // If below the terrain, move us above the ground a little. 554 // If below the terrain, move us above the ground a little.
596 if (pos.Z < m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos)) 555 float terrainHeight = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos);
556 // Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset.
557 // Need to add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass.
558 // Vector3 rotatedSize = m_prim.Size * m_prim.Orientation;
559 // if (rotatedSize.Z < terrainHeight)
560 if (pos.Z < terrainHeight)
597 { 561 {
598 pos.Z = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 2; 562 pos.Z = terrainHeight + 2;
599 m_prim.Position = pos; 563 m_prim.Position = pos;
600 VDetailLog("{0},MoveLinear,terrainHeight,pos={1}", m_prim.LocalID, pos); 564 VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", m_prim.LocalID, terrainHeight, pos);
601 } 565 }
602 566
603 // Check if hovering 567 // Check if hovering
@@ -606,11 +570,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
606 // We should hover, get the target height 570 // We should hover, get the target height
607 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0) 571 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
608 { 572 {
609 m_VhoverTargetHeight = m_prim.Scene.GetWaterLevel() + m_VhoverHeight; 573 m_VhoverTargetHeight = m_prim.Scene.GetWaterLevelAtXYZ(pos) + m_VhoverHeight;
610 } 574 }
611 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) 575 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
612 { 576 {
613 m_VhoverTargetHeight = m_prim.Scene.TerrainManager.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight; 577 m_VhoverTargetHeight = terrainHeight + m_VhoverHeight;
614 } 578 }
615 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0) 579 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
616 { 580 {
@@ -635,82 +599,92 @@ namespace OpenSim.Region.Physics.BulletSPlugin
635 // Replace Vertical speed with correction figure if significant 599 // Replace Vertical speed with correction figure if significant
636 if (Math.Abs(herr0) > 0.01f) 600 if (Math.Abs(herr0) > 0.01f)
637 { 601 {
638 m_dir.Z = -((herr0 * pTimestep * 50.0f) / m_VhoverTimescale); 602 m_newVelocity.Z = -((herr0 * pTimestep * 50.0f) / m_VhoverTimescale);
639 //KF: m_VhoverEfficiency is not yet implemented 603 //KF: m_VhoverEfficiency is not yet implemented
640 } 604 }
641 else 605 else
642 { 606 {
643 m_dir.Z = 0f; 607 m_newVelocity.Z = 0f;
644 } 608 }
645 } 609 }
646 610
647 VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", m_prim.LocalID, pos, m_dir, m_VhoverHeight, m_VhoverTargetHeight); 611 VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", m_prim.LocalID, pos, m_newVelocity, m_VhoverHeight, m_VhoverTargetHeight);
612 }
648 613
649// m_VhoverEfficiency = 0f; // 0=boucy, 1=Crit.damped 614 Vector3 posChange = pos - m_lastPositionVector;
650// m_VhoverTimescale = 0f; // time to acheive height 615 if (m_BlockingEndPoint != Vector3.Zero)
651// pTimestep is time since last frame,in secs 616 {
617 bool changed = false;
618 if (pos.X >= (m_BlockingEndPoint.X - (float)1))
619 {
620 pos.X -= posChange.X + 1;
621 changed = true;
622 }
623 if (pos.Y >= (m_BlockingEndPoint.Y - (float)1))
624 {
625 pos.Y -= posChange.Y + 1;
626 changed = true;
627 }
628 if (pos.Z >= (m_BlockingEndPoint.Z - (float)1))
629 {
630 pos.Z -= posChange.Z + 1;
631 changed = true;
632 }
633 if (pos.X <= 0)
634 {
635 pos.X += posChange.X + 1;
636 changed = true;
637 }
638 if (pos.Y <= 0)
639 {
640 pos.Y += posChange.Y + 1;
641 changed = true;
642 }
643 if (changed)
644 {
645 m_prim.Position = pos;
646 VDetailLog("{0},MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
647 m_prim.LocalID, m_BlockingEndPoint, posChange, pos);
648 }
652 } 649 }
653 650
651 float Zchange = Math.Abs(posChange.Z);
654 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) 652 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
655 { 653 {
656 //Start Experimental Values
657 if (Zchange > .3) 654 if (Zchange > .3)
658 {
659 grav.Z = (float)(grav.Z * 3); 655 grav.Z = (float)(grav.Z * 3);
660 }
661 if (Zchange > .15) 656 if (Zchange > .15)
662 {
663 grav.Z = (float)(grav.Z * 2); 657 grav.Z = (float)(grav.Z * 2);
664 }
665 if (Zchange > .75) 658 if (Zchange > .75)
666 {
667 grav.Z = (float)(grav.Z * 1.5); 659 grav.Z = (float)(grav.Z * 1.5);
668 }
669 if (Zchange > .05) 660 if (Zchange > .05)
670 {
671 grav.Z = (float)(grav.Z * 1.25); 661 grav.Z = (float)(grav.Z * 1.25);
672 }
673 if (Zchange > .025) 662 if (Zchange > .025)
674 {
675 grav.Z = (float)(grav.Z * 1.125); 663 grav.Z = (float)(grav.Z * 1.125);
676 } 664 float postemp = (pos.Z - terrainHeight);
677 float terraintemp = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos);
678 float postemp = (pos.Z - terraintemp);
679 if (postemp > 2.5f) 665 if (postemp > 2.5f)
680 {
681 grav.Z = (float)(grav.Z * 1.037125); 666 grav.Z = (float)(grav.Z * 1.037125);
682 }
683 VDetailLog("{0},MoveLinear,limitMotorUp,grav={1}", m_prim.LocalID, grav); 667 VDetailLog("{0},MoveLinear,limitMotorUp,grav={1}", m_prim.LocalID, grav);
684 //End Experimental Values
685 } 668 }
686 if ((m_flags & (VehicleFlag.NO_X)) != 0) 669 if ((m_flags & (VehicleFlag.NO_X)) != 0)
687 { 670 m_newVelocity.X = 0;
688 m_dir.X = 0;
689 }
690 if ((m_flags & (VehicleFlag.NO_Y)) != 0) 671 if ((m_flags & (VehicleFlag.NO_Y)) != 0)
691 { 672 m_newVelocity.Y = 0;
692 m_dir.Y = 0;
693 }
694 if ((m_flags & (VehicleFlag.NO_Z)) != 0) 673 if ((m_flags & (VehicleFlag.NO_Z)) != 0)
695 { 674 m_newVelocity.Z = 0;
696 m_dir.Z = 0;
697 }
698
699 m_lastPositionVector = m_prim.Position;
700 675
701 // Apply velocity 676 // Apply velocity
702 m_prim.Velocity = m_dir; 677 m_prim.Velocity = m_newVelocity;
703 // apply gravity force 678 // apply gravity force
704 // Why is this set here? The physics engine already does gravity. 679 // Why is this set here? The physics engine already does gravity.
705 // m_prim.AddForce(grav, false); 680 // m_prim.AddForce(grav, false);
706 // m_prim.Force = grav;
707 681
708 // Apply friction 682 // Apply friction
709 Vector3 decayamount = Vector3.One / (m_linearFrictionTimescale / pTimestep); 683 Vector3 keepFraction = Vector3.One - (Vector3.One / (m_linearFrictionTimescale / pTimestep));
710 m_lastLinearVelocityVector -= m_lastLinearVelocityVector * decayamount; 684 m_lastLinearVelocityVector *= keepFraction;
711 685
712 VDetailLog("{0},MoveLinear,done,pos={1},vel={2},force={3},decay={4}", 686 VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},grav={4},1Mdecay={5}",
713 m_prim.LocalID, m_lastPositionVector, m_dir, grav, decayamount); 687 m_prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, grav, keepFraction);
714 688
715 } // end MoveLinear() 689 } // end MoveLinear()
716 690
@@ -735,17 +709,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin
735 // There are m_angularMotorApply steps. 709 // There are m_angularMotorApply steps.
736 Vector3 origAngularVelocity = m_angularMotorVelocity; 710 Vector3 origAngularVelocity = m_angularMotorVelocity;
737 // ramp up to new value 711 // ramp up to new value
738 // current velocity += error / (time to get there / step interval) 712 // current velocity += error / ( time to get there / step interval)
739 // requested speed - last motor speed 713 // requested speed - last motor speed
740 m_angularMotorVelocity.X += (m_angularMotorDirection.X - m_angularMotorVelocity.X) / (m_angularMotorTimescale / pTimestep); 714 m_angularMotorVelocity.X += (m_angularMotorDirection.X - m_angularMotorVelocity.X) / (m_angularMotorTimescale / pTimestep);
741 m_angularMotorVelocity.Y += (m_angularMotorDirection.Y - m_angularMotorVelocity.Y) / (m_angularMotorTimescale / pTimestep); 715 m_angularMotorVelocity.Y += (m_angularMotorDirection.Y - m_angularMotorVelocity.Y) / (m_angularMotorTimescale / pTimestep);
742 m_angularMotorVelocity.Z += (m_angularMotorDirection.Z - m_angularMotorVelocity.Z) / (m_angularMotorTimescale / pTimestep); 716 m_angularMotorVelocity.Z += (m_angularMotorDirection.Z - m_angularMotorVelocity.Z) / (m_angularMotorTimescale / pTimestep);
743 717
744 VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},origvel={2},dir={3},vel={4}", 718 VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},angTScale={2},timeStep={3},origvel={4},dir={5},vel={6}",
745 m_prim.LocalID,m_angularMotorApply,origAngularVelocity, m_angularMotorDirection, m_angularMotorVelocity); 719 m_prim.LocalID, m_angularMotorApply, m_angularMotorTimescale, pTimestep, origAngularVelocity, m_angularMotorDirection, m_angularMotorVelocity);
746 720
747 m_angularMotorApply--; // This is done so that if script request rate is less than phys frame rate the expected 721 // This is done so that if script request rate is less than phys frame rate the expected
748 // velocity may still be acheived. 722 // velocity may still be acheived.
723 m_angularMotorApply--;
749 } 724 }
750 else 725 else
751 { 726 {
@@ -760,7 +735,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
760 Vector3 vertattr = Vector3.Zero; 735 Vector3 vertattr = Vector3.Zero;
761 if (m_verticalAttractionTimescale < 300) 736 if (m_verticalAttractionTimescale < 300)
762 { 737 {
763 float VAservo = 0.2f / (m_verticalAttractionTimescale * pTimestep); 738 float VAservo = 0.2f / (m_verticalAttractionTimescale / pTimestep);
764 // get present body rotation 739 // get present body rotation
765 Quaternion rotq = m_prim.Orientation; 740 Quaternion rotq = m_prim.Orientation;
766 // make a vector pointing up 741 // make a vector pointing up
@@ -863,16 +838,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
863 m_rot.Y = 0; 838 m_rot.Y = 0;
864 changed = true; 839 changed = true;
865 } 840 }
866 if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0)
867 {
868 m_rot.X = 0;
869 m_rot.Y = 0;
870 changed = true;
871 }
872 if (changed) 841 if (changed)
842 {
873 m_prim.Orientation = m_rot; 843 m_prim.Orientation = m_rot;
844 VDetailLog("{0},LimitRotation,done,orig={1},new={2}", m_prim.LocalID, rotq, m_rot);
845 }
874 846
875 VDetailLog("{0},LimitRotation,done,changed={1},orig={2},new={3}", m_prim.LocalID, changed, rotq, m_rot);
876 } 847 }
877 848
878 // Invoke the detailed logger and output something if it's enabled. 849 // Invoke the detailed logger and output something if it's enabled.
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
index 5f6601d..7e784eb 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
@@ -202,11 +202,33 @@ public class BSLinkset
202 return com; 202 return com;
203 } 203 }
204 204
205 // The object is going dynamic (physical). Do any setup necessary
206 // for a dynamic linkset.
207 // Only the state of the passed object can be modified. The rest of the linkset
208 // has not yet been fully constructed.
209 // Return 'true' if any properties updated on the passed object.
210 // Called at taint-time!
211 public bool MakeDynamic(BSPhysObject child)
212 {
213 bool ret = false;
214 return ret;
215 }
216
217 // The object is going static (non-physical). Do any setup necessary
218 // for a static linkset.
219 // Return 'true' if any properties updated on the passed object.
220 // Called at taint-time!
221 public bool MakeStatic(BSPhysObject child)
222 {
223 // What is done for each object in BSPrim is what we want.
224 return false;
225 }
226
205 // When physical properties are changed the linkset needs to recalculate 227 // When physical properties are changed the linkset needs to recalculate
206 // its internal properties. 228 // its internal properties.
207 public void Refresh(BSPhysObject requestor) 229 public void Refresh(BSPhysObject requestor)
208 { 230 {
209 // If there are no children, there aren't any constraints to recompute 231 // If there are no children, there can't be any constraints to recompute
210 if (!HasAnyChildren) 232 if (!HasAnyChildren)
211 return; 233 return;
212 234
@@ -225,11 +247,12 @@ public class BSLinkset
225 // from a linkset to make sure the constraints know about the new mass and 247 // from a linkset to make sure the constraints know about the new mass and
226 // geometry. 248 // geometry.
227 // Must only be called at taint time!! 249 // Must only be called at taint time!!
228 private bool RecomputeLinksetConstraintVariables() 250 private void RecomputeLinksetConstraintVariables()
229 { 251 {
230 float linksetMass = LinksetMass; 252 float linksetMass = LinksetMass;
231 lock (m_linksetActivityLock) 253 lock (m_linksetActivityLock)
232 { 254 {
255 bool somethingMissing = false;
233 foreach (BSPhysObject child in m_children) 256 foreach (BSPhysObject child in m_children)
234 { 257 {
235 BSConstraint constrain; 258 BSConstraint constrain;
@@ -241,16 +264,36 @@ public class BSLinkset
241 } 264 }
242 else 265 else
243 { 266 {
244 // Non-fatal error that can happen when children are being added to the linkset but 267 // Non-fatal error that happens when children are being added to the linkset but
245 // their constraints have not been created yet. 268 // their constraints have not been created yet.
246 // Caused by the fact that m_children is built at run time but building constraints 269 // Caused by the fact that m_children is built at run time but building constraints
247 // happens at taint time. 270 // happens at taint time.
248 // m_physicsScene.Logger.ErrorFormat("{0} RecomputeLinksetConstraintVariables: constraint not found for root={1}, child={2}", 271 somethingMissing = true;
249 // LogHeader, m_linksetRoot.Body.ID, child.Body.ID); 272 break;
250 } 273 }
251 } 274 }
275
276 // If the whole linkset is not here, doesn't make sense to recompute linkset wide values
277 if (!somethingMissing)
278 {
279 // If this is a multiple object linkset, set everybody's center of mass to the set's center of mass
280 OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass();
281 BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.Ptr, centerOfMass, OMV.Quaternion.Identity);
282 foreach (BSPhysObject child in m_children)
283 {
284 BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.Ptr, centerOfMass, OMV.Quaternion.Identity);
285 }
286 /*
287 // The root prim takes on the weight of the whole linkset
288 OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(LinksetRoot.BSShape.Ptr, linksetMass);
289 BulletSimAPI.SetMassProps2(LinksetRoot.BSBody.Ptr, linksetMass, inertia);
290 OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass();
291 BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.Ptr, centerOfMass, OMV.Quaternion.Identity);
292 BulletSimAPI.UpdateInertiaTensor2(LinksetRoot.BSBody.Ptr);
293 */
294 }
252 } 295 }
253 return false; 296 return;
254 } 297 }
255 298
256 // I am the root of a linkset and a new child is being added 299 // I am the root of a linkset and a new child is being added
@@ -296,9 +339,9 @@ public class BSLinkset
296 DetailLog("{0},RemoveChildFromLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID); 339 DetailLog("{0},RemoveChildFromLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID);
297 340
298 PhysicallyUnlinkAChildFromRoot(rootx, childx); 341 PhysicallyUnlinkAChildFromRoot(rootx, childx);
342 RecomputeLinksetConstraintVariables();
299 }); 343 });
300 344
301 RecomputeLinksetConstraintVariables();
302 } 345 }
303 else 346 else
304 { 347 {
@@ -377,6 +420,10 @@ public class BSLinkset
377 PhysicsScene.Params.linkConstraintTransMotorMaxVel, 420 PhysicsScene.Params.linkConstraintTransMotorMaxVel,
378 PhysicsScene.Params.linkConstraintTransMotorMaxForce); 421 PhysicsScene.Params.linkConstraintTransMotorMaxForce);
379 constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP); 422 constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP);
423 if (PhysicsScene.Params.linkConstraintSolverIterations != 0f)
424 {
425 constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations);
426 }
380 427
381 RecomputeLinksetConstraintVariables(); 428 RecomputeLinksetConstraintVariables();
382 } 429 }
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
index e411fcb..3fe71e1 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
@@ -41,7 +41,7 @@ public abstract class BSPhysObject : PhysicsActor
41{ 41{
42 public abstract BSLinkset Linkset { get; set; } 42 public abstract BSLinkset Linkset { get; set; }
43 43
44 public abstract void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, 44 public abstract bool Collide(uint collidingWith, BSPhysObject collidee,
45 OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth); 45 OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth);
46 public abstract void SendCollisions(); 46 public abstract void SendCollisions();
47 47
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
index 6d0af63..26a581f 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -136,11 +136,11 @@ public sealed class BSPrim : BSPhysObject
136 Linkset = new BSLinkset(Scene, this); // a linkset of one 136 Linkset = new BSLinkset(Scene, this); // a linkset of one
137 _vehicle = new BSDynamics(Scene, this); // add vehicleness 137 _vehicle = new BSDynamics(Scene, this); // add vehicleness
138 _mass = CalculateMass(); 138 _mass = CalculateMass();
139 // do the actual object creation at taint time
140 DetailLog("{0},BSPrim.constructor,call", LocalID); 139 DetailLog("{0},BSPrim.constructor,call", LocalID);
140 // do the actual object creation at taint time
141 _scene.TaintedObject("BSPrim.create", delegate() 141 _scene.TaintedObject("BSPrim.create", delegate()
142 { 142 {
143 RecreateGeomAndObject(); 143 CreateGeomAndObject(true);
144 144
145 // Get the pointer to the physical body for this object. 145 // Get the pointer to the physical body for this object.
146 // At the moment, we're still letting BulletSim manage the creation and destruction 146 // At the moment, we're still letting BulletSim manage the creation and destruction
@@ -186,9 +186,10 @@ public sealed class BSPrim : BSPhysObject
186 _scene.TaintedObject("BSPrim.setSize", delegate() 186 _scene.TaintedObject("BSPrim.setSize", delegate()
187 { 187 {
188 _mass = CalculateMass(); // changing size changes the mass 188 _mass = CalculateMass(); // changing size changes the mass
189 BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, (IsPhysical ? _mass : 0f), IsPhysical); 189 // Since _size changed, the mesh needs to be rebuilt. If rebuilt, all the correct
190 DetailLog("{0}: BSPrim.setSize: size={1}, mass={2}, physical={3}", LocalID, _size, _mass, IsPhysical); 190 // scale and margins are set.
191 RecreateGeomAndObject(); 191 CreateGeomAndObject(true);
192 DetailLog("{0}: BSPrim.setSize: size={1}, scale={2}, mass={3}, physical={4}", LocalID, _size, _scale, _mass, IsPhysical);
192 }); 193 });
193 } 194 }
194 } 195 }
@@ -198,7 +199,7 @@ public sealed class BSPrim : BSPhysObject
198 _scene.TaintedObject("BSPrim.setShape", delegate() 199 _scene.TaintedObject("BSPrim.setShape", delegate()
199 { 200 {
200 _mass = CalculateMass(); // changing the shape changes the mass 201 _mass = CalculateMass(); // changing the shape changes the mass
201 RecreateGeomAndObject(); 202 CreateGeomAndObject(false);
202 }); 203 });
203 } 204 }
204 } 205 }
@@ -279,7 +280,7 @@ public sealed class BSPrim : BSPhysObject
279 get { 280 get {
280 if (!Linkset.IsRoot(this)) 281 if (!Linkset.IsRoot(this))
281 // child prims move around based on their parent. Need to get the latest location 282 // child prims move around based on their parent. Need to get the latest location
282 _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); 283 _position = BulletSimAPI.GetPosition2(BSBody.Ptr);
283 284
284 // don't do the GetObjectPosition for root elements because this function is called a zillion times 285 // don't do the GetObjectPosition for root elements because this function is called a zillion times
285 // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); 286 // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
@@ -291,7 +292,7 @@ public sealed class BSPrim : BSPhysObject
291 _scene.TaintedObject("BSPrim.setPosition", delegate() 292 _scene.TaintedObject("BSPrim.setPosition", delegate()
292 { 293 {
293 DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); 294 DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
294 BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); 295 BulletSimAPI.SetTranslation2(BSBody.Ptr, _position, _orientation);
295 }); 296 });
296 } 297 }
297 } 298 }
@@ -302,7 +303,8 @@ public sealed class BSPrim : BSPhysObject
302 { 303 {
303 get 304 get
304 { 305 {
305 return Linkset.LinksetMass; 306 // return Linkset.LinksetMass;
307 return _mass;
306 } 308 }
307 } 309 }
308 310
@@ -328,7 +330,6 @@ public sealed class BSPrim : BSPhysObject
328 _scene.TaintedObject("BSPrim.setForce", delegate() 330 _scene.TaintedObject("BSPrim.setForce", delegate()
329 { 331 {
330 DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force); 332 DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force);
331 // BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force);
332 BulletSimAPI.SetObjectForce2(BSBody.Ptr, _force); 333 BulletSimAPI.SetObjectForce2(BSBody.Ptr, _force);
333 }); 334 });
334 } 335 }
@@ -345,7 +346,7 @@ public sealed class BSPrim : BSPhysObject
345 { 346 {
346 // Done at taint time so we're sure the physics engine is not using the variables 347 // Done at taint time so we're sure the physics engine is not using the variables
347 // Vehicle code changes the parameters for this vehicle type. 348 // Vehicle code changes the parameters for this vehicle type.
348 _vehicle.ProcessTypeChange(type, Scene.LastSimulatedTimestep); 349 _vehicle.ProcessTypeChange(type);
349 // Tell the scene about the vehicle so it will get processing each frame. 350 // Tell the scene about the vehicle so it will get processing each frame.
350 _scene.VehicleInSceneTypeChanged(this, type); 351 _scene.VehicleInSceneTypeChanged(this, type);
351 }); 352 });
@@ -355,14 +356,14 @@ public sealed class BSPrim : BSPhysObject
355 { 356 {
356 _scene.TaintedObject("BSPrim.VehicleFloatParam", delegate() 357 _scene.TaintedObject("BSPrim.VehicleFloatParam", delegate()
357 { 358 {
358 _vehicle.ProcessFloatVehicleParam((Vehicle)param, value, _scene.LastSimulatedTimestep); 359 _vehicle.ProcessFloatVehicleParam((Vehicle)param, value);
359 }); 360 });
360 } 361 }
361 public override void VehicleVectorParam(int param, OMV.Vector3 value) 362 public override void VehicleVectorParam(int param, OMV.Vector3 value)
362 { 363 {
363 _scene.TaintedObject("BSPrim.VehicleVectorParam", delegate() 364 _scene.TaintedObject("BSPrim.VehicleVectorParam", delegate()
364 { 365 {
365 _vehicle.ProcessVectorVehicleParam((Vehicle)param, value, _scene.LastSimulatedTimestep); 366 _vehicle.ProcessVectorVehicleParam((Vehicle)param, value);
366 }); 367 });
367 } 368 }
368 public override void VehicleRotationParam(int param, OMV.Quaternion rotation) 369 public override void VehicleRotationParam(int param, OMV.Quaternion rotation)
@@ -406,7 +407,7 @@ public sealed class BSPrim : BSPhysObject
406 _scene.TaintedObject("BSPrim.setVelocity", delegate() 407 _scene.TaintedObject("BSPrim.setVelocity", delegate()
407 { 408 {
408 DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); 409 DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
409 BulletSimAPI.SetObjectVelocity(_scene.WorldID, LocalID, _velocity); 410 BulletSimAPI.SetLinearVelocity2(BSBody.Ptr, _velocity);
410 }); 411 });
411 } 412 }
412 } 413 }
@@ -430,7 +431,7 @@ public sealed class BSPrim : BSPhysObject
430 if (!Linkset.IsRoot(this)) 431 if (!Linkset.IsRoot(this))
431 { 432 {
432 // Children move around because tied to parent. Get a fresh value. 433 // Children move around because tied to parent. Get a fresh value.
433 _orientation = BulletSimAPI.GetObjectOrientation(_scene.WorldID, LocalID); 434 _orientation = BulletSimAPI.GetOrientation2(BSBody.Ptr);
434 } 435 }
435 return _orientation; 436 return _orientation;
436 } 437 }
@@ -441,7 +442,7 @@ public sealed class BSPrim : BSPhysObject
441 { 442 {
442 // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); 443 // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
443 DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation); 444 DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
444 BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); 445 BulletSimAPI.SetTranslation2(BSBody.Ptr, _position, _orientation);
445 }); 446 });
446 } 447 }
447 } 448 }
@@ -483,31 +484,37 @@ public sealed class BSPrim : BSPhysObject
483 { 484 {
484 // If it's becoming dynamic, it will need hullness 485 // If it's becoming dynamic, it will need hullness
485 VerifyCorrectPhysicalShape(); 486 VerifyCorrectPhysicalShape();
487 UpdatePhysicalParameters();
488 }
486 489
490 private void UpdatePhysicalParameters()
491 {
492 /*
487 // Bullet wants static objects to have a mass of zero 493 // Bullet wants static objects to have a mass of zero
488 float mass = IsStatic ? 0f : _mass; 494 float mass = IsStatic ? 0f : _mass;
489 495
490 BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), mass); 496 BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), mass);
491 /* 497 */
492 BulletSimAPI.RemoveObjectFromWorld2(Scene.World.Ptr, BSBody.Ptr); 498 BulletSimAPI.RemoveObjectFromWorld2(Scene.World.Ptr, BSBody.Ptr);
493 499
494 // Set up the object physicalness (static or dynamic) 500 // Set up the object physicalness (does gravity and collisions move this object)
495 MakeDynamic(); 501 MakeDynamic(IsStatic);
496 502
497 // Make solid or not and arrange for collisions, etc 503 // Make solid or not (do things bounce off or pass through this object)
498 MakeSolid(); 504 MakeSolid(IsSolid);
499 505
500 m_currentCollisionFlags = BulletSimAPI.GetCollisionFlags2(BSBody.Ptr); 506 // Arrange for collisions events if the simulator wants them
507 EnableCollisions(SubscribedEvents());
501 508
502 BulletSimAPI.AddObjectToWorld2(Scene.World.Ptr, BSBody.Ptr); 509 BulletSimAPI.AddObjectToWorld2(Scene.World.Ptr, BSBody.Ptr);
503 */
504 510
505 // Recompute any linkset parameters. 511 // Recompute any linkset parameters.
506 // When going from non-physical to physical, this re-enables the constraints that 512 // When going from non-physical to physical, this re-enables the constraints that
507 // had been automatically disabled when the mass was set to zero. 513 // had been automatically disabled when the mass was set to zero.
508 Linkset.Refresh(this); 514 Linkset.Refresh(this);
509 515
510 DetailLog("{0},BSPrim.SetObjectDynamic,taint,static={1},solid={2},mass={3}, cf={4}", LocalID, IsStatic, IsSolid, mass, m_currentCollisionFlags); 516 DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,static={1},solid={2},mass={3}, cf={4}",
517 LocalID, IsStatic, IsSolid, _mass, m_currentCollisionFlags);
511 } 518 }
512 519
513 // "Making dynamic" means changing to and from static. 520 // "Making dynamic" means changing to and from static.
@@ -515,39 +522,84 @@ public sealed class BSPrim : BSPhysObject
515 // When dynamic, the object can fall and be pushed by others. 522 // When dynamic, the object can fall and be pushed by others.
516 // This is independent of its 'solidness' which controls what passes through 523 // This is independent of its 'solidness' which controls what passes through
517 // this object and what interacts with it. 524 // this object and what interacts with it.
518 private void MakeDynamic() 525 private void MakeDynamic(bool makeStatic)
519 { 526 {
520 if (IsStatic) 527 if (makeStatic)
521 { 528 {
522 // Become a Bullet 'static' object type 529 // Become a Bullet 'static' object type
523 BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT); 530 m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
524 // Stop all movement 531 // Stop all movement
525 BulletSimAPI.ClearAllForces2(BSBody.Ptr); 532 BulletSimAPI.ClearAllForces2(BSBody.Ptr);
533 // Center of mass is at the center of the object
534 BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.BSBody.Ptr, _position, _orientation);
526 // Mass is zero which disables a bunch of physics stuff in Bullet 535 // Mass is zero which disables a bunch of physics stuff in Bullet
527 BulletSimAPI.SetMassProps2(BSBody.Ptr, 0f, OMV.Vector3.Zero); 536 BulletSimAPI.SetMassProps2(BSBody.Ptr, 0f, OMV.Vector3.Zero);
528 // There is no inertia in a static object 537 // There is no inertia in a static object
529 BulletSimAPI.UpdateInertiaTensor2(BSBody.Ptr); 538 BulletSimAPI.UpdateInertiaTensor2(BSBody.Ptr);
539 // There can be special things needed for implementing linksets
540 Linkset.MakeStatic(this);
530 // The activation state is 'sleeping' so Bullet will not try to act on it 541 // The activation state is 'sleeping' so Bullet will not try to act on it
531 BulletSimAPI.ForceActivationState2(BSBody.Ptr, ActivationState.ISLAND_SLEEPING); 542 BulletSimAPI.ForceActivationState2(BSBody.Ptr, ActivationState.ISLAND_SLEEPING);
532 } 543 }
533 else 544 else
534 { 545 {
535 // Not a Bullet static object 546 // Not a Bullet static object
536 BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT); 547 m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
548
549 // Set various physical properties so internal things will get computed correctly as they are set
550 BulletSimAPI.SetFriction2(BSBody.Ptr, Scene.Params.defaultFriction);
551 BulletSimAPI.SetRestitution2(BSBody.Ptr, Scene.Params.defaultRestitution);
552 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
553 BulletSimAPI.SetInterpolationLinearVelocity2(BSBody.Ptr, OMV.Vector3.Zero);
554 BulletSimAPI.SetInterpolationAngularVelocity2(BSBody.Ptr, OMV.Vector3.Zero);
555 BulletSimAPI.SetInterpolationVelocity2(BSBody.Ptr, OMV.Vector3.Zero, OMV.Vector3.Zero);
556
537 // A dynamic object has mass 557 // A dynamic object has mass
538 BulletSimAPI.SetMassProps2(BSBody.Ptr, _mass, OMV.Vector3.Zero);
539 // The shape is interesting and has mass and a center of gravity
540 IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.Ptr); 558 IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.Ptr);
541 BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, _mass, OMV.Vector3.Zero); 559 OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, Linkset.LinksetMass);
560 BulletSimAPI.SetMassProps2(BSBody.Ptr, _mass, inertia);
542 // Inertia is based on our new mass 561 // Inertia is based on our new mass
543 BulletSimAPI.UpdateInertiaTensor2(BSBody.Ptr); 562 BulletSimAPI.UpdateInertiaTensor2(BSBody.Ptr);
563
564 // Various values for simulation limits
565 BulletSimAPI.SetDamping2(BSBody.Ptr, Scene.Params.linearDamping, Scene.Params.angularDamping);
566 BulletSimAPI.SetDeactivationTime2(BSBody.Ptr, Scene.Params.deactivationTime);
567 BulletSimAPI.SetSleepingThresholds2(BSBody.Ptr, Scene.Params.linearSleepingThreshold, Scene.Params.angularSleepingThreshold);
568 BulletSimAPI.SetContactProcessingThreshold2(BSBody.Ptr, Scene.Params.contactProcessingThreshold);
569
570 // There can be special things needed for implementing linksets
571 Linkset.MakeDynamic(this);
572
544 // Force activation of the object so Bullet will act on it. 573 // Force activation of the object so Bullet will act on it.
545 BulletSimAPI.Activate2(BSBody.Ptr, true); 574 BulletSimAPI.Activate2(BSBody.Ptr, true);
546 } 575 }
547 } 576 }
548 577
549 private void MakeSolid() 578 // "Making solid" means that other object will not pass through this object.
579 private void MakeSolid(bool makeSolid)
550 { 580 {
581 if (makeSolid)
582 {
583 // Easy in Bullet -- just remove the object flag that controls collision response
584 m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
585 }
586 else
587 {
588 m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
589 }
590 }
591
592 // Turn on or off the flag controlling whether collision events are returned to the simulator.
593 private void EnableCollisions(bool wantsCollisionEvents)
594 {
595 if (wantsCollisionEvents)
596 {
597 m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
598 }
599 else
600 {
601 m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
602 }
551 } 603 }
552 604
553 // prims don't fly 605 // prims don't fly
@@ -607,7 +659,7 @@ public sealed class BSPrim : BSPhysObject
607 _scene.TaintedObject("BSPrim.setRotationalVelocity", delegate() 659 _scene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
608 { 660 {
609 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); 661 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
610 BulletSimAPI.SetObjectAngularVelocity(_scene.WorldID, LocalID, _rotationalVelocity); 662 BulletSimAPI.SetAngularVelocity2(BSBody.Ptr, _rotationalVelocity);
611 }); 663 });
612 } 664 }
613 } 665 }
@@ -624,7 +676,10 @@ public sealed class BSPrim : BSPhysObject
624 _scene.TaintedObject("BSPrim.setBuoyancy", delegate() 676 _scene.TaintedObject("BSPrim.setBuoyancy", delegate()
625 { 677 {
626 DetailLog("{0},BSPrim.SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy); 678 DetailLog("{0},BSPrim.SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
627 BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy); 679 // Buoyancy is faked by changing the gravity applied to the object
680 float grav = Scene.Params.gravity * (1f - _buoyancy);
681 BulletSimAPI.SetGravity2(BSBody.Ptr, new OMV.Vector3(0f, 0f, grav));
682 // BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy);
628 }); 683 });
629 } 684 }
630 } 685 }
@@ -686,8 +741,8 @@ public sealed class BSPrim : BSPhysObject
686 } 741 }
687 m_accumulatedForces.Clear(); 742 m_accumulatedForces.Clear();
688 } 743 }
689 DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, _force); 744 DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, fSum);
690 // For unknown reason, "ApplyCentralForce" is really additive. 745 // For unknown reasons, "ApplyCentralForce" adds this force to the total force on the object.
691 BulletSimAPI.ApplyCentralForce2(BSBody.Ptr, fSum); 746 BulletSimAPI.ApplyCentralForce2(BSBody.Ptr, fSum);
692 }); 747 });
693 } 748 }
@@ -1023,36 +1078,44 @@ public sealed class BSPrim : BSPhysObject
1023 }// end CalculateMass 1078 }// end CalculateMass
1024 #endregion Mass Calculation 1079 #endregion Mass Calculation
1025 1080
1026 // Create the geometry information in Bullet for later use 1081 // Create the geometry information in Bullet for later use.
1027 // The objects needs a hull if it's physical otherwise a mesh is enough 1082 // The objects needs a hull if it's physical otherwise a mesh is enough.
1028 // No locking here because this is done when we know physics is not simulating 1083 // No locking here because this is done when we know physics is not simulating.
1029 // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used 1084 // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used.
1030 // Returns 'true' if the geometry was rebuilt 1085 // Returns 'true' if the geometry was rebuilt.
1086 // Called at taint-time!
1031 private bool CreateGeom(bool forceRebuild) 1087 private bool CreateGeom(bool forceRebuild)
1032 { 1088 {
1033 // the mesher thought this was too simple to mesh. Use a native Bullet collision shape.
1034 bool ret = false; 1089 bool ret = false;
1035 if (!_scene.NeedsMeshing(_pbs)) 1090 bool haveShape = false;
1091
1092 // If the prim attributes are simple, this could be a simple Bullet native shape
1093 if ((_pbs.SculptEntry && !Scene.ShouldMeshSculptedPrim)
1094 || (_pbs.ProfileBegin == 0 && _pbs.ProfileEnd == 0
1095 && _pbs.ProfileHollow == 0
1096 && _pbs.PathTwist == 0 && _pbs.PathTwistBegin == 0
1097 && _pbs.PathBegin == 0 && _pbs.PathEnd == 0
1098 && _pbs.PathTaperX == 0 && _pbs.PathTaperY == 0
1099 && _pbs.PathScaleX == 100 && _pbs.PathScaleY == 100
1100 && _pbs.PathShearX == 0 && _pbs.PathShearY == 0) )
1036 { 1101 {
1037 if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1) 1102 if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1)
1038 { 1103 {
1039 // if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z) 1104 haveShape = true;
1040 // { 1105 if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_SPHERE))
1041 // m_log.DebugFormat("{0}: CreateGeom: Defaulting to sphere of size {1}", LogHeader, _size); 1106 {
1042 if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_SPHERE)) 1107 DetailLog("{0},BSPrim.CreateGeom,sphere (force={1}", LocalID, forceRebuild);
1043 { 1108 _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE;
1044 DetailLog("{0},BSPrim.CreateGeom,sphere (force={1}", LocalID, forceRebuild); 1109 // Bullet native objects are scaled by the Bullet engine so pass the size in
1045 _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE; 1110 _scale = _size;
1046 // Bullet native objects are scaled by the Bullet engine so pass the size in 1111 // TODO: do we need to check for and destroy a mesh or hull that might have been left from before?
1047 _scale = _size; 1112 ret = true;
1048 // TODO: do we need to check for and destroy a mesh or hull that might have been left from before? 1113 }
1049 ret = true;
1050 }
1051 // }
1052 } 1114 }
1053 else 1115 else
1054 { 1116 {
1055 // m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, type={2}, size={3}", LogHeader, LocalID, _shapeType, _size); 1117 // m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, type={2}, size={3}", LogHeader, LocalID, _shapeType, _size);
1118 haveShape = true;
1056 if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_BOX)) 1119 if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_BOX))
1057 { 1120 {
1058 DetailLog("{0},BSPrim.CreateGeom,box (force={1})", LocalID, forceRebuild); 1121 DetailLog("{0},BSPrim.CreateGeom,box (force={1})", LocalID, forceRebuild);
@@ -1063,16 +1126,16 @@ public sealed class BSPrim : BSPhysObject
1063 } 1126 }
1064 } 1127 }
1065 } 1128 }
1066 else 1129 // If a simple shape isn't happening, create a mesh and possibly a hull
1130 if (!haveShape)
1067 { 1131 {
1068 if (IsPhysical) 1132 if (IsPhysical)
1069 { 1133 {
1070 if (forceRebuild || _hullKey == 0) 1134 if (forceRebuild || _hullKey == 0)
1071 { 1135 {
1072 // physical objects require a hull for interaction. 1136 // physical objects require a hull for interaction.
1073 // This will create the mesh if it doesn't already exist 1137 // This also creates the mesh if it doesn't already exist
1074 CreateGeomHull(); 1138 ret = CreateGeomHull();
1075 ret = true;
1076 } 1139 }
1077 } 1140 }
1078 else 1141 else
@@ -1080,8 +1143,7 @@ public sealed class BSPrim : BSPhysObject
1080 if (forceRebuild || _meshKey == 0) 1143 if (forceRebuild || _meshKey == 0)
1081 { 1144 {
1082 // Static (non-physical) objects only need a mesh for bumping into 1145 // Static (non-physical) objects only need a mesh for bumping into
1083 CreateGeomMesh(); 1146 ret = CreateGeomMesh();
1084 ret = true;
1085 } 1147 }
1086 } 1148 }
1087 } 1149 }
@@ -1089,7 +1151,9 @@ public sealed class BSPrim : BSPhysObject
1089 } 1151 }
1090 1152
1091 // No locking here because this is done when we know physics is not simulating 1153 // No locking here because this is done when we know physics is not simulating
1092 private void CreateGeomMesh() 1154 // Returns 'true' of a mesh was actually rebuild (we could also have one of these specs).
1155 // Called at taint-time!
1156 private bool CreateGeomMesh()
1093 { 1157 {
1094 // level of detail based on size and type of the object 1158 // level of detail based on size and type of the object
1095 float lod = _scene.MeshLOD; 1159 float lod = _scene.MeshLOD;
@@ -1103,7 +1167,7 @@ public sealed class BSPrim : BSPhysObject
1103 // m_log.DebugFormat("{0}: CreateGeomMesh: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _meshKey, newMeshKey); 1167 // m_log.DebugFormat("{0}: CreateGeomMesh: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _meshKey, newMeshKey);
1104 1168
1105 // if this new shape is the same as last time, don't recreate the mesh 1169 // if this new shape is the same as last time, don't recreate the mesh
1106 if (_meshKey == newMeshKey) return; 1170 if (_meshKey == newMeshKey) return false;
1107 1171
1108 DetailLog("{0},BSPrim.CreateGeomMesh,create,key={1}", LocalID, newMeshKey); 1172 DetailLog("{0},BSPrim.CreateGeomMesh,create,key={1}", LocalID, newMeshKey);
1109 // Since we're recreating new, get rid of any previously generated shape 1173 // Since we're recreating new, get rid of any previously generated shape
@@ -1140,19 +1204,19 @@ public sealed class BSPrim : BSPhysObject
1140 _shapeType = ShapeData.PhysicsShapeType.SHAPE_MESH; 1204 _shapeType = ShapeData.PhysicsShapeType.SHAPE_MESH;
1141 // meshes are already scaled by the meshmerizer 1205 // meshes are already scaled by the meshmerizer
1142 _scale = new OMV.Vector3(1f, 1f, 1f); 1206 _scale = new OMV.Vector3(1f, 1f, 1f);
1143 DetailLog("{0},BSPrim.CreateGeomMesh,done", LocalID); 1207 return true;
1144 return;
1145 } 1208 }
1146 1209
1147 // No locking here because this is done when we know physics is not simulating 1210 // No locking here because this is done when we know physics is not simulating
1148 private void CreateGeomHull() 1211 // Returns 'true' of a mesh was actually rebuild (we could also have one of these specs).
1212 private bool CreateGeomHull()
1149 { 1213 {
1150 float lod = _pbs.SculptEntry ? _scene.SculptLOD : _scene.MeshLOD; 1214 float lod = _pbs.SculptEntry ? _scene.SculptLOD : _scene.MeshLOD;
1151 ulong newHullKey = (ulong)_pbs.GetMeshKey(_size, lod); 1215 ulong newHullKey = (ulong)_pbs.GetMeshKey(_size, lod);
1152 // m_log.DebugFormat("{0}: CreateGeomHull: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _hullKey, newHullKey); 1216 // m_log.DebugFormat("{0}: CreateGeomHull: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _hullKey, newHullKey);
1153 1217
1154 // if the hull hasn't changed, don't rebuild it 1218 // if the hull hasn't changed, don't rebuild it
1155 if (newHullKey == _hullKey) return; 1219 if (newHullKey == _hullKey) return false;
1156 1220
1157 DetailLog("{0},BSPrim.CreateGeomHull,create,oldKey={1},newKey={2}", LocalID, _hullKey, newHullKey); 1221 DetailLog("{0},BSPrim.CreateGeomHull,create,oldKey={1},newKey={2}", LocalID, _hullKey, newHullKey);
1158 1222
@@ -1255,7 +1319,7 @@ public sealed class BSPrim : BSPhysObject
1255 // meshes are already scaled by the meshmerizer 1319 // meshes are already scaled by the meshmerizer
1256 _scale = new OMV.Vector3(1f, 1f, 1f); 1320 _scale = new OMV.Vector3(1f, 1f, 1f);
1257 DetailLog("{0},BSPrim.CreateGeomHull,done", LocalID); 1321 DetailLog("{0},BSPrim.CreateGeomHull,done", LocalID);
1258 return; 1322 return true;
1259 } 1323 }
1260 1324
1261 // Callback from convex hull creater with a newly created hull. 1325 // Callback from convex hull creater with a newly created hull.
@@ -1268,20 +1332,12 @@ public sealed class BSPrim : BSPhysObject
1268 1332
1269 private void VerifyCorrectPhysicalShape() 1333 private void VerifyCorrectPhysicalShape()
1270 { 1334 {
1271 if (IsStatic) 1335 if (!IsStatic)
1272 {
1273 // if static, we don't need a hull so, if there is one, rebuild without it
1274 if (_hullKey != 0)
1275 {
1276 RecreateGeomAndObject();
1277 }
1278 }
1279 else
1280 { 1336 {
1281 // if not static, it will need a hull to efficiently collide with things 1337 // if not static, it will need a hull to efficiently collide with things
1282 if (_hullKey == 0) 1338 if (_hullKey == 0)
1283 { 1339 {
1284 RecreateGeomAndObject(); 1340 CreateGeomAndObject(false);
1285 } 1341 }
1286 1342
1287 } 1343 }
@@ -1300,8 +1356,9 @@ public sealed class BSPrim : BSPhysObject
1300 // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type); 1356 // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type);
1301 bool ret = BulletSimAPI.CreateObject(_scene.WorldID, shape); 1357 bool ret = BulletSimAPI.CreateObject(_scene.WorldID, shape);
1302 1358
1303 // the CreateObject() may have recreated the rigid body. Make sure we have the latest. 1359 // the CreateObject() may have recreated the rigid body. Make sure we have the latest address.
1304 BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID)); 1360 BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID));
1361 BSShape = new BulletShape(BulletSimAPI.GetCollisionShape2(BSBody.Ptr));
1305 1362
1306 return ret; 1363 return ret;
1307 } 1364 }
@@ -1325,15 +1382,20 @@ public sealed class BSPrim : BSPhysObject
1325 shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue; 1382 shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue;
1326 } 1383 }
1327 1384
1328
1329 // Rebuild the geometry and object. 1385 // Rebuild the geometry and object.
1330 // This is called when the shape changes so we need to recreate the mesh/hull. 1386 // This is called when the shape changes so we need to recreate the mesh/hull.
1331 // No locking here because this is done when the physics engine is not simulating 1387 // No locking here because this is done when the physics engine is not simulating
1332 private void RecreateGeomAndObject() 1388 private void CreateGeomAndObject(bool forceRebuild)
1333 { 1389 {
1334 // m_log.DebugFormat("{0}: RecreateGeomAndObject. lID={1}", LogHeader, _localID); 1390 // m_log.DebugFormat("{0}: CreateGeomAndObject. lID={1}, force={2}", LogHeader, _localID, forceRebuild);
1335 if (CreateGeom(true)) 1391 // Create the geometry that will make up the object
1392 if (CreateGeom(forceRebuild))
1393 {
1394 // Create the object and place it into the world
1336 CreateObject(); 1395 CreateObject();
1396 // Make sure the properties are set on the new object
1397 UpdatePhysicalParameters();
1398 }
1337 return; 1399 return;
1338 } 1400 }
1339 1401
@@ -1414,12 +1476,14 @@ public sealed class BSPrim : BSPhysObject
1414 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", 1476 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
1415 LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); 1477 LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
1416 1478
1479 // BulletSimAPI.DumpRigidBody2(Scene.World.Ptr, BSBody.Ptr);
1480
1417 base.RequestPhysicsterseUpdate(); 1481 base.RequestPhysicsterseUpdate();
1418 } 1482 }
1419 /* 1483 /*
1420 else 1484 else
1421 { 1485 {
1422 // For debugging, we also report the movement of children 1486 // For debugging, we can also report the movement of children
1423 DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}", 1487 DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
1424 LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, 1488 LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
1425 entprop.Acceleration, entprop.RotationalVelocity); 1489 entprop.Acceleration, entprop.RotationalVelocity);
@@ -1430,15 +1494,15 @@ public sealed class BSPrim : BSPhysObject
1430 // I've collided with something 1494 // I've collided with something
1431 // Called at taint time from within the Step() function 1495 // Called at taint time from within the Step() function
1432 CollisionEventUpdate collisionCollection; 1496 CollisionEventUpdate collisionCollection;
1433 public override void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) 1497 public override bool Collide(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
1434 { 1498 {
1435 // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith); 1499 bool ret = false;
1436 1500
1437 // The following lines make IsColliding() and IsCollidingGround() work 1501 // The following lines make IsColliding() and IsCollidingGround() work
1438 _collidingStep = _scene.SimulationStep; 1502 _collidingStep = Scene.SimulationStep;
1439 if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID) 1503 if (collidingWith <= Scene.TerrainManager.HighestTerrainID)
1440 { 1504 {
1441 _collidingGroundStep = _scene.SimulationStep; 1505 _collidingGroundStep = Scene.SimulationStep;
1442 } 1506 }
1443 1507
1444 // DetailLog("{0},BSPrim.Collison,call,with={1}", LocalID, collidingWith); 1508 // DetailLog("{0},BSPrim.Collison,call,with={1}", LocalID, collidingWith);
@@ -1446,21 +1510,23 @@ public sealed class BSPrim : BSPhysObject
1446 // prims in the same linkset cannot collide with each other 1510 // prims in the same linkset cannot collide with each other
1447 if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID)) 1511 if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
1448 { 1512 {
1449 return; 1513 return ret;
1450 } 1514 }
1451 1515
1452 // if someone has subscribed for collision events.... 1516 // if someone has subscribed for collision events....
1453 if (SubscribedEvents()) { 1517 if (SubscribedEvents()) {
1454 // throttle the collisions to the number of milliseconds specified in the subscription 1518 // throttle the collisions to the number of milliseconds specified in the subscription
1455 int nowTime = _scene.SimulationNowTime; 1519 int nowTime = Scene.SimulationNowTime;
1456 if (nowTime >= _nextCollisionOkTime) { 1520 if (nowTime >= _nextCollisionOkTime) {
1457 _nextCollisionOkTime = nowTime + _subscribedEventsMs; 1521 _nextCollisionOkTime = nowTime + _subscribedEventsMs;
1458 1522
1459 if (collisionCollection == null) 1523 if (collisionCollection == null)
1460 collisionCollection = new CollisionEventUpdate(); 1524 collisionCollection = new CollisionEventUpdate();
1461 collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); 1525 collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
1526 ret = true;
1462 } 1527 }
1463 } 1528 }
1529 return ret;
1464 } 1530 }
1465 1531
1466 // The scene is telling us it's time to pass our collected collisions into the simulator 1532 // The scene is telling us it's time to pass our collected collisions into the simulator
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 4a468af..52997dd 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -56,7 +56,6 @@ using OpenMetaverse;
56// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect 56// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
57// Implement LockAngularMotion 57// Implement LockAngularMotion
58// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) 58// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
59// Does NeedsMeshing() really need to exclude all the different shapes?
60// Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet. 59// Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet.
61// Add PID movement operations. What does ScenePresence.MoveToTarget do? 60// Add PID movement operations. What does ScenePresence.MoveToTarget do?
62// Check terrain size. 128 or 127? 61// Check terrain size. 128 or 127?
@@ -79,7 +78,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
79 private HashSet<BSPhysObject> m_objectsWithCollisions = new HashSet<BSPhysObject>(); 78 private HashSet<BSPhysObject> m_objectsWithCollisions = new HashSet<BSPhysObject>();
80 // Following is a kludge and can be removed when avatar animation updating is 79 // Following is a kludge and can be removed when avatar animation updating is
81 // moved to a better place. 80 // moved to a better place.
82 private HashSet<BSCharacter> m_avatarsWithCollisions = new HashSet<BSCharacter>(); 81 private HashSet<BSPhysObject> m_avatarsWithCollisions = new HashSet<BSPhysObject>();
83 82
84 // List of all the objects that have vehicle properties and should be called 83 // List of all the objects that have vehicle properties and should be called
85 // to update each physics step. 84 // to update each physics step.
@@ -111,11 +110,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
111 private long m_simulationStep = 0; 110 private long m_simulationStep = 0;
112 public long SimulationStep { get { return m_simulationStep; } } 111 public long SimulationStep { get { return m_simulationStep; } }
113 112
114 // The length of the last timestep we were asked to simulate.
115 // This is used by the vehicle code. Since the vehicle code is called
116 // once per simulation step, its constants need to be scaled by this.
117 public float LastSimulatedTimestep { get; private set; }
118
119 // A value of the time now so all the collision and update routines do not have to get their own 113 // A value of the time now so all the collision and update routines do not have to get their own
120 // Set to 'now' just before all the prims and actors are called for collisions and updates 114 // Set to 'now' just before all the prims and actors are called for collisions and updates
121 public int SimulationNowTime { get; private set; } 115 public int SimulationNowTime { get; private set; }
@@ -132,8 +126,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
132 private EntityProperties[] m_updateArray; 126 private EntityProperties[] m_updateArray;
133 private GCHandle m_updateArrayPinnedHandle; 127 private GCHandle m_updateArrayPinnedHandle;
134 128
135 private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed 129 public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
136 private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes 130 public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
137 131
138 public float PID_D { get; private set; } // derivative 132 public float PID_D { get; private set; } // derivative
139 public float PID_P { get; private set; } // proportional 133 public float PID_P { get; private set; } // proportional
@@ -153,6 +147,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
153 { 147 {
154 get { return new Vector3(0f, 0f, Params.gravity); } 148 get { return new Vector3(0f, 0f, Params.gravity); }
155 } 149 }
150 // Just the Z value of the gravity
151 public float DefaultGravityZ
152 {
153 get { return Params.gravity; }
154 }
156 155
157 public float MaximumObjectMass { get; private set; } 156 public float MaximumObjectMass { get; private set; }
158 157
@@ -171,8 +170,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
171 callback = c; 170 callback = c;
172 } 171 }
173 } 172 }
173 private Object _taintLock = new Object(); // lock for using the next object
174 private List<TaintCallbackEntry> _taintedObjects; 174 private List<TaintCallbackEntry> _taintedObjects;
175 private Object _taintLock = new Object();
176 175
177 // A pointer to an instance if this structure is passed to the C++ code 176 // A pointer to an instance if this structure is passed to the C++ code
178 // Used to pass basic configuration values to the unmanaged code. 177 // Used to pass basic configuration values to the unmanaged code.
@@ -465,12 +464,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
465 int collidersCount = 0; 464 int collidersCount = 0;
466 IntPtr collidersPtr; 465 IntPtr collidersPtr;
467 466
468 LastSimulatedTimestep = timeStep;
469
470 // prevent simulation until we've been initialized 467 // prevent simulation until we've been initialized
471 if (!m_initialized) return 10.0f; 468 if (!m_initialized) return 5.0f;
472
473 int simulateStartTime = Util.EnvironmentTickCount();
474 469
475 // update the prim states while we know the physics engine is not busy 470 // update the prim states while we know the physics engine is not busy
476 int numTaints = _taintedObjects.Count; 471 int numTaints = _taintedObjects.Count;
@@ -478,6 +473,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
478 473
479 // Some of the prims operate with special vehicle properties 474 // Some of the prims operate with special vehicle properties
480 ProcessVehicles(timeStep); 475 ProcessVehicles(timeStep);
476 numTaints += _taintedObjects.Count;
481 ProcessTaints(); // the vehicles might have added taints 477 ProcessTaints(); // the vehicles might have added taints
482 478
483 // step the physical world one interval 479 // step the physical world one interval
@@ -506,6 +502,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
506 // Get a value for 'now' so all the collision and update routines don't have to get their own 502 // Get a value for 'now' so all the collision and update routines don't have to get their own
507 SimulationNowTime = Util.EnvironmentTickCount(); 503 SimulationNowTime = Util.EnvironmentTickCount();
508 504
505 // This is a kludge to get avatar movement updates.
506 // ODE sends collisions for avatars even if there are have been no collisions. This updates
507 // avatar animations and stuff.
508 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
509 m_objectsWithCollisions = new HashSet<BSPhysObject>(m_avatarsWithCollisions);
510
509 // If there were collisions, process them by sending the event to the prim. 511 // If there were collisions, process them by sending the event to the prim.
510 // Collisions must be processed before updates. 512 // Collisions must be processed before updates.
511 if (collidersCount > 0) 513 if (collidersCount > 0)
@@ -527,13 +529,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
527 bsp.SendCollisions(); 529 bsp.SendCollisions();
528 m_objectsWithCollisions.Clear(); 530 m_objectsWithCollisions.Clear();
529 531
530 // This is a kludge to get avatar movement updated.
531 // ODE sends collisions even if there are none and this is used to update
532 // avatar animations and stuff.
533 foreach (BSPhysObject bpo in m_avatarsWithCollisions)
534 bpo.SendCollisions();
535 // m_avatarsWithCollisions.Clear();
536
537 // If any of the objects had updated properties, tell the object it has been changed by the physics engine 532 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
538 if (updatedEntityCount > 0) 533 if (updatedEntityCount > 0)
539 { 534 {
@@ -544,7 +539,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
544 if (PhysObjects.TryGetValue(entprop.ID, out pobj)) 539 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
545 { 540 {
546 pobj.UpdateProperties(entprop); 541 pobj.UpdateProperties(entprop);
547 continue;
548 } 542 }
549 } 543 }
550 } 544 }
@@ -558,18 +552,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
558 } 552 }
559 } 553 }
560 554
561 // this is a waste since the outside routine also calcuates the physics simulation 555 // The physics engine returns the number of milliseconds it simulated this call.
562 // period. TODO: There should be a way of computing physics frames from simulator computation. 556 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
563 // long simulateTotalTime = Util.EnvironmentTickCountSubtract(simulateStartTime); 557 // Since Bullet normally does 5 or 6 substeps, this will normally sum to about 60 FPS.
564 // return (timeStep * (float)simulateTotalTime); 558 return numSubSteps * m_fixedTimeStep;
565
566 // TODO: FIX THIS: fps calculation possibly wrong.
567 // This calculation says 1/timeStep is the ideal frame rate. Any time added to
568 // that by the physics simulation gives a slower frame rate.
569 long totalSimulationTime = Util.EnvironmentTickCountSubtract(simulateStartTime);
570 if (totalSimulationTime >= timeStep)
571 return 0;
572 return 1f / (timeStep + totalSimulationTime);
573 } 559 }
574 560
575 // Something has collided 561 // Something has collided
@@ -580,28 +566,25 @@ public class BSScene : PhysicsScene, IPhysicsParameters
580 return; // don't send collisions to the terrain 566 return; // don't send collisions to the terrain
581 } 567 }
582 568
583 BSPhysObject collider = PhysObjects[localID]; 569 BSPhysObject collider;
584 // TODO: as of this code, terrain was not in the physical object list. 570 if (!PhysObjects.TryGetValue(localID, out collider))
585 // When BSTerrain is created and it will be in the list, we can remove
586 // the possibility that it's not there and just fetch the collidee.
587 BSPhysObject collidee = null;
588
589 ActorTypes type = ActorTypes.Prim;
590 if (collidingWith <= TerrainManager.HighestTerrainID)
591 {
592 type = ActorTypes.Ground;
593 }
594 else
595 { 571 {
596 collidee = PhysObjects[collidingWith]; 572 // If the object that is colliding cannot be found, just ignore the collision.
597 if (collidee is BSCharacter) 573 return;
598 type = ActorTypes.Agent;
599 } 574 }
600 575
576 // The terrain is not in the physical object list so 'collidee'
577 // can be null when Collide() is called.
578 BSPhysObject collidee = null;
579 PhysObjects.TryGetValue(collidingWith, out collidee);
580
601 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); 581 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
602 582
603 collider.Collide(collidingWith, collidee, type, collidePoint, collideNormal, penetration); 583 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
604 m_objectsWithCollisions.Add(collider); 584 {
585 // If a collision was posted, remember to send it to the simulator
586 m_objectsWithCollisions.Add(collider);
587 }
605 588
606 return; 589 return;
607 } 590 }
@@ -619,9 +602,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
619 public override void SetWaterLevel(float baseheight) 602 public override void SetWaterLevel(float baseheight)
620 { 603 {
621 m_waterLevel = baseheight; 604 m_waterLevel = baseheight;
622 // TODO: pass to physics engine so things will float?
623 } 605 }
624 public float GetWaterLevel() 606 // Someday....
607 public float GetWaterLevelAtXYZ(Vector3 loc)
625 { 608 {
626 return m_waterLevel; 609 return m_waterLevel;
627 } 610 }
@@ -659,121 +642,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
659 642
660 public override bool IsThreaded { get { return false; } } 643 public override bool IsThreaded { get { return false; } }
661 644
662 /// <summary>
663 /// Routine to figure out if we need to mesh this prim with our mesher
664 /// </summary>
665 /// <param name="pbs"></param>
666 /// <returns>true if the prim needs meshing</returns>
667 public bool NeedsMeshing(PrimitiveBaseShape pbs)
668 {
669 // most of this is redundant now as the mesher will return null if it cant mesh a prim
670 // but we still need to check for sculptie meshing being enabled so this is the most
671 // convenient place to do it for now...
672
673 // int iPropertiesNotSupportedDefault = 0;
674
675 if (pbs.SculptEntry && !_meshSculptedPrim)
676 {
677 // Render sculpties as boxes
678 return false;
679 }
680
681 // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet
682 // can use an internal representation for the prim
683 if (!_forceSimplePrimMeshing)
684 {
685 if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
686 || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
687 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z))
688 {
689
690 if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
691 && pbs.ProfileHollow == 0
692 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
693 && pbs.PathBegin == 0 && pbs.PathEnd == 0
694 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
695 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
696 && pbs.PathShearX == 0 && pbs.PathShearY == 0)
697 {
698 return false;
699 }
700 }
701 }
702
703 /* TODO: verify that the mesher will now do all these shapes
704 if (pbs.ProfileHollow != 0)
705 iPropertiesNotSupportedDefault++;
706
707 if ((pbs.PathBegin != 0) || pbs.PathEnd != 0)
708 iPropertiesNotSupportedDefault++;
709
710 if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0))
711 iPropertiesNotSupportedDefault++;
712
713 if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0)
714 iPropertiesNotSupportedDefault++;
715
716 if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100))
717 iPropertiesNotSupportedDefault++;
718
719 if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0))
720 iPropertiesNotSupportedDefault++;
721
722 if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight)
723 iPropertiesNotSupportedDefault++;
724
725 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))
726 iPropertiesNotSupportedDefault++;
727
728 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1)
729 iPropertiesNotSupportedDefault++;
730
731 // test for torus
732 if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square)
733 {
734 if (pbs.PathCurve == (byte)Extrusion.Curve1)
735 {
736 iPropertiesNotSupportedDefault++;
737 }
738 }
739 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
740 {
741 if (pbs.PathCurve == (byte)Extrusion.Straight)
742 {
743 iPropertiesNotSupportedDefault++;
744 }
745 // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits
746 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
747 {
748 iPropertiesNotSupportedDefault++;
749 }
750 }
751 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
752 {
753 if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2)
754 {
755 iPropertiesNotSupportedDefault++;
756 }
757 }
758 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
759 {
760 if (pbs.PathCurve == (byte)Extrusion.Straight)
761 {
762 iPropertiesNotSupportedDefault++;
763 }
764 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
765 {
766 iPropertiesNotSupportedDefault++;
767 }
768 }
769 if (iPropertiesNotSupportedDefault == 0)
770 {
771 return false;
772 }
773 */
774 return true;
775 }
776
777 // Calls to the PhysicsActors can't directly call into the physics engine 645 // Calls to the PhysicsActors can't directly call into the physics engine
778 // because it might be busy. We delay changes to a known time. 646 // because it might be busy. We delay changes to a known time.
779 // We rely on C#'s closure to save and restore the context for the delegate. 647 // We rely on C#'s closure to save and restore the context for the delegate.
@@ -782,7 +650,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
782 if (!m_initialized) return; 650 if (!m_initialized) return;
783 651
784 lock (_taintLock) 652 lock (_taintLock)
653 {
785 _taintedObjects.Add(new TaintCallbackEntry(ident, callback)); 654 _taintedObjects.Add(new TaintCallbackEntry(ident, callback));
655 }
656
786 return; 657 return;
787 } 658 }
788 659
@@ -919,14 +790,14 @@ public class BSScene : PhysicsScene, IPhysicsParameters
919 { 790 {
920 new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", 791 new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
921 ConfigurationParameters.numericTrue, 792 ConfigurationParameters.numericTrue,
922 (s,cf,p,v) => { s._meshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); }, 793 (s,cf,p,v) => { s.ShouldMeshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); },
923 (s) => { return s.NumericBool(s._meshSculptedPrim); }, 794 (s) => { return s.NumericBool(s.ShouldMeshSculptedPrim); },
924 (s,p,l,v) => { s._meshSculptedPrim = s.BoolNumeric(v); } ), 795 (s,p,l,v) => { s.ShouldMeshSculptedPrim = s.BoolNumeric(v); } ),
925 new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", 796 new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
926 ConfigurationParameters.numericFalse, 797 ConfigurationParameters.numericFalse,
927 (s,cf,p,v) => { s._forceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); }, 798 (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); },
928 (s) => { return s.NumericBool(s._forceSimplePrimMeshing); }, 799 (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); },
929 (s,p,l,v) => { s._forceSimplePrimMeshing = s.BoolNumeric(v); } ), 800 (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ),
930 801
931 new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", 802 new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
932 8f, 803 8f,
@@ -1162,8 +1033,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1162 (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); }, 1033 (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
1163 (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, 1034 (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; },
1164 (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), 1035 (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ),
1165 new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=none, 1=all. Default=0", 1036 new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
1166 0.0f, 1037 0.1f,
1167 (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, 1038 (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
1168 (s) => { return s.m_params[0].linkConstraintCFM; }, 1039 (s) => { return s.m_params[0].linkConstraintCFM; },
1169 (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), 1040 (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ),
@@ -1172,6 +1043,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1172 (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, 1043 (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
1173 (s) => { return s.m_params[0].linkConstraintERP; }, 1044 (s) => { return s.m_params[0].linkConstraintERP; },
1174 (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), 1045 (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ),
1046 new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
1047 40,
1048 (s,cf,p,v) => { s.m_params[0].linkConstraintSolverIterations = cf.GetFloat(p, v); },
1049 (s) => { return s.m_params[0].linkConstraintSolverIterations; },
1050 (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ),
1175 1051
1176 new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)", 1052 new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)",
1177 0f, 1053 0f,
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
index 47d7199..d48462e 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
@@ -154,27 +154,31 @@ public class BSTerrainManager
154 154
155 // The simulator wants to set a new heightmap for the terrain. 155 // The simulator wants to set a new heightmap for the terrain.
156 public void SetTerrain(float[] heightMap) { 156 public void SetTerrain(float[] heightMap) {
157 if (m_worldOffset != Vector3.Zero && m_parentScene != null) 157 float[] localHeightMap = heightMap;
158 m_physicsScene.TaintedObject("TerrainManager.SetTerrain", delegate()
158 { 159 {
159 // If a child of a mega-region, we shouldn't have any terrain allocated for us 160 if (m_worldOffset != Vector3.Zero && m_parentScene != null)
160 ReleaseGroundPlaneAndTerrain();
161 // If doing the mega-prim stuff and we are the child of the zero region,
162 // the terrain is added to our parent
163 if (m_parentScene is BSScene)
164 { 161 {
165 DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", 162 // If a child of a mega-region, we shouldn't have any terrain allocated for us
166 BSScene.DetailLogZero, m_worldOffset, m_worldMax); 163 ReleaseGroundPlaneAndTerrain();
167 ((BSScene)m_parentScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID, 164 // If doing the mega-prim stuff and we are the child of the zero region,
168 heightMap, m_worldOffset, m_worldOffset+DefaultRegionSize, false); 165 // the terrain is added to our parent
166 if (m_parentScene is BSScene)
167 {
168 DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
169 BSScene.DetailLogZero, m_worldOffset, m_worldMax);
170 ((BSScene)m_parentScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID,
171 localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true);
172 }
169 } 173 }
170 } 174 else
171 else 175 {
172 { 176 // If not doing the mega-prim thing, just change the terrain
173 // If not doing the mega-prim thing, just change the terrain 177 DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
174 DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
175 178
176 UpdateOrCreateTerrain(BSScene.TERRAIN_ID, heightMap, m_worldOffset, m_worldOffset+DefaultRegionSize, false); 179 UpdateOrCreateTerrain(BSScene.TERRAIN_ID, localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true);
177 } 180 }
181 });
178 } 182 }
179 183
180 // If called with no mapInfo for the terrain, this will create a new mapInfo and terrain 184 // If called with no mapInfo for the terrain, this will create a new mapInfo and terrain
@@ -319,6 +323,8 @@ public class BSTerrainManager
319 323
320 // Make sure the new shape is processed. 324 // Make sure the new shape is processed.
321 BulletSimAPI.Activate2(mapInfo.terrainBody.Ptr, true); 325 BulletSimAPI.Activate2(mapInfo.terrainBody.Ptr, true);
326
327 m_terrainModified = true;
322 }; 328 };
323 329
324 // There is the option to do the changes now (we're already in 'taint time'), or 330 // There is the option to do the changes now (we're already in 'taint time'), or
@@ -357,6 +363,8 @@ public class BSTerrainManager
357 m_heightMaps.Add(terrainRegionBase, mapInfo); 363 m_heightMaps.Add(terrainRegionBase, mapInfo);
358 // Build the terrain 364 // Build the terrain
359 UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true); 365 UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true);
366
367 m_terrainModified = true;
360 }; 368 };
361 369
362 // If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time. 370 // If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time.
@@ -383,7 +391,7 @@ public class BSTerrainManager
383 private float lastHeightTX = 999999f; 391 private float lastHeightTX = 999999f;
384 private float lastHeightTY = 999999f; 392 private float lastHeightTY = 999999f;
385 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT; 393 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
386 public float GetTerrainHeightAtXY(float tX, float tY) 394 private float GetTerrainHeightAtXY(float tX, float tY)
387 { 395 {
388 // You'd be surprized at the number of times this routine is called 396 // You'd be surprized at the number of times this routine is called
389 // with the same parameters as last time. 397 // with the same parameters as last time.
@@ -403,11 +411,18 @@ public class BSTerrainManager
403 { 411 {
404 float regionX = tX - offsetX; 412 float regionX = tX - offsetX;
405 float regionY = tY - offsetY; 413 float regionY = tY - offsetY;
406 if (regionX >= mapInfo.sizeX || regionX < 0f) regionX = 0;
407 if (regionY >= mapInfo.sizeY || regionY < 0f) regionY = 0;
408 int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX; 414 int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX;
409 ret = mapInfo.heightMap[mapIndex]; 415 try
410 m_terrainModified = false; 416 {
417 ret = mapInfo.heightMap[mapIndex];
418 }
419 catch
420 {
421 // Sometimes they give us wonky values of X and Y. Give a warning and return something.
422 m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, x={2}, y={3}",
423 LogHeader, terrainBaseXY, regionX, regionY);
424 ret = HEIGHT_GETHEIGHT_RET;
425 }
411 // DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXY,bX={1},baseY={2},szX={3},szY={4},regX={5},regY={6},index={7},ht={8}", 426 // DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXY,bX={1},baseY={2},szX={3},szY={4},regX={5},regY={6},index={7},ht={8}",
412 // BSScene.DetailLogZero, offsetX, offsetY, mapInfo.sizeX, mapInfo.sizeY, regionX, regionY, mapIndex, ret); 427 // BSScene.DetailLogZero, offsetX, offsetY, mapInfo.sizeX, mapInfo.sizeY, regionX, regionY, mapIndex, ret);
413 } 428 }
@@ -416,6 +431,7 @@ public class BSTerrainManager
416 m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", 431 m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
417 LogHeader, m_physicsScene.RegionName, tX, tY); 432 LogHeader, m_physicsScene.RegionName, tX, tY);
418 } 433 }
434 m_terrainModified = false;
419 lastHeight = ret; 435 lastHeight = ret;
420 return ret; 436 return ret;
421 } 437 }
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
index e579cf2..9221cdb 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
@@ -213,6 +213,7 @@ public struct ConfigurationParameters
213 public float linkConstraintTransMotorMaxForce; 213 public float linkConstraintTransMotorMaxForce;
214 public float linkConstraintERP; 214 public float linkConstraintERP;
215 public float linkConstraintCFM; 215 public float linkConstraintCFM;
216 public float linkConstraintSolverIterations;
216 217
217 public const float numericTrue = 1f; 218 public const float numericTrue = 1f;
218 public const float numericFalse = 0f; 219 public const float numericFalse = 0f;
@@ -395,23 +396,6 @@ public static extern bool DestroyMesh(uint worldID, System.UInt64 meshKey);
395[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 396[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
396public static extern bool CreateObject(uint worldID, ShapeData shapeData); 397public static extern bool CreateObject(uint worldID, ShapeData shapeData);
397 398
398/* Remove old functionality
399[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
400public static extern void CreateLinkset(uint worldID, int objectCount, ShapeData[] shapeDatas);
401
402[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
403public static extern void AddConstraint(uint worldID, uint id1, uint id2,
404 Vector3 frame1, Quaternion frame1rot,
405 Vector3 frame2, Quaternion frame2rot,
406 Vector3 lowLinear, Vector3 hiLinear, Vector3 lowAngular, Vector3 hiAngular);
407
408[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
409public static extern bool RemoveConstraintByID(uint worldID, uint id1);
410
411[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
412public static extern bool RemoveConstraint(uint worldID, uint id1, uint id2);
413 */
414
415[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 399[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
416public static extern Vector3 GetObjectPosition(uint WorldID, uint id); 400public static extern Vector3 GetObjectPosition(uint WorldID, uint id);
417 401
@@ -539,15 +523,27 @@ public static extern IntPtr BuildNativeShape2(IntPtr world,
539 float shapeType, float collisionMargin, Vector3 scale); 523 float shapeType, float collisionMargin, Vector3 scale);
540 524
541[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 525[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
542public static extern bool DeleteCollisionShape2(IntPtr world, IntPtr shape); 526public static extern bool IsNativeShape2(IntPtr shape);
543 527
544[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 528[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
545public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, Vector3 pos, Quaternion rot); 529public static extern IntPtr CreateCompoundShape2(IntPtr sim);
530
531[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
532public static extern void AddChildToCompoundShape2(IntPtr cShape, IntPtr addShape, Vector3 pos, Quaternion rot);
533
534[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
535public static extern void RemoveChildFromCompoundShape2(IntPtr cShape, IntPtr removeShape);
546 536
547[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 537[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
548public static extern IntPtr CreateBodyFromShapeAndInfo2(IntPtr sim, IntPtr shape, IntPtr constructionInfo); 538public static extern IntPtr CreateBodyFromShapeAndInfo2(IntPtr sim, IntPtr shape, IntPtr constructionInfo);
549 539
550[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 540[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
541public static extern bool DeleteCollisionShape2(IntPtr world, IntPtr shape);
542
543[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
544public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, Vector3 pos, Quaternion rot);
545
546[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
551public static extern IntPtr CreateBodyWithDefaultMotionState2(IntPtr shape, Vector3 pos, Quaternion rot); 547public static extern IntPtr CreateBodyWithDefaultMotionState2(IntPtr shape, Vector3 pos, Quaternion rot);
552 548
553[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 549[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
@@ -562,9 +558,6 @@ public static extern void DestroyObject2(IntPtr sim, IntPtr obj);
562// ===================================================================================== 558// =====================================================================================
563// Terrain creation and helper routines 559// Terrain creation and helper routines
564[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 560[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
565public static extern void DumpMapInfo(IntPtr sim, IntPtr manInfo);
566
567[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
568public static extern IntPtr CreateHeightMapInfo2(IntPtr sim, uint id, Vector3 minCoords, Vector3 maxCoords, 561public static extern IntPtr CreateHeightMapInfo2(IntPtr sim, uint id, Vector3 minCoords, Vector3 maxCoords,
569 [MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin); 562 [MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin);
570 563
@@ -1010,7 +1003,7 @@ public static extern void SetLocalScaling2(IntPtr shape, Vector3 scale);
1010public static extern Vector3 GetLocalScaling2(IntPtr shape); 1003public static extern Vector3 GetLocalScaling2(IntPtr shape);
1011 1004
1012[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 1005[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1013public static extern void CalculateLocalInertia2(IntPtr shape, float mass, Vector3 inertia); 1006public static extern Vector3 CalculateLocalInertia2(IntPtr shape, float mass);
1014 1007
1015[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 1008[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1016public static extern int GetShapeType2(IntPtr shape); 1009public static extern int GetShapeType2(IntPtr shape);
@@ -1027,6 +1020,12 @@ public static extern void SetCollisionFilterMask(IntPtr shape, uint filter, uint
1027// ===================================================================================== 1020// =====================================================================================
1028// Debugging 1021// Debugging
1029[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 1022[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1023public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject);
1024
1025[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1026public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo);
1027
1028[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1030public static extern void DumpPhysicsStatistics2(IntPtr sim); 1029public static extern void DumpPhysicsStatistics2(IntPtr sim);
1031 1030
1032} 1031}