diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs index 3218abc695..0cc1f86951 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs @@ -51,26 +51,33 @@ namespace OpenSim.Region.CoreModules.World.Archiver private static System.Text.ASCIIEncoding m_asciiEncoding = new System.Text.ASCIIEncoding(); - private Scene m_scene; + private Scene m_scene; private Stream m_loadStream; private string m_errorMessage; + + /// + /// Should the archive being loaded be merged with what is already on the region? + /// + private bool m_merge; /// /// Used to cache lookups for valid uuids. /// private IDictionary m_validUserUuids = new Dictionary(); - public ArchiveReadRequest(Scene scene, string loadPath) + public ArchiveReadRequest(Scene scene, string loadPath, bool merge) { m_scene = scene; m_loadStream = new GZipStream(GetStream(loadPath), CompressionMode.Decompress); m_errorMessage = String.Empty; + m_merge = merge; } - public ArchiveReadRequest(Scene scene, Stream loadStream) + public ArchiveReadRequest(Scene scene, Stream loadStream, bool merge) { m_scene = scene; m_loadStream = loadStream; + m_merge = merge; } /// @@ -92,8 +99,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver { TarArchiveReader archive = new TarArchiveReader(m_loadStream); - //AssetsDearchiver dearchiver = new AssetsDearchiver(m_scene.AssetCache); - string filePath = "ERROR"; byte[] data; @@ -103,6 +108,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver { //m_log.DebugFormat( // "[ARCHIVER]: Successfully read {0} ({1} bytes)}", filePath, data.Length); + if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType) { m_log.WarnFormat("[ARCHIVER]: Ignoring directory entry {0}", @@ -112,11 +118,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver { serialisedSceneObjects.Add(m_asciiEncoding.GetString(data)); } -// else if (filePath.Equals(ArchiveConstants.ASSETS_METADATA_PATH)) -// { -// string xml = m_asciiEncoding.GetString(data); -// dearchiver.AddAssetMetadata(xml); -// } else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH)) { if (LoadAsset(filePath, data)) @@ -124,11 +125,11 @@ namespace OpenSim.Region.CoreModules.World.Archiver else failedAssetRestores++; } - else if (filePath.StartsWith(ArchiveConstants.TERRAINS_PATH)) + else if (!m_merge && filePath.StartsWith(ArchiveConstants.TERRAINS_PATH)) { LoadTerrain(filePath, data); } - else if (filePath.StartsWith(ArchiveConstants.SETTINGS_PATH)) + else if (!m_merge && filePath.StartsWith(ArchiveConstants.SETTINGS_PATH)) { LoadRegionSettings(filePath, data); } @@ -155,8 +156,11 @@ namespace OpenSim.Region.CoreModules.World.Archiver m_errorMessage += String.Format("Failed to load {0} assets", failedAssetRestores); } - m_log.Info("[ARCHIVER]: Clearing all existing scene objects"); - m_scene.DeleteAllSceneObjects(); + if (!m_merge) + { + m_log.Info("[ARCHIVER]: Clearing all existing scene objects"); + m_scene.DeleteAllSceneObjects(); + } // Reload serialized prims m_log.InfoFormat("[ARCHIVER]: Loading {0} scene objects. Please wait.", serialisedSceneObjects.Count); @@ -182,13 +186,13 @@ namespace OpenSim.Region.CoreModules.World.Archiver foreach (SceneObjectPart part in sceneObject.Children.Values) { - if (!resolveUserUuid(part.CreatorID)) + if (!ResolveUserUuid(part.CreatorID)) part.CreatorID = masterAvatarId; - if (!resolveUserUuid(part.OwnerID)) + if (!ResolveUserUuid(part.OwnerID)) part.OwnerID = masterAvatarId; - if (!resolveUserUuid(part.LastOwnerID)) + if (!ResolveUserUuid(part.LastOwnerID)) part.LastOwnerID = masterAvatarId; // And zap any troublesome sit target information @@ -201,11 +205,11 @@ namespace OpenSim.Region.CoreModules.World.Archiver TaskInventoryDictionary inv = part.TaskInventory; foreach (KeyValuePair kvp in inv) { - if (!resolveUserUuid(kvp.Value.OwnerID)) + if (!ResolveUserUuid(kvp.Value.OwnerID)) { kvp.Value.OwnerID = masterAvatarId; } - if (!resolveUserUuid(kvp.Value.CreatorID)) + if (!ResolveUserUuid(kvp.Value.CreatorID)) { kvp.Value.CreatorID = masterAvatarId; } @@ -242,7 +246,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver /// /// /// - private bool resolveUserUuid(UUID uuid) + private bool ResolveUserUuid(UUID uuid) { if (!m_validUserUuids.ContainsKey(uuid)) { diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs index c1f5b18d75..6259662c08 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs @@ -80,16 +80,26 @@ namespace OpenSim.Region.CoreModules.World.Archiver } public void DearchiveRegion(string loadPath) + { + DearchiveRegion(loadPath, false); + } + + public void DearchiveRegion(string loadPath, bool merge) { m_log.InfoFormat( "[ARCHIVER]: Loading archive to region {0} from {1}", m_scene.RegionInfo.RegionName, loadPath); - new ArchiveReadRequest(m_scene, loadPath).DearchiveRegion(); - } + new ArchiveReadRequest(m_scene, loadPath, merge).DearchiveRegion(); + } public void DearchiveRegion(Stream loadStream) { - new ArchiveReadRequest(m_scene, loadStream).DearchiveRegion(); + DearchiveRegion(loadStream, false); } + + public void DearchiveRegion(Stream loadStream, bool merge) + { + new ArchiveReadRequest(m_scene, loadStream, merge).DearchiveRegion(); + } } } diff --git a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs index 77a3044c52..f201e74248 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs @@ -167,14 +167,13 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests [Test] public void TestLoadOarV0p2() { - log4net.Config.XmlConfigurator.Configure(); + //log4net.Config.XmlConfigurator.Configure(); MemoryStream archiveWriteStream = new MemoryStream(); TarArchiveWriter tar = new TarArchiveWriter(); tar.AddFile(ArchiveConstants.CONTROL_FILE_PATH, ArchiveWriteRequestExecution.Create0p2ControlFile()); - UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000020"); string part1Name = "object1"; PrimitiveBaseShape shape = PrimitiveBaseShape.CreateCylinder(); Vector3 groupPosition = new Vector3(90, 80, 70); @@ -216,5 +215,84 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests Assert.That( object1PartLoaded.OffsetPosition, Is.EqualTo(offsetPosition), "object1 offset position not equal"); } + + /// + /// Test merging a V0.2 OpenSim Region Archive into an existing scene + /// + [Test] + public void TestMergeOarV0p2() + { + log4net.Config.XmlConfigurator.Configure(); + + MemoryStream archiveWriteStream = new MemoryStream(); + + string part2Name = "objectMerge"; + PrimitiveBaseShape part2Shape = PrimitiveBaseShape.CreateCylinder(); + Vector3 part2GroupPosition = new Vector3(90, 80, 70); + Quaternion part2RotationOffset = new Quaternion(60, 70, 80, 90); + Vector3 part2OffsetPosition = new Vector3(20, 25, 30); + + // Create an oar file that we can use for the merge + { + ArchiverModule archiverModule = new ArchiverModule(); + SerialiserModule serialiserModule = new SerialiserModule(); + TerrainModule terrainModule = new TerrainModule(); + + Scene scene = SceneSetupHelpers.SetupScene(); + SceneSetupHelpers.SetupSceneModules(scene, archiverModule, serialiserModule, terrainModule); + + SceneObjectPart part2 + = new SceneObjectPart( + UUID.Zero, part2Shape, part2GroupPosition, part2RotationOffset, part2OffsetPosition); + part2.Name = part2Name; + SceneObjectGroup object2 = new SceneObjectGroup(part2); + + scene.AddNewSceneObject(object2, false); + + // Write out this scene + scene.EventManager.OnOarFileSaved += SaveCompleted; + archiverModule.ArchiveRegion(archiveWriteStream); + m_waitHandle.WaitOne(60000, true); + } + + { + ArchiverModule archiverModule = new ArchiverModule(); + SerialiserModule serialiserModule = new SerialiserModule(); + TerrainModule terrainModule = new TerrainModule(); + + Scene scene = SceneSetupHelpers.SetupScene(); + SceneSetupHelpers.SetupSceneModules(scene, archiverModule, serialiserModule, terrainModule); + + string part1Name = "objectExisting"; + PrimitiveBaseShape part1Shape = PrimitiveBaseShape.CreateCylinder(); + Vector3 part1GroupPosition = new Vector3(80, 70, 60); + Quaternion part1RotationOffset = new Quaternion(50, 60, 70, 80); + Vector3 part1OffsetPosition = new Vector3(15, 20, 25); + + SceneObjectPart part1 + = new SceneObjectPart( + UUID.Zero, part1Shape, part1GroupPosition, part1RotationOffset, part1OffsetPosition); + part1.Name = part1Name; + SceneObjectGroup object1 = new SceneObjectGroup(part1); + + scene.AddNewSceneObject(object1, false); + + // Merge in the archive we created earlier + byte[] archive = archiveWriteStream.ToArray(); + MemoryStream archiveReadStream = new MemoryStream(archive); + + archiverModule.DearchiveRegion(archiveReadStream, true); + + SceneObjectPart object1Existing = scene.GetSceneObjectPart(part1Name); + Assert.That(object1Existing, Is.Not.Null, "object1 was not present after merge"); + Assert.That(object1Existing.Name, Is.EqualTo(part1Name), "object1 names not identical after merge"); + Assert.That(object1Existing.GroupPosition, Is.EqualTo(part1GroupPosition), "object1 group position not equal after merge"); + + SceneObjectPart object2PartMerged = scene.GetSceneObjectPart(part2Name); + Assert.That(object2PartMerged, Is.Not.Null, "object2 was not present after merge"); + Assert.That(object2PartMerged.Name, Is.EqualTo(part2Name), "object2 names not identical after merge"); + Assert.That(object2PartMerged.GroupPosition, Is.EqualTo(part2GroupPosition), "object2 group position not equal after merge"); + } + } } } \ No newline at end of file diff --git a/OpenSim/Region/Framework/Interfaces/IRegionArchiverModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionArchiverModule.cs index 97afe8d6c4..601b83e249 100644 --- a/OpenSim/Region/Framework/Interfaces/IRegionArchiverModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IRegionArchiverModule.cs @@ -55,7 +55,7 @@ namespace OpenSim.Region.Framework.Interfaces void ArchiveRegion(Stream saveStream); /// - /// Dearchive the given region archive into the scene + /// Dearchive the given region archive. This replaces the existing scene. /// /// /// If you want notification of when it has completed then subscribe to the EventManager.OnOarFileLoaded event. @@ -64,12 +64,38 @@ namespace OpenSim.Region.Framework.Interfaces void DearchiveRegion(string loadPath); /// - /// Dearchive a region from a stream. + /// Dearchive the given region archive. This replaces the existing scene. + /// + /// + /// If you want notification of when it has completed then subscribe to the EventManager.OnOarFileLoaded event. + /// + /// + /// + /// If true, the loaded region merges with the existing one rather than replacing it. Any terrain or region + /// settings in the archive will be ignored. + /// + void DearchiveRegion(string loadPath, bool merge); + + /// + /// Dearchive a region from a stream. This replaces the existing scene. /// /// /// If you want notification of when it has completed then subscribe to the EventManager.OnOarFileLoaded event. /// /// void DearchiveRegion(Stream loadStream); + + /// + /// Dearchive a region from a stream. This replaces the existing scene. + /// + /// + /// If you want notification of when it has completed then subscribe to the EventManager.OnOarFileLoaded event. + /// + /// + /// + /// If true, the loaded region merges with the existing one rather than replacing it. Any terrain or region + /// settings in the archive will be ignored. + /// + void DearchiveRegion(Stream loadStream, bool merge); } }