diff options
author | Oren Hurvitz | 2012-07-24 19:48:08 +0300 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2012-09-14 20:25:03 +0100 |
commit | ce468215d576cc301a261d85bee9baa68a246ce6 (patch) | |
tree | ad2c6d7e3156bf8dab596f2772cc712d4f96c698 /OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs | |
parent | Don't store the unnecessary VERSIONMIN. VERSIONMAX, METHOD or UserID (present... (diff) | |
download | opensim-SC_OLD-ce468215d576cc301a261d85bee9baa68a246ce6.zip opensim-SC_OLD-ce468215d576cc301a261d85bee9baa68a246ce6.tar.gz opensim-SC_OLD-ce468215d576cc301a261d85bee9baa68a246ce6.tar.bz2 opensim-SC_OLD-ce468215d576cc301a261d85bee9baa68a246ce6.tar.xz |
Support multi-region OAR files
Merged ArchiveWriteRequestPreparation.cs and ArchiveWriteRequestExecution.cs. This simplifies the code, and it's faster to write each scene to the archive as it's found rather than all at once at the end.
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs | 492 |
1 files changed, 344 insertions, 148 deletions
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs index 4edaaca..2c34f4b 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs | |||
@@ -43,6 +43,7 @@ using OpenSim.Region.Framework.Scenes; | |||
43 | using Ionic.Zlib; | 43 | using Ionic.Zlib; |
44 | using GZipStream = Ionic.Zlib.GZipStream; | 44 | using GZipStream = Ionic.Zlib.GZipStream; |
45 | using CompressionMode = Ionic.Zlib.CompressionMode; | 45 | using CompressionMode = Ionic.Zlib.CompressionMode; |
46 | using OpenSim.Framework.Serialization.External; | ||
46 | 47 | ||
47 | namespace OpenSim.Region.CoreModules.World.Archiver | 48 | namespace OpenSim.Region.CoreModules.World.Archiver |
48 | { | 49 | { |
@@ -61,17 +62,29 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
61 | /// <summary> | 62 | /// <summary> |
62 | /// The maximum major version of OAR that we can write. | 63 | /// The maximum major version of OAR that we can write. |
63 | /// </summary> | 64 | /// </summary> |
64 | public static int MAX_MAJOR_VERSION = 0; | 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; } | ||
65 | 71 | ||
66 | /// <summary> | 72 | /// <summary> |
67 | /// Determine whether this archive will save assets. Default is true. | 73 | /// Determine whether this archive will save assets. Default is true. |
68 | /// </summary> | 74 | /// </summary> |
69 | public bool SaveAssets { get; set; } | 75 | public bool SaveAssets { get; set; } |
70 | 76 | ||
71 | protected ArchiverModule m_module; | 77 | /// <summary> |
72 | protected Scene m_scene; | 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; | ||
73 | protected Stream m_saveStream; | 84 | protected Stream m_saveStream; |
85 | protected TarArchiveWriter m_archiveWriter; | ||
74 | protected Guid m_requestId; | 86 | protected Guid m_requestId; |
87 | protected Dictionary<string, object> m_options; | ||
75 | 88 | ||
76 | /// <summary> | 89 | /// <summary> |
77 | /// Constructor | 90 | /// Constructor |
@@ -82,7 +95,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
82 | /// <exception cref="System.IO.IOException"> | 95 | /// <exception cref="System.IO.IOException"> |
83 | /// If there was a problem opening a stream for the file specified by the savePath | 96 | /// If there was a problem opening a stream for the file specified by the savePath |
84 | /// </exception> | 97 | /// </exception> |
85 | public ArchiveWriteRequestPreparation(ArchiverModule module, string savePath, Guid requestId) : this(module, requestId) | 98 | public ArchiveWriteRequestPreparation(Scene scene, string savePath, Guid requestId) : this(scene, requestId) |
86 | { | 99 | { |
87 | try | 100 | try |
88 | { | 101 | { |
@@ -100,26 +113,23 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
100 | /// <summary> | 113 | /// <summary> |
101 | /// Constructor. | 114 | /// Constructor. |
102 | /// </summary> | 115 | /// </summary> |
103 | /// <param name="module">Calling module</param> | 116 | /// <param name="scene">The root scene to archive</param> |
104 | /// <param name="saveStream">The stream to which to save data.</param> | 117 | /// <param name="saveStream">The stream to which to save data.</param> |
105 | /// <param name="requestId">The id associated with this request</param> | 118 | /// <param name="requestId">The id associated with this request</param> |
106 | public ArchiveWriteRequestPreparation(ArchiverModule module, Stream saveStream, Guid requestId) : this(module, requestId) | 119 | public ArchiveWriteRequestPreparation(Scene scene, Stream saveStream, Guid requestId) : this(scene, requestId) |
107 | { | 120 | { |
108 | m_saveStream = saveStream; | 121 | m_saveStream = saveStream; |
109 | } | 122 | } |
110 | 123 | ||
111 | protected ArchiveWriteRequestPreparation(ArchiverModule module, Guid requestId) | 124 | protected ArchiveWriteRequestPreparation(Scene scene, Guid requestId) |
112 | { | 125 | { |
113 | m_module = module; | 126 | m_rootScene = scene; |
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; | 127 | m_requestId = requestId; |
128 | m_archiveWriter = null; | ||
121 | 129 | ||
130 | MultiRegionFormat = false; | ||
122 | SaveAssets = true; | 131 | SaveAssets = true; |
132 | CheckPermissions = null; | ||
123 | } | 133 | } |
124 | 134 | ||
125 | /// <summary> | 135 | /// <summary> |
@@ -128,126 +138,157 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
128 | /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception> | 138 | /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception> |
129 | public void ArchiveRegion(Dictionary<string, object> options) | 139 | public void ArchiveRegion(Dictionary<string, object> options) |
130 | { | 140 | { |
141 | m_options = options; | ||
142 | |||
143 | if (options.ContainsKey("all") && (bool)options["all"]) | ||
144 | MultiRegionFormat = true; | ||
145 | |||
131 | if (options.ContainsKey("noassets") && (bool)options["noassets"]) | 146 | if (options.ContainsKey("noassets") && (bool)options["noassets"]) |
132 | SaveAssets = false; | 147 | SaveAssets = false; |
133 | 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 | |||
134 | try | 173 | try |
135 | { | 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 | |||
136 | Dictionary<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>(); | 181 | Dictionary<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>(); |
137 | 182 | ||
138 | EntityBase[] entities = m_scene.GetEntities(); | 183 | scenesGroup.ForEachScene(delegate(Scene scene) |
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 | { | 184 | { |
152 | if (entity is SceneObjectGroup) | 185 | string regionDir = MultiRegionFormat ? scenesGroup.GetRegionDir(scene.RegionInfo.RegionID) : ""; |
153 | { | 186 | ArchiveOneRegion(scene, regionDir, assetUuids); |
154 | SceneObjectGroup sceneObject = (SceneObjectGroup)entity; | 187 | }); |
155 | 188 | ||
156 | if (!sceneObject.IsDeleted && !sceneObject.IsAttachment) | 189 | // Archive the assets |
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 | 190 | ||
171 | if (SaveAssets) | 191 | if (SaveAssets) |
172 | { | 192 | { |
173 | UuidGatherer assetGatherer = new UuidGatherer(m_scene.AssetService); | 193 | m_log.DebugFormat("[ARCHIVER]: Saving {0} assets", assetUuids.Count); |
174 | 194 | ||
175 | foreach (SceneObjectGroup sceneObject in sceneObjects) | 195 | // Asynchronously request all the assets required to perform this archive operation |
176 | { | 196 | AssetsRequest ar |
177 | assetGatherer.GatherAssetUuids(sceneObject, assetUuids); | 197 | = new AssetsRequest( |
178 | } | 198 | new AssetsArchiver(m_archiveWriter), assetUuids, |
199 | m_rootScene.AssetService, m_rootScene.UserAccountService, | ||
200 | m_rootScene.RegionInfo.ScopeID, options, ReceivedAllAssets); | ||
179 | 201 | ||
180 | m_log.DebugFormat( | 202 | Util.FireAndForget(o => ar.Execute()); |
181 | "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets", | 203 | |
182 | sceneObjects.Count, assetUuids.Count); | 204 | // CloseArchive() will be called from ReceivedAllAssets() |
183 | } | 205 | } |
184 | else | 206 | else |
185 | { | 207 | { |
186 | m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified"); | 208 | m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified"); |
209 | CloseArchive(string.Empty); | ||
187 | } | 210 | } |
211 | } | ||
212 | catch (Exception e) | ||
213 | { | ||
214 | CloseArchive(e.Message); | ||
215 | throw; | ||
216 | } | ||
217 | } | ||
188 | 218 | ||
189 | if (numObjectsSkippedPermissions > 0) | ||
190 | { | ||
191 | m_log.DebugFormat( | ||
192 | "[ARCHIVER]: {0} scene objects skipped due to lack of permissions", | ||
193 | numObjectsSkippedPermissions); | ||
194 | } | ||
195 | 219 | ||
196 | // Make sure that we also request terrain texture assets | 220 | private void ArchiveOneRegion(Scene scene, string regionDir, Dictionary<UUID, AssetType> assetUuids) |
197 | RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings; | 221 | { |
198 | 222 | m_log.InfoFormat("[ARCHIVER]: Writing region {0}", scene.RegionInfo.RegionName); | |
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 | 223 | ||
231 | if (SaveAssets) | 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) | ||
232 | { | 236 | { |
233 | AssetsRequest ar | 237 | SceneObjectGroup sceneObject = (SceneObjectGroup)entity; |
234 | = new AssetsRequest( | ||
235 | new AssetsArchiver(archiveWriter), assetUuids, | ||
236 | m_scene.AssetService, m_scene.UserAccountService, | ||
237 | m_scene.RegionInfo.ScopeID, options, awre.ReceivedAllAssets); | ||
238 | 238 | ||
239 | Util.FireAndForget(o => ar.Execute()); | 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 | } | ||
240 | } | 251 | } |
241 | else | 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) | ||
242 | { | 260 | { |
243 | awre.ReceivedAllAssets(new List<UUID>(), new List<UUID>()); | 261 | assetGatherer.GatherAssetUuids(sceneObject, assetUuids); |
244 | } | 262 | } |
263 | |||
264 | m_log.DebugFormat( | ||
265 | "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets", | ||
266 | sceneObjects.Count, assetUuids.Count - prevAssets); | ||
245 | } | 267 | } |
246 | catch (Exception) | 268 | |
269 | if (numObjectsSkippedPermissions > 0) | ||
247 | { | 270 | { |
248 | m_saveStream.Close(); | 271 | m_log.DebugFormat( |
249 | throw; | 272 | "[ARCHIVER]: {0} scene objects skipped due to lack of permissions", |
250 | } | 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); | ||
251 | } | 292 | } |
252 | 293 | ||
253 | /// <summary> | 294 | /// <summary> |
@@ -256,14 +297,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
256 | /// <param name="user">The user</param> | 297 | /// <param name="user">The user</param> |
257 | /// <param name="objGroup">The object group</param> | 298 | /// <param name="objGroup">The object group</param> |
258 | /// <param name="checkPermissions">Which permissions to check: "C" = Copy, "T" = Transfer</param> | 299 | /// <param name="checkPermissions">Which permissions to check: "C" = Copy, "T" = Transfer</param> |
300 | /// <param name="permissionsModule">The scene's permissions module</param> | ||
259 | /// <returns>Whether the user is allowed to export the object to an OAR</returns> | 301 | /// <returns>Whether the user is allowed to export the object to an OAR</returns> |
260 | private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions) | 302 | private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions, IPermissionsModule permissionsModule) |
261 | { | 303 | { |
262 | if (checkPermissions == null) | 304 | if (checkPermissions == null) |
263 | return true; | 305 | return true; |
264 | 306 | ||
265 | IPermissionsModule module = m_scene.RequestModuleInterface<IPermissionsModule>(); | 307 | if (permissionsModule == null) |
266 | if (module == null) | ||
267 | return true; // this shouldn't happen | 308 | return true; // this shouldn't happen |
268 | 309 | ||
269 | // Check whether the user is permitted to export all of the parts in the SOG. If any | 310 | // Check whether the user is permitted to export all of the parts in the SOG. If any |
@@ -275,7 +316,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
275 | foreach (SceneObjectPart obj in objGroup.Parts) | 316 | foreach (SceneObjectPart obj in objGroup.Parts) |
276 | { | 317 | { |
277 | uint perm; | 318 | uint perm; |
278 | PermissionClass permissionClass = module.GetPermissionClass(user, obj); | 319 | PermissionClass permissionClass = permissionsModule.GetPermissionClass(user, obj); |
279 | switch (permissionClass) | 320 | switch (permissionClass) |
280 | { | 321 | { |
281 | case PermissionClass.Owner: | 322 | case PermissionClass.Owner: |
@@ -330,16 +371,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
330 | } | 371 | } |
331 | 372 | ||
332 | /// <summary> | 373 | /// <summary> |
333 | /// Create the control file for the most up to date archive | 374 | /// Create the control file. |
334 | /// </summary> | 375 | /// </summary> |
335 | /// <returns></returns> | 376 | /// <returns></returns> |
336 | public string CreateControlFile(Dictionary<string, object> options) | 377 | public string CreateControlFile(ArchiveScenesGroup scenesGroup) |
337 | { | 378 | { |
338 | int majorVersion = MAX_MAJOR_VERSION, minorVersion = 8; | 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 | } | ||
339 | // | 394 | // |
340 | // if (options.ContainsKey("version")) | 395 | // if (m_options.ContainsKey("version")) |
341 | // { | 396 | // { |
342 | // string[] parts = options["version"].ToString().Split('.'); | 397 | // string[] parts = m_options["version"].ToString().Split('.'); |
343 | // if (parts.Length >= 1) | 398 | // if (parts.Length >= 1) |
344 | // { | 399 | // { |
345 | // majorVersion = Int32.Parse(parts[0]); | 400 | // majorVersion = Int32.Parse(parts[0]); |
@@ -368,10 +423,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
368 | // } | 423 | // } |
369 | 424 | ||
370 | m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion); | 425 | m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion); |
371 | //if (majorVersion == 1) | 426 | if (majorVersion == 1) |
372 | //{ | 427 | { |
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"); | 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"); |
374 | //} | 429 | } |
375 | 430 | ||
376 | String s; | 431 | String s; |
377 | 432 | ||
@@ -389,50 +444,191 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
389 | DateTime now = DateTime.UtcNow; | 444 | DateTime now = DateTime.UtcNow; |
390 | TimeSpan t = now - new DateTime(1970, 1, 1); | 445 | TimeSpan t = now - new DateTime(1970, 1, 1); |
391 | xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString()); | 446 | xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString()); |
392 | xtw.WriteElementString("id", UUID.Random().ToString()); | 447 | if (!MultiRegionFormat) |
448 | xtw.WriteElementString("id", m_rootScene.RegionInfo.RegionID.ToString()); | ||
393 | xtw.WriteEndElement(); | 449 | xtw.WriteEndElement(); |
450 | |||
451 | xtw.WriteElementString("assets_included", SaveAssets.ToString()); | ||
394 | 452 | ||
395 | xtw.WriteStartElement("region_info"); | 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 | } | ||
396 | 463 | ||
397 | bool isMegaregion; | 464 | xtw.WriteEndElement(); |
398 | Vector2 size; | 465 | |
399 | IRegionCombinerModule rcMod = null; | 466 | xtw.Flush(); |
467 | } | ||
400 | 468 | ||
401 | // FIXME: This is only here for regression test purposes since they do not supply a module. Need to fix | 469 | s = sw.ToString(); |
402 | // this, possibly by doing control file creation somewhere else. | 470 | } |
403 | if (m_module != null) | ||
404 | rcMod = m_module.RegionCombinerModule; | ||
405 | 471 | ||
406 | if (rcMod != null) | 472 | return s; |
407 | isMegaregion = rcMod.IsRootForMegaregion(m_scene.RegionInfo.RegionID); | 473 | } |
408 | else | ||
409 | isMegaregion = false; | ||
410 | 474 | ||
411 | if (isMegaregion) | 475 | /// <summary> |
412 | size = rcMod.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID); | 476 | /// Writes the list of regions included in a multi-region OAR. |
413 | else | 477 | /// </summary> |
414 | size = new Vector2((float)Constants.RegionSize, (float)Constants.RegionSize); | 478 | private static void WriteRegionsManifest(ArchiveScenesGroup scenesGroup, XmlTextWriter xtw) |
479 | { | ||
480 | xtw.WriteStartElement("regions"); | ||
415 | 481 | ||
416 | xtw.WriteElementString("is_megaregion", isMegaregion.ToString()); | 482 | // Write the regions in order: rows from South to North, then regions from West to East. |
417 | xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y)); | 483 | // The list of regions can have "holes"; we write empty elements in their position. |
418 | 484 | ||
419 | xtw.WriteEndElement(); | 485 | for (uint y = (uint)scenesGroup.Rect.Top; y < scenesGroup.Rect.Bottom; ++y) |
420 | 486 | { | |
421 | xtw.WriteElementString("assets_included", SaveAssets.ToString()); | 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 | } | ||
422 | 509 | ||
423 | xtw.WriteEndElement(); | 510 | xtw.WriteEndElement(); |
424 | |||
425 | xtw.Flush(); | ||
426 | } | 511 | } |
512 | else | ||
513 | { | ||
514 | // Write a placeholder for a missing row | ||
515 | xtw.WriteElementString("row", ""); | ||
516 | } | ||
517 | } | ||
427 | 518 | ||
428 | s = sw.ToString(); | 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)); | ||
429 | } | 566 | } |
430 | 567 | ||
431 | // if (m_scene != null) | 568 | m_log.InfoFormat("[ARCHIVER]: Adding terrain information to archive."); |
432 | // Console.WriteLine( | ||
433 | // "[ARCHIVE WRITE REQUEST PREPARATION]: Control file for {0} is: {1}", m_scene.RegionInfo.RegionName, s); | ||
434 | 569 | ||
435 | return s; | 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 | } | ||
436 | } | 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 | |||
437 | } | 633 | } |
438 | } | 634 | } |