diff options
author | Justin Clark-Casey (justincc) | 2012-09-07 23:30:54 +0100 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2012-09-14 20:25:44 +0100 |
commit | d7e6fe488d5258ec92230b6c652969f2d0420376 (patch) | |
tree | 2541ddb0752c20f372baffa2a9c6b72fee954a81 /OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs | |
parent | Support multi-region OAR files (diff) | |
download | opensim-SC_OLD-d7e6fe488d5258ec92230b6c652969f2d0420376.zip opensim-SC_OLD-d7e6fe488d5258ec92230b6c652969f2d0420376.tar.gz opensim-SC_OLD-d7e6fe488d5258ec92230b6c652969f2d0420376.tar.bz2 opensim-SC_OLD-d7e6fe488d5258ec92230b6c652969f2d0420376.tar.xz |
Rename ArchiveWriteRequestPreparatio nto ArchiveWriteRequest since after the multi-OAR patch there is now only one class that handles this operation.
Adapation of 0004-Renamed-ArchiveWriteRequestPreparation-to-ArchiveWri.patch in http://opensimulator.org/mantis/view.php?id=6105
since that did not directly apply
Diffstat (limited to 'OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs | 634 |
1 files changed, 634 insertions, 0 deletions
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.IO.Compression; | ||
32 | using System.Reflection; | ||
33 | using System.Text.RegularExpressions; | ||
34 | using System.Threading; | ||
35 | using System.Xml; | ||
36 | using log4net; | ||
37 | using OpenMetaverse; | ||
38 | using OpenSim.Framework; | ||
39 | using OpenSim.Framework.Serialization; | ||
40 | using OpenSim.Region.CoreModules.World.Terrain; | ||
41 | using OpenSim.Region.Framework.Interfaces; | ||
42 | using OpenSim.Region.Framework.Scenes; | ||
43 | using Ionic.Zlib; | ||
44 | using GZipStream = Ionic.Zlib.GZipStream; | ||
45 | using CompressionMode = Ionic.Zlib.CompressionMode; | ||
46 | using OpenSim.Framework.Serialization.External; | ||
47 | |||
48 | namespace 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 | } | ||