diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs new file mode 100644 index 0000000..ee0ec69 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs | |||
@@ -0,0 +1,333 @@ | |||
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 OpenSim 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 OpenSim.Framework; | ||
29 | using OpenSim.Framework.Communications.Cache; | ||
30 | using OpenSim.Region.Framework.Interfaces; | ||
31 | using OpenSim.Region.Framework.Scenes; | ||
32 | using OpenSim.Region.CoreModules.World.Serialiser; | ||
33 | using OpenSim.Region.CoreModules.World.Terrain; | ||
34 | using System; | ||
35 | using System.Collections.Generic; | ||
36 | using System.IO; | ||
37 | using System.IO.Compression; | ||
38 | using System.Reflection; | ||
39 | using System.Text.RegularExpressions; | ||
40 | using System.Threading; | ||
41 | using OpenMetaverse; | ||
42 | using log4net; | ||
43 | using Nini.Config; | ||
44 | |||
45 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
46 | { | ||
47 | /// <summary> | ||
48 | /// Prepare to write out an archive. | ||
49 | /// </summary> | ||
50 | public class ArchiveWriteRequestPreparation | ||
51 | { | ||
52 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
53 | |||
54 | protected Scene m_scene; | ||
55 | protected Stream m_saveStream; | ||
56 | |||
57 | /// <summary> | ||
58 | /// Used as a temporary store of an asset which represents an object. This can be a null if no appropriate | ||
59 | /// asset was found by the asset service. | ||
60 | /// </summary> | ||
61 | protected AssetBase m_requestedObjectAsset; | ||
62 | |||
63 | /// <summary> | ||
64 | /// Signal whether we are currently waiting for the asset service to deliver an asset. | ||
65 | /// </summary> | ||
66 | protected bool m_waitingForObjectAsset; | ||
67 | |||
68 | /// <summary> | ||
69 | /// Constructor | ||
70 | /// </summary> | ||
71 | public ArchiveWriteRequestPreparation(Scene scene, string savePath) | ||
72 | { | ||
73 | m_scene = scene; | ||
74 | m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress); | ||
75 | } | ||
76 | |||
77 | /// <summary> | ||
78 | /// Constructor. | ||
79 | /// </summary> | ||
80 | /// <param name="scene"></param> | ||
81 | /// <param name="saveStream">The stream to which to save data.</param> | ||
82 | public ArchiveWriteRequestPreparation(Scene scene, Stream saveStream) | ||
83 | { | ||
84 | m_scene = scene; | ||
85 | m_saveStream = saveStream; | ||
86 | } | ||
87 | |||
88 | /// <summary> | ||
89 | /// The callback made when we request the asset for an object from the asset service. | ||
90 | /// </summary> | ||
91 | public void AssetRequestCallback(UUID assetID, AssetBase asset) | ||
92 | { | ||
93 | lock (this) | ||
94 | { | ||
95 | m_requestedObjectAsset = asset; | ||
96 | m_waitingForObjectAsset = false; | ||
97 | Monitor.Pulse(this); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /// <summary> | ||
102 | /// Get an asset synchronously, potentially using an asynchronous callback. If the | ||
103 | /// asynchronous callback is used, we will wait for it to complete. | ||
104 | /// </summary> | ||
105 | /// <param name="uuid"></param> | ||
106 | /// <returns></returns> | ||
107 | protected AssetBase GetAsset(UUID uuid) | ||
108 | { | ||
109 | m_waitingForObjectAsset = true; | ||
110 | m_scene.AssetCache.GetAsset(uuid, AssetRequestCallback, true); | ||
111 | |||
112 | // The asset cache callback can either | ||
113 | // | ||
114 | // 1. Complete on the same thread (if the asset is already in the cache) or | ||
115 | // 2. Come in via a different thread (if we need to go fetch it). | ||
116 | // | ||
117 | // The code below handles both these alternatives. | ||
118 | lock (this) | ||
119 | { | ||
120 | if (m_waitingForObjectAsset) | ||
121 | { | ||
122 | Monitor.Wait(this); | ||
123 | m_waitingForObjectAsset = false; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | return m_requestedObjectAsset; | ||
128 | } | ||
129 | |||
130 | /// <summary> | ||
131 | /// Record the asset uuids embedded within the given script. | ||
132 | /// </summary> | ||
133 | /// <param name="scriptUuid"></param> | ||
134 | /// <param name="assetUuids">Dictionary in which to record the references</param> | ||
135 | protected void GetScriptAssetUuids(UUID scriptUuid, IDictionary<UUID, int> assetUuids) | ||
136 | { | ||
137 | AssetBase scriptAsset = GetAsset(scriptUuid); | ||
138 | |||
139 | if (null != scriptAsset) | ||
140 | { | ||
141 | string script = Utils.BytesToString(scriptAsset.Data); | ||
142 | //m_log.DebugFormat("[ARCHIVER]: Script {0}", script); | ||
143 | MatchCollection uuidMatches = Util.UUIDPattern.Matches(script); | ||
144 | //m_log.DebugFormat("[ARCHIVER]: Found {0} matches in script", uuidMatches.Count); | ||
145 | |||
146 | foreach (Match uuidMatch in uuidMatches) | ||
147 | { | ||
148 | UUID uuid = new UUID(uuidMatch.Value); | ||
149 | //m_log.DebugFormat("[ARCHIVER]: Recording {0} in script", uuid); | ||
150 | assetUuids[uuid] = 1; | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | /// <summary> | ||
156 | /// Record the uuids referenced by the given wearable asset | ||
157 | /// </summary> | ||
158 | /// <param name="wearableAssetUuid"></param> | ||
159 | /// <param name="assetUuids">Dictionary in which to record the references</param> | ||
160 | protected void GetWearableAssetUuids(UUID wearableAssetUuid, IDictionary<UUID, int> assetUuids) | ||
161 | { | ||
162 | AssetBase assetBase = GetAsset(wearableAssetUuid); | ||
163 | //m_log.Debug(new System.Text.ASCIIEncoding().GetString(bodypartAsset.Data)); | ||
164 | AssetWearable wearableAsset = new AssetBodypart(wearableAssetUuid, assetBase.Data); | ||
165 | wearableAsset.Decode(); | ||
166 | |||
167 | //m_log.DebugFormat( | ||
168 | // "[ARCHIVER]: Wearable asset {0} references {1} assets", wearableAssetUuid, wearableAsset.Textures.Count); | ||
169 | |||
170 | foreach (UUID uuid in wearableAsset.Textures.Values) | ||
171 | { | ||
172 | //m_log.DebugFormat("[ARCHIVER]: Got bodypart uuid {0}", uuid); | ||
173 | assetUuids[uuid] = 1; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | /// <summary> | ||
178 | /// Get all the asset uuids associated with a given object. This includes both those directly associated with | ||
179 | /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained | ||
180 | /// within this object). | ||
181 | /// </summary> | ||
182 | /// <param name="sceneObject"></param> | ||
183 | /// <param name="assetUuids"></param> | ||
184 | protected void GetSceneObjectAssetUuids(UUID sceneObjectUuid, IDictionary<UUID, int> assetUuids) | ||
185 | { | ||
186 | AssetBase objectAsset = GetAsset(sceneObjectUuid); | ||
187 | |||
188 | if (null != objectAsset) | ||
189 | { | ||
190 | string xml = Utils.BytesToString(objectAsset.Data); | ||
191 | SceneObjectGroup sog = new SceneObjectGroup(xml, true); | ||
192 | GetSceneObjectAssetUuids(sog, assetUuids); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | /// <summary> | ||
197 | /// Get all the asset uuids associated with a given object. This includes both those directly associated with | ||
198 | /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained | ||
199 | /// within this object). | ||
200 | /// </summary> | ||
201 | /// <param name="sceneObject"></param> | ||
202 | /// <param name="assetUuids"></param> | ||
203 | protected void GetSceneObjectAssetUuids(SceneObjectGroup sceneObject, IDictionary<UUID, int> assetUuids) | ||
204 | { | ||
205 | m_log.DebugFormat( | ||
206 | "[ARCHIVER]: Getting assets for object {0}, {1}", sceneObject.Name, sceneObject.UUID); | ||
207 | |||
208 | foreach (SceneObjectPart part in sceneObject.GetParts()) | ||
209 | { | ||
210 | //m_log.DebugFormat( | ||
211 | // "[ARCHIVER]: Getting part {0}, {1} for object {2}", part.Name, part.UUID, sceneObject.UUID); | ||
212 | |||
213 | try | ||
214 | { | ||
215 | Primitive.TextureEntry textureEntry = part.Shape.Textures; | ||
216 | |||
217 | // Get the prim's default texture. This will be used for faces which don't have their own texture | ||
218 | assetUuids[textureEntry.DefaultTexture.TextureID] = 1; | ||
219 | |||
220 | // XXX: Not a great way to iterate through face textures, but there's no | ||
221 | // other method available to tell how many faces there actually are | ||
222 | //int i = 0; | ||
223 | foreach (Primitive.TextureEntryFace texture in textureEntry.FaceTextures) | ||
224 | { | ||
225 | if (texture != null) | ||
226 | { | ||
227 | //m_log.DebugFormat("[ARCHIVER]: Got face {0}", i++); | ||
228 | assetUuids[texture.TextureID] = 1; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | // If the prim is a sculpt then preserve this information too | ||
233 | if (part.Shape.SculptTexture != UUID.Zero) | ||
234 | assetUuids[part.Shape.SculptTexture] = 1; | ||
235 | |||
236 | // Now analyze this prim's inventory items to preserve all the uuids that they reference | ||
237 | foreach (TaskInventoryItem tii in part.TaskInventory.Values) | ||
238 | { | ||
239 | //m_log.DebugFormat("[ARCHIVER]: Analysing item asset type {0}", tii.Type); | ||
240 | |||
241 | if (!assetUuids.ContainsKey(tii.AssetID)) | ||
242 | { | ||
243 | assetUuids[tii.AssetID] = 1; | ||
244 | |||
245 | if ((int)AssetType.Bodypart == tii.Type || ((int)AssetType.Clothing == tii.Type)) | ||
246 | { | ||
247 | GetWearableAssetUuids(tii.AssetID, assetUuids); | ||
248 | } | ||
249 | else if ((int)AssetType.LSLText == tii.Type) | ||
250 | { | ||
251 | GetScriptAssetUuids(tii.AssetID, assetUuids); | ||
252 | } | ||
253 | else if ((int)AssetType.Object == tii.Type) | ||
254 | { | ||
255 | GetSceneObjectAssetUuids(tii.AssetID, assetUuids); | ||
256 | } | ||
257 | //else | ||
258 | //{ | ||
259 | //m_log.DebugFormat("[ARCHIVER]: Recording asset {0} in object {1}", tii.AssetID, part.UUID); | ||
260 | //} | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | catch (Exception e) | ||
265 | { | ||
266 | m_log.ErrorFormat("[ARCHIVER]: Failed to get part - {0}", e); | ||
267 | m_log.DebugFormat("[ARCHIVER]: Texture entry length for prim was {0} (min is 46)", part.Shape.TextureEntry.Length); | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | |||
272 | /// <summary> | ||
273 | /// Archive the region requested. | ||
274 | /// </summary> | ||
275 | /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception> | ||
276 | public void ArchiveRegion() | ||
277 | { | ||
278 | Dictionary<UUID, int> assetUuids = new Dictionary<UUID, int>(); | ||
279 | |||
280 | List<EntityBase> entities = m_scene.GetEntities(); | ||
281 | List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>(); | ||
282 | |||
283 | // Filter entities so that we only have scene objects. | ||
284 | // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods | ||
285 | // end up having to do this | ||
286 | foreach (EntityBase entity in entities) | ||
287 | { | ||
288 | if (entity is SceneObjectGroup) | ||
289 | { | ||
290 | SceneObjectGroup sceneObject = (SceneObjectGroup)entity; | ||
291 | |||
292 | if (!sceneObject.IsDeleted && !sceneObject.IsAttachment) | ||
293 | sceneObjects.Add((SceneObjectGroup)entity); | ||
294 | } | ||
295 | } | ||
296 | |||
297 | foreach (SceneObjectGroup sceneObject in sceneObjects) | ||
298 | { | ||
299 | GetSceneObjectAssetUuids(sceneObject, assetUuids); | ||
300 | } | ||
301 | |||
302 | m_log.DebugFormat( | ||
303 | "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets", | ||
304 | sceneObjects.Count, assetUuids.Count); | ||
305 | |||
306 | // Make sure that we also request terrain texture assets | ||
307 | RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings; | ||
308 | |||
309 | if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1) | ||
310 | assetUuids[regionSettings.TerrainTexture1] = 1; | ||
311 | |||
312 | if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2) | ||
313 | assetUuids[regionSettings.TerrainTexture2] = 1; | ||
314 | |||
315 | if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3) | ||
316 | assetUuids[regionSettings.TerrainTexture3] = 1; | ||
317 | |||
318 | if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4) | ||
319 | assetUuids[regionSettings.TerrainTexture4] = 1; | ||
320 | |||
321 | // Asynchronously request all the assets required to perform this archive operation | ||
322 | ArchiveWriteRequestExecution awre | ||
323 | = new ArchiveWriteRequestExecution( | ||
324 | sceneObjects, | ||
325 | m_scene.RequestModuleInterface<ITerrainModule>(), | ||
326 | m_scene.RequestModuleInterface<IRegionSerialiserModule>(), | ||
327 | m_scene, | ||
328 | m_saveStream); | ||
329 | |||
330 | new AssetsRequest(assetUuids.Keys, m_scene.AssetCache, awre.ReceivedAllAssets).Execute(); | ||
331 | } | ||
332 | } | ||
333 | } | ||