diff options
Diffstat (limited to '')
9 files changed, 317 insertions, 69 deletions
diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 14f670d..86975c9 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs | |||
@@ -267,12 +267,13 @@ namespace OpenSim | |||
267 | 267 | ||
268 | m_console.Commands.AddCommand("region", false, "save oar", | 268 | m_console.Commands.AddCommand("region", false, "save oar", |
269 | //"save oar [-v|--version=<N>] [-p|--profile=<url>] [<OAR path>]", | 269 | //"save oar [-v|--version=<N>] [-p|--profile=<url>] [<OAR path>]", |
270 | "save oar [-p|--profile=<url>] [<OAR path>]", | 270 | "save oar [-p|--profile=<url>] [--noassets] [<OAR path>]", |
271 | "Save a region's data to an OAR archive.", | 271 | "Save a region's data to an OAR archive.", |
272 | // "-v|--version=<N> generates scene objects as per older versions of the serialization (e.g. -v=0)" + Environment.NewLine | 272 | // "-v|--version=<N> generates scene objects as per older versions of the serialization (e.g. -v=0)" + Environment.NewLine |
273 | "-p|--profile=<url> adds the url of the profile service to the saved user information" + Environment.NewLine | 273 | "-p|--profile=<url> adds the url of the profile service to the saved user information." + Environment.NewLine |
274 | + "The OAR path must be a filesystem path." | 274 | + " The OAR path must be a filesystem path." |
275 | + " If this is not given then the oar is saved to region.oar in the current directory.", | 275 | + " If this is not given then the oar is saved to region.oar in the current directory." + Environment.NewLine |
276 | + "--noassets stops assets being saved to the OAR.", | ||
276 | SaveOar); | 277 | SaveOar); |
277 | 278 | ||
278 | m_console.Commands.AddCommand("region", false, "edit scale", | 279 | m_console.Commands.AddCommand("region", false, "edit scale", |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs index dd16bfe..e043caa 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs | |||
@@ -46,6 +46,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.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 | /// Determine whether this archive will save assets. Default is true. | ||
51 | /// </summary> | ||
52 | public bool SaveAssets { get; set; } | ||
53 | |||
49 | /// <value> | 54 | /// <value> |
50 | /// Used to select all inventory nodes in a folder but not the folder itself | 55 | /// Used to select all inventory nodes in a folder but not the folder itself |
51 | /// </value> | 56 | /// </value> |
@@ -112,6 +117,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
112 | m_invPath = invPath; | 117 | m_invPath = invPath; |
113 | m_saveStream = saveStream; | 118 | m_saveStream = saveStream; |
114 | m_assetGatherer = new UuidGatherer(m_scene.AssetService); | 119 | m_assetGatherer = new UuidGatherer(m_scene.AssetService); |
120 | |||
121 | SaveAssets = true; | ||
115 | } | 122 | } |
116 | 123 | ||
117 | protected void ReceivedAllAssets(ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids) | 124 | protected void ReceivedAllAssets(ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids) |
@@ -147,7 +154,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
147 | string serialization = UserInventoryItemSerializer.Serialize(inventoryItem, options, userAccountService); | 154 | string serialization = UserInventoryItemSerializer.Serialize(inventoryItem, options, userAccountService); |
148 | m_archiveWriter.WriteFile(filename, serialization); | 155 | m_archiveWriter.WriteFile(filename, serialization); |
149 | 156 | ||
150 | m_assetGatherer.GatherAssetUuids(inventoryItem.AssetID, (AssetType)inventoryItem.AssetType, m_assetUuids); | 157 | if (SaveAssets) |
158 | m_assetGatherer.GatherAssetUuids(inventoryItem.AssetID, (AssetType)inventoryItem.AssetType, m_assetUuids); | ||
151 | } | 159 | } |
152 | 160 | ||
153 | /// <summary> | 161 | /// <summary> |
@@ -189,6 +197,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
189 | /// </summary> | 197 | /// </summary> |
190 | public void Execute(Dictionary<string, object> options, IUserAccountService userAccountService) | 198 | public void Execute(Dictionary<string, object> options, IUserAccountService userAccountService) |
191 | { | 199 | { |
200 | if (options.ContainsKey("noassets") && (bool)options["noassets"]) | ||
201 | SaveAssets = false; | ||
202 | |||
192 | try | 203 | try |
193 | { | 204 | { |
194 | InventoryFolderBase inventoryFolder = null; | 205 | InventoryFolderBase inventoryFolder = null; |
@@ -279,12 +290,23 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
279 | 290 | ||
280 | // Don't put all this profile information into the archive right now. | 291 | // Don't put all this profile information into the archive right now. |
281 | //SaveUsers(); | 292 | //SaveUsers(); |
282 | 293 | ||
283 | new AssetsRequest( | 294 | if (SaveAssets) |
284 | new AssetsArchiver(m_archiveWriter), | 295 | { |
285 | m_assetUuids, m_scene.AssetService, | 296 | m_log.DebugFormat("[INVENTORY ARCHIVER]: Saving {0} assets for items", m_assetUuids.Count); |
286 | m_scene.UserAccountService, m_scene.RegionInfo.ScopeID, | 297 | |
287 | options, ReceivedAllAssets).Execute(); | 298 | new AssetsRequest( |
299 | new AssetsArchiver(m_archiveWriter), | ||
300 | m_assetUuids, m_scene.AssetService, | ||
301 | m_scene.UserAccountService, m_scene.RegionInfo.ScopeID, | ||
302 | options, ReceivedAllAssets).Execute(); | ||
303 | } | ||
304 | else | ||
305 | { | ||
306 | m_log.DebugFormat("[INVENTORY ARCHIVER]: Not saving assets since --noassets was specified"); | ||
307 | |||
308 | ReceivedAllAssets(new List<UUID>(), new List<UUID>()); | ||
309 | } | ||
288 | } | 310 | } |
289 | catch (Exception) | 311 | catch (Exception) |
290 | { | 312 | { |
@@ -381,19 +403,19 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
381 | /// </summary> | 403 | /// </summary> |
382 | /// <param name="options"></param> | 404 | /// <param name="options"></param> |
383 | /// <returns></returns> | 405 | /// <returns></returns> |
384 | public static string CreateControlFile(Dictionary<string, object> options) | 406 | public string CreateControlFile(Dictionary<string, object> options) |
385 | { | 407 | { |
386 | int majorVersion, minorVersion; | 408 | int majorVersion, minorVersion; |
387 | 409 | ||
388 | if (options.ContainsKey("profile")) | 410 | if (options.ContainsKey("profile")) |
389 | { | 411 | { |
390 | majorVersion = 1; | 412 | majorVersion = 1; |
391 | minorVersion = 1; | 413 | minorVersion = 2; |
392 | } | 414 | } |
393 | else | 415 | else |
394 | { | 416 | { |
395 | majorVersion = 0; | 417 | majorVersion = 0; |
396 | minorVersion = 2; | 418 | minorVersion = 3; |
397 | } | 419 | } |
398 | 420 | ||
399 | m_log.InfoFormat("[INVENTORY ARCHIVER]: Creating version {0}.{1} IAR", majorVersion, minorVersion); | 421 | m_log.InfoFormat("[INVENTORY ARCHIVER]: Creating version {0}.{1} IAR", majorVersion, minorVersion); |
@@ -405,6 +427,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
405 | xtw.WriteStartElement("archive"); | 427 | xtw.WriteStartElement("archive"); |
406 | xtw.WriteAttributeString("major_version", majorVersion.ToString()); | 428 | xtw.WriteAttributeString("major_version", majorVersion.ToString()); |
407 | xtw.WriteAttributeString("minor_version", minorVersion.ToString()); | 429 | xtw.WriteAttributeString("minor_version", minorVersion.ToString()); |
430 | |||
431 | xtw.WriteElementString("assets_included", SaveAssets.ToString()); | ||
432 | |||
408 | xtw.WriteEndElement(); | 433 | xtw.WriteEndElement(); |
409 | 434 | ||
410 | xtw.Flush(); | 435 | xtw.Flush(); |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs index 613f0ed..e0b02aa 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs | |||
@@ -122,7 +122,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
122 | 122 | ||
123 | scene.AddCommand( | 123 | scene.AddCommand( |
124 | this, "save iar", | 124 | this, "save iar", |
125 | "save iar [--p|-profile=<url>] <first> <last> <inventory path> <password> [<IAR path>]", | 125 | "save iar [--p|-profile=<url>] [--noassets] <first> <last> <inventory path> <password> [<IAR path>] [--v|-verbose]", |
126 | "Save user inventory archive (IAR).", | 126 | "Save user inventory archive (IAR).", |
127 | "<first> is the user's first name." + Environment.NewLine | 127 | "<first> is the user's first name." + Environment.NewLine |
128 | + "<last> is the user's last name." + Environment.NewLine | 128 | + "<last> is the user's last name." + Environment.NewLine |
@@ -130,6 +130,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
130 | + "-p|--profile=<url> adds the url of the profile service to the saved user information." + Environment.NewLine | 130 | + "-p|--profile=<url> adds the url of the profile service to the saved user information." + Environment.NewLine |
131 | + "-c|--creators preserves information about foreign creators." + Environment.NewLine | 131 | + "-c|--creators preserves information about foreign creators." + Environment.NewLine |
132 | + "-v|--verbose extra debug messages." + Environment.NewLine | 132 | + "-v|--verbose extra debug messages." + Environment.NewLine |
133 | + "--noassets stops assets being saved to the IAR." | ||
133 | + "<IAR path> is the filesystem path at which to save the IAR." | 134 | + "<IAR path> is the filesystem path at which to save the IAR." |
134 | + string.Format(" If this is not given then the filename {0} in the current directory is used", DEFAULT_INV_BACKUP_FILENAME), | 135 | + string.Format(" If this is not given then the filename {0} in the current directory is used", DEFAULT_INV_BACKUP_FILENAME), |
135 | HandleSaveInvConsoleCommand); | 136 | HandleSaveInvConsoleCommand); |
@@ -398,6 +399,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
398 | ops.Add("p|profile=", delegate(string v) { options["profile"] = v; }); | 399 | ops.Add("p|profile=", delegate(string v) { options["profile"] = v; }); |
399 | ops.Add("v|verbose", delegate(string v) { options["verbose"] = v; }); | 400 | ops.Add("v|verbose", delegate(string v) { options["verbose"] = v; }); |
400 | ops.Add("c|creators", delegate(string v) { options["creators"] = v; }); | 401 | ops.Add("c|creators", delegate(string v) { options["creators"] = v; }); |
402 | ops.Add("noassets", delegate(string v) { options["noassets"] = v != null; }); | ||
401 | 403 | ||
402 | List<string> mainParams = ops.Parse(cmdparams); | 404 | List<string> mainParams = ops.Parse(cmdparams); |
403 | 405 | ||
@@ -406,7 +408,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
406 | if (mainParams.Count < 6) | 408 | if (mainParams.Count < 6) |
407 | { | 409 | { |
408 | m_log.Error( | 410 | m_log.Error( |
409 | "[INVENTORY ARCHIVER]: usage is save iar [--p|-profile=<url>] <first name> <last name> <inventory path> <user password> [<save file path>]"); | 411 | "[INVENTORY ARCHIVER]: usage is save iar [--p|-profile=<url>] [--noassets] <first name> <last name> <inventory path> <user password> [<save file path>]"); |
410 | return; | 412 | return; |
411 | } | 413 | } |
412 | 414 | ||
@@ -423,16 +425,16 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
423 | m_log.InfoFormat( | 425 | m_log.InfoFormat( |
424 | "[INVENTORY ARCHIVER]: Saving archive {0} using inventory path {1} for {2} {3}", | 426 | "[INVENTORY ARCHIVER]: Saving archive {0} using inventory path {1} for {2} {3}", |
425 | savePath, invPath, firstName, lastName); | 427 | savePath, invPath, firstName, lastName); |
426 | 428 | ||
429 | lock (m_pendingConsoleSaves) | ||
430 | m_pendingConsoleSaves.Add(id); | ||
431 | |||
427 | ArchiveInventory(id, firstName, lastName, invPath, pass, savePath, options); | 432 | ArchiveInventory(id, firstName, lastName, invPath, pass, savePath, options); |
428 | } | 433 | } |
429 | catch (InventoryArchiverException e) | 434 | catch (InventoryArchiverException e) |
430 | { | 435 | { |
431 | m_log.ErrorFormat("[INVENTORY ARCHIVER]: {0}", e.Message); | 436 | m_log.ErrorFormat("[INVENTORY ARCHIVER]: {0}", e.Message); |
432 | } | 437 | } |
433 | |||
434 | lock (m_pendingConsoleSaves) | ||
435 | m_pendingConsoleSaves.Add(id); | ||
436 | } | 438 | } |
437 | 439 | ||
438 | private void SaveInvConsoleCommandCompleted( | 440 | private void SaveInvConsoleCommandCompleted( |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs index d97311a..ae3ab21 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs | |||
@@ -123,11 +123,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
123 | } | 123 | } |
124 | 124 | ||
125 | /// <summary> | 125 | /// <summary> |
126 | /// Test saving a single inventory item to a V0.1 OpenSim Inventory Archive | 126 | /// Test saving a single inventory item to an IAR |
127 | /// (subject to change since there is no fixed format yet). | 127 | /// (subject to change since there is no fixed format yet). |
128 | /// </summary> | 128 | /// </summary> |
129 | [Test] | 129 | [Test] |
130 | public void TestSaveItemToIarV0_1() | 130 | public void TestSaveItemToIar() |
131 | { | 131 | { |
132 | TestHelper.InMethod(); | 132 | TestHelper.InMethod(); |
133 | // log4net.Config.XmlConfigurator.Configure(); | 133 | // log4net.Config.XmlConfigurator.Configure(); |
@@ -217,6 +217,106 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
217 | 217 | ||
218 | // TODO: Test presence of more files and contents of files. | 218 | // TODO: Test presence of more files and contents of files. |
219 | } | 219 | } |
220 | |||
221 | /// <summary> | ||
222 | /// Test saving a single inventory item to an IAR without its asset | ||
223 | /// </summary> | ||
224 | [Test] | ||
225 | public void TestSaveItemToIarNoAssets() | ||
226 | { | ||
227 | TestHelper.InMethod(); | ||
228 | // log4net.Config.XmlConfigurator.Configure(); | ||
229 | |||
230 | // Create user | ||
231 | string userFirstName = "Jock"; | ||
232 | string userLastName = "Stirrup"; | ||
233 | string userPassword = "troll"; | ||
234 | UUID userId = UUID.Parse("00000000-0000-0000-0000-000000000020"); | ||
235 | UserAccountHelpers.CreateUserWithInventory(m_scene, userFirstName, userLastName, userId, userPassword); | ||
236 | |||
237 | // Create asset | ||
238 | UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); | ||
239 | SceneObjectGroup object1 = SceneSetupHelpers.CreateSceneObject(1, ownerId, "My Little Dog Object", 0x50); | ||
240 | |||
241 | UUID asset1Id = UUID.Parse("00000000-0000-0000-0000-000000000060"); | ||
242 | AssetBase asset1 = AssetHelpers.CreateAsset(asset1Id, object1); | ||
243 | m_scene.AssetService.Store(asset1); | ||
244 | |||
245 | // Create item | ||
246 | UUID item1Id = UUID.Parse("00000000-0000-0000-0000-000000000080"); | ||
247 | string item1Name = "My Little Dog"; | ||
248 | InventoryItemBase item1 = new InventoryItemBase(); | ||
249 | item1.Name = item1Name; | ||
250 | item1.AssetID = asset1.FullID; | ||
251 | item1.ID = item1Id; | ||
252 | InventoryFolderBase objsFolder | ||
253 | = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, userId, "Objects")[0]; | ||
254 | item1.Folder = objsFolder.ID; | ||
255 | m_scene.AddInventoryItem(item1); | ||
256 | |||
257 | MemoryStream archiveWriteStream = new MemoryStream(); | ||
258 | |||
259 | Dictionary<string, Object> options = new Dictionary<string, Object>(); | ||
260 | options.Add("noassets", true); | ||
261 | |||
262 | // When we're not saving assets, archiving is being done synchronously. | ||
263 | m_archiverModule.ArchiveInventory( | ||
264 | Guid.NewGuid(), userFirstName, userLastName, "Objects/" + item1Name, userPassword, archiveWriteStream, options); | ||
265 | |||
266 | byte[] archive = archiveWriteStream.ToArray(); | ||
267 | MemoryStream archiveReadStream = new MemoryStream(archive); | ||
268 | TarArchiveReader tar = new TarArchiveReader(archiveReadStream); | ||
269 | |||
270 | //bool gotControlFile = false; | ||
271 | bool gotObject1File = false; | ||
272 | //bool gotObject2File = false; | ||
273 | string expectedObject1FileName = InventoryArchiveWriteRequest.CreateArchiveItemName(item1); | ||
274 | string expectedObject1FilePath = string.Format( | ||
275 | "{0}{1}", | ||
276 | ArchiveConstants.INVENTORY_PATH, | ||
277 | expectedObject1FileName); | ||
278 | |||
279 | string filePath; | ||
280 | TarArchiveReader.TarEntryType tarEntryType; | ||
281 | |||
282 | // Console.WriteLine("Reading archive"); | ||
283 | |||
284 | while (tar.ReadEntry(out filePath, out tarEntryType) != null) | ||
285 | { | ||
286 | Console.WriteLine("Got {0}", filePath); | ||
287 | |||
288 | // if (ArchiveConstants.CONTROL_FILE_PATH == filePath) | ||
289 | // { | ||
290 | // gotControlFile = true; | ||
291 | // } | ||
292 | |||
293 | if (filePath.StartsWith(ArchiveConstants.INVENTORY_PATH) && filePath.EndsWith(".xml")) | ||
294 | { | ||
295 | // string fileName = filePath.Remove(0, "Objects/".Length); | ||
296 | // | ||
297 | // if (fileName.StartsWith(part1.Name)) | ||
298 | // { | ||
299 | Assert.That(expectedObject1FilePath, Is.EqualTo(filePath)); | ||
300 | gotObject1File = true; | ||
301 | // } | ||
302 | // else if (fileName.StartsWith(part2.Name)) | ||
303 | // { | ||
304 | // Assert.That(fileName, Is.EqualTo(expectedObject2FileName)); | ||
305 | // gotObject2File = true; | ||
306 | // } | ||
307 | } | ||
308 | else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH)) | ||
309 | { | ||
310 | Assert.Fail("Found asset path in TestSaveItemToIarNoAssets()"); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | // Assert.That(gotControlFile, Is.True, "No control file in archive"); | ||
315 | Assert.That(gotObject1File, Is.True, "No item1 file in archive"); | ||
316 | // Assert.That(gotObject2File, Is.True, "No object2 file in archive"); | ||
317 | |||
318 | // TODO: Test presence of more files and contents of files. | ||
319 | } | ||
220 | 320 | ||
221 | /// <summary> | 321 | /// <summary> |
222 | /// Test case where a creator account exists for the creator UUID embedded in item metadata and serialized | 322 | /// Test case where a creator account exists for the creator UUID embedded in item metadata and serialized |
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs index 597b780..10a83ee 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs | |||
@@ -49,7 +49,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
49 | public class ArchiveWriteRequestPreparation | 49 | public class ArchiveWriteRequestPreparation |
50 | { | 50 | { |
51 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 51 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
52 | 52 | ||
53 | /// <summary> | 53 | /// <summary> |
54 | /// The minimum major version of OAR that we can write. | 54 | /// The minimum major version of OAR that we can write. |
55 | /// </summary> | 55 | /// </summary> |
@@ -58,7 +58,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
58 | /// <summary> | 58 | /// <summary> |
59 | /// The maximum major version of OAR that we can write. | 59 | /// The maximum major version of OAR that we can write. |
60 | /// </summary> | 60 | /// </summary> |
61 | public static int MAX_MAJOR_VERSION = 0; | 61 | public static int MAX_MAJOR_VERSION = 0; |
62 | |||
63 | /// <summary> | ||
64 | /// Determine whether this archive will save assets. Default is true. | ||
65 | /// </summary> | ||
66 | public bool SaveAssets { get; set; } | ||
62 | 67 | ||
63 | protected Scene m_scene; | 68 | protected Scene m_scene; |
64 | protected Stream m_saveStream; | 69 | protected Stream m_saveStream; |
@@ -73,10 +78,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
73 | /// <exception cref="System.IO.IOException"> | 78 | /// <exception cref="System.IO.IOException"> |
74 | /// If there was a problem opening a stream for the file specified by the savePath | 79 | /// If there was a problem opening a stream for the file specified by the savePath |
75 | /// </exception> | 80 | /// </exception> |
76 | public ArchiveWriteRequestPreparation(Scene scene, string savePath, Guid requestId) | 81 | public ArchiveWriteRequestPreparation(Scene scene, string savePath, Guid requestId) : this(scene, requestId) |
77 | { | 82 | { |
78 | m_scene = scene; | ||
79 | |||
80 | try | 83 | try |
81 | { | 84 | { |
82 | m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress); | 85 | m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress); |
@@ -86,10 +89,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
86 | m_log.ErrorFormat( | 89 | m_log.ErrorFormat( |
87 | "[ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream." | 90 | "[ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream." |
88 | + "If you've manually installed Mono, have you appropriately updated zlib1g as well?"); | 91 | + "If you've manually installed Mono, have you appropriately updated zlib1g as well?"); |
89 | m_log.Error(e); | 92 | m_log.ErrorFormat("{0} {1}", e.Message, e.StackTrace); |
90 | } | 93 | } |
91 | |||
92 | m_requestId = requestId; | ||
93 | } | 94 | } |
94 | 95 | ||
95 | /// <summary> | 96 | /// <summary> |
@@ -98,11 +99,17 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
98 | /// <param name="scene"></param> | 99 | /// <param name="scene"></param> |
99 | /// <param name="saveStream">The stream to which to save data.</param> | 100 | /// <param name="saveStream">The stream to which to save data.</param> |
100 | /// <param name="requestId">The id associated with this request</param> | 101 | /// <param name="requestId">The id associated with this request</param> |
101 | public ArchiveWriteRequestPreparation(Scene scene, Stream saveStream, Guid requestId) | 102 | public ArchiveWriteRequestPreparation(Scene scene, Stream saveStream, Guid requestId) : this(scene, requestId) |
102 | { | 103 | { |
103 | m_scene = scene; | ||
104 | m_saveStream = saveStream; | 104 | m_saveStream = saveStream; |
105 | } | ||
106 | |||
107 | protected ArchiveWriteRequestPreparation(Scene scene, Guid requestId) | ||
108 | { | ||
109 | m_scene = scene; | ||
105 | m_requestId = requestId; | 110 | m_requestId = requestId; |
111 | |||
112 | SaveAssets = true; | ||
106 | } | 113 | } |
107 | 114 | ||
108 | /// <summary> | 115 | /// <summary> |
@@ -111,22 +118,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
111 | /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception> | 118 | /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception> |
112 | public void ArchiveRegion(Dictionary<string, object> options) | 119 | public void ArchiveRegion(Dictionary<string, object> options) |
113 | { | 120 | { |
121 | if (options.ContainsKey("noassets") && (bool)options["noassets"]) | ||
122 | SaveAssets = false; | ||
123 | |||
114 | try | 124 | try |
115 | { | 125 | { |
116 | Dictionary<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>(); | 126 | Dictionary<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>(); |
117 | 127 | ||
118 | EntityBase[] entities = m_scene.GetEntities(); | 128 | EntityBase[] entities = m_scene.GetEntities(); |
119 | List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>(); | 129 | List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>(); |
120 | |||
121 | /* | ||
122 | foreach (ILandObject lo in m_scene.LandChannel.AllParcels()) | ||
123 | { | ||
124 | if (name == lo.LandData.Name) | ||
125 | { | ||
126 | // This is the parcel we want | ||
127 | } | ||
128 | } | ||
129 | */ | ||
130 | 130 | ||
131 | // Filter entities so that we only have scene objects. | 131 | // Filter entities so that we only have scene objects. |
132 | // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods | 132 | // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods |
@@ -141,17 +141,24 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
141 | sceneObjects.Add((SceneObjectGroup)entity); | 141 | sceneObjects.Add((SceneObjectGroup)entity); |
142 | } | 142 | } |
143 | } | 143 | } |
144 | 144 | ||
145 | UuidGatherer assetGatherer = new UuidGatherer(m_scene.AssetService); | 145 | if (SaveAssets) |
146 | 146 | { | |
147 | foreach (SceneObjectGroup sceneObject in sceneObjects) | 147 | UuidGatherer assetGatherer = new UuidGatherer(m_scene.AssetService); |
148 | |||
149 | foreach (SceneObjectGroup sceneObject in sceneObjects) | ||
150 | { | ||
151 | assetGatherer.GatherAssetUuids(sceneObject, assetUuids); | ||
152 | } | ||
153 | |||
154 | m_log.DebugFormat( | ||
155 | "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets", | ||
156 | sceneObjects.Count, assetUuids.Count); | ||
157 | } | ||
158 | else | ||
148 | { | 159 | { |
149 | assetGatherer.GatherAssetUuids(sceneObject, assetUuids); | 160 | m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified"); |
150 | } | 161 | } |
151 | |||
152 | m_log.DebugFormat( | ||
153 | "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets", | ||
154 | sceneObjects.Count, assetUuids.Count); | ||
155 | 162 | ||
156 | // Make sure that we also request terrain texture assets | 163 | // Make sure that we also request terrain texture assets |
157 | RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings; | 164 | RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings; |
@@ -187,11 +194,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
187 | // XXX: I know this is a weak way of doing it since external non-OAR aware tar executables will not do this | 194 | // XXX: I know this is a weak way of doing it since external non-OAR aware tar executables will not do this |
188 | archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(options)); | 195 | archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(options)); |
189 | m_log.InfoFormat("[ARCHIVER]: Added control file to archive."); | 196 | m_log.InfoFormat("[ARCHIVER]: Added control file to archive."); |
190 | 197 | ||
191 | new AssetsRequest( | 198 | if (SaveAssets) |
192 | new AssetsArchiver(archiveWriter), assetUuids, | 199 | new AssetsRequest( |
193 | m_scene.AssetService, m_scene.UserAccountService, | 200 | new AssetsArchiver(archiveWriter), assetUuids, |
194 | m_scene.RegionInfo.ScopeID, options, awre.ReceivedAllAssets).Execute(); | 201 | m_scene.AssetService, m_scene.UserAccountService, |
202 | m_scene.RegionInfo.ScopeID, options, awre.ReceivedAllAssets).Execute(); | ||
203 | else | ||
204 | awre.ReceivedAllAssets(new List<UUID>(), new List<UUID>()); | ||
195 | } | 205 | } |
196 | catch (Exception) | 206 | catch (Exception) |
197 | { | 207 | { |
@@ -204,9 +214,9 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
204 | /// Create the control file for the most up to date archive | 214 | /// Create the control file for the most up to date archive |
205 | /// </summary> | 215 | /// </summary> |
206 | /// <returns></returns> | 216 | /// <returns></returns> |
207 | public static string CreateControlFile(Dictionary<string, object> options) | 217 | public string CreateControlFile(Dictionary<string, object> options) |
208 | { | 218 | { |
209 | int majorVersion = MAX_MAJOR_VERSION, minorVersion = 6; | 219 | int majorVersion = MAX_MAJOR_VERSION, minorVersion = 7; |
210 | // | 220 | // |
211 | // if (options.ContainsKey("version")) | 221 | // if (options.ContainsKey("version")) |
212 | // { | 222 | // { |
@@ -258,6 +268,9 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
258 | xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString()); | 268 | xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString()); |
259 | xtw.WriteElementString("id", UUID.Random().ToString()); | 269 | xtw.WriteElementString("id", UUID.Random().ToString()); |
260 | xtw.WriteEndElement(); | 270 | xtw.WriteEndElement(); |
271 | |||
272 | xtw.WriteElementString("assets_included", SaveAssets.ToString()); | ||
273 | |||
261 | xtw.WriteEndElement(); | 274 | xtw.WriteEndElement(); |
262 | 275 | ||
263 | xtw.Flush(); | 276 | xtw.Flush(); |
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs index 9277c59..08eb80c 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs | |||
@@ -127,6 +127,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
127 | OptionSet ops = new OptionSet(); | 127 | OptionSet ops = new OptionSet(); |
128 | // ops.Add("v|version=", delegate(string v) { options["version"] = v; }); | 128 | // ops.Add("v|version=", delegate(string v) { options["version"] = v; }); |
129 | ops.Add("p|profile=", delegate(string v) { options["profile"] = v; }); | 129 | ops.Add("p|profile=", delegate(string v) { options["profile"] = v; }); |
130 | ops.Add("noassets", delegate(string v) { options["noassets"] = v != null; }); | ||
130 | 131 | ||
131 | List<string> mainParams = ops.Parse(cmdparams); | 132 | List<string> mainParams = ops.Parse(cmdparams); |
132 | 133 | ||
@@ -160,7 +161,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
160 | 161 | ||
161 | public void ArchiveRegion(Stream saveStream, Guid requestId) | 162 | public void ArchiveRegion(Stream saveStream, Guid requestId) |
162 | { | 163 | { |
163 | new ArchiveWriteRequestPreparation(m_scene, saveStream, requestId).ArchiveRegion(new Dictionary<string, object>()); | 164 | ArchiveRegion(saveStream, requestId, new Dictionary<string, object>()); |
165 | } | ||
166 | |||
167 | public void ArchiveRegion(Stream saveStream, Guid requestId, Dictionary<string, object> options) | ||
168 | { | ||
169 | new ArchiveWriteRequestPreparation(m_scene, saveStream, requestId).ArchiveRegion(options); | ||
164 | } | 170 | } |
165 | 171 | ||
166 | public void DearchiveRegion(string loadPath) | 172 | public void DearchiveRegion(string loadPath) |
diff --git a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs index 2eb2861..34e2e23 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs | |||
@@ -212,6 +212,89 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests | |||
212 | } | 212 | } |
213 | 213 | ||
214 | /// <summary> | 214 | /// <summary> |
215 | /// Test saving an OpenSim Region Archive with the no assets option | ||
216 | /// </summary> | ||
217 | [Test] | ||
218 | public void TestSaveOarNoAssets() | ||
219 | { | ||
220 | TestHelper.InMethod(); | ||
221 | // log4net.Config.XmlConfigurator.Configure(); | ||
222 | |||
223 | SceneObjectPart part1 = CreateSceneObjectPart1(); | ||
224 | SceneObjectGroup sog1 = new SceneObjectGroup(part1); | ||
225 | m_scene.AddNewSceneObject(sog1, false); | ||
226 | |||
227 | SceneObjectPart part2 = CreateSceneObjectPart2(); | ||
228 | |||
229 | AssetNotecard nc = new AssetNotecard(); | ||
230 | nc.BodyText = "Hello World!"; | ||
231 | nc.Encode(); | ||
232 | UUID ncAssetUuid = new UUID("00000000-0000-0000-1000-000000000000"); | ||
233 | UUID ncItemUuid = new UUID("00000000-0000-0000-1100-000000000000"); | ||
234 | AssetBase ncAsset | ||
235 | = AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero); | ||
236 | m_scene.AssetService.Store(ncAsset); | ||
237 | SceneObjectGroup sog2 = new SceneObjectGroup(part2); | ||
238 | TaskInventoryItem ncItem | ||
239 | = new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid }; | ||
240 | part2.Inventory.AddInventoryItem(ncItem, true); | ||
241 | |||
242 | m_scene.AddNewSceneObject(sog2, false); | ||
243 | |||
244 | MemoryStream archiveWriteStream = new MemoryStream(); | ||
245 | |||
246 | Guid requestId = new Guid("00000000-0000-0000-0000-808080808080"); | ||
247 | |||
248 | Dictionary<string, Object> options = new Dictionary<string, Object>(); | ||
249 | options.Add("noassets", true); | ||
250 | m_archiverModule.ArchiveRegion(archiveWriteStream, requestId, options); | ||
251 | //AssetServerBase assetServer = (AssetServerBase)scene.CommsManager.AssetCache.AssetServer; | ||
252 | //while (assetServer.HasWaitingRequests()) | ||
253 | // assetServer.ProcessNextRequest(); | ||
254 | |||
255 | // Don't wait for completion - with --noassets save oar happens synchronously | ||
256 | // Monitor.Wait(this, 60000); | ||
257 | |||
258 | Assert.That(m_lastRequestId, Is.EqualTo(requestId)); | ||
259 | |||
260 | byte[] archive = archiveWriteStream.ToArray(); | ||
261 | MemoryStream archiveReadStream = new MemoryStream(archive); | ||
262 | TarArchiveReader tar = new TarArchiveReader(archiveReadStream); | ||
263 | |||
264 | List<string> foundPaths = new List<string>(); | ||
265 | List<string> expectedPaths = new List<string>(); | ||
266 | expectedPaths.Add(ArchiveHelpers.CreateObjectPath(sog1)); | ||
267 | expectedPaths.Add(ArchiveHelpers.CreateObjectPath(sog2)); | ||
268 | |||
269 | string filePath; | ||
270 | TarArchiveReader.TarEntryType tarEntryType; | ||
271 | |||
272 | byte[] data = tar.ReadEntry(out filePath, out tarEntryType); | ||
273 | Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH)); | ||
274 | |||
275 | ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty); | ||
276 | arr.LoadControlFile(filePath, data); | ||
277 | |||
278 | Assert.That(arr.ControlFileLoaded, Is.True); | ||
279 | |||
280 | while (tar.ReadEntry(out filePath, out tarEntryType) != null) | ||
281 | { | ||
282 | if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH)) | ||
283 | { | ||
284 | Assert.Fail("Asset was found in saved oar of TestSaveOarNoAssets()"); | ||
285 | } | ||
286 | else if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH)) | ||
287 | { | ||
288 | foundPaths.Add(filePath); | ||
289 | } | ||
290 | } | ||
291 | |||
292 | Assert.That(foundPaths, Is.EquivalentTo(expectedPaths)); | ||
293 | |||
294 | // TODO: Test presence of more files and contents of files. | ||
295 | } | ||
296 | |||
297 | /// <summary> | ||
215 | /// Test loading an OpenSim Region Archive. | 298 | /// Test loading an OpenSim Region Archive. |
216 | /// </summary> | 299 | /// </summary> |
217 | [Test] | 300 | [Test] |
@@ -230,7 +313,9 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests | |||
230 | // upset load | 313 | // upset load |
231 | tar.WriteDir(ArchiveConstants.TERRAINS_PATH); | 314 | tar.WriteDir(ArchiveConstants.TERRAINS_PATH); |
232 | 315 | ||
233 | tar.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, ArchiveWriteRequestPreparation.CreateControlFile(new Dictionary<string, Object>())); | 316 | tar.WriteFile( |
317 | ArchiveConstants.CONTROL_FILE_PATH, | ||
318 | new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary<string, Object>())); | ||
234 | 319 | ||
235 | SceneObjectPart part1 = CreateSceneObjectPart1(); | 320 | SceneObjectPart part1 = CreateSceneObjectPart1(); |
236 | SceneObjectGroup object1 = new SceneObjectGroup(part1); | 321 | SceneObjectGroup object1 = new SceneObjectGroup(part1); |
@@ -331,7 +416,9 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests | |||
331 | TarArchiveWriter tar = new TarArchiveWriter(archiveWriteStream); | 416 | TarArchiveWriter tar = new TarArchiveWriter(archiveWriteStream); |
332 | 417 | ||
333 | tar.WriteDir(ArchiveConstants.TERRAINS_PATH); | 418 | tar.WriteDir(ArchiveConstants.TERRAINS_PATH); |
334 | tar.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, ArchiveWriteRequestPreparation.CreateControlFile(new Dictionary<string, Object>())); | 419 | tar.WriteFile( |
420 | ArchiveConstants.CONTROL_FILE_PATH, | ||
421 | new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary<string, Object>())); | ||
335 | 422 | ||
336 | RegionSettings rs = new RegionSettings(); | 423 | RegionSettings rs = new RegionSettings(); |
337 | rs.AgentLimit = 17; | 424 | rs.AgentLimit = 17; |
diff --git a/OpenSim/Region/Framework/Interfaces/IRegionArchiverModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionArchiverModule.cs index d8229de..3fafc47 100644 --- a/OpenSim/Region/Framework/Interfaces/IRegionArchiverModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IRegionArchiverModule.cs | |||
@@ -52,31 +52,44 @@ namespace OpenSim.Region.Framework.Interfaces | |||
52 | /// <summary> | 52 | /// <summary> |
53 | /// Archive the region to the given path | 53 | /// Archive the region to the given path |
54 | /// </summary> | 54 | /// </summary> |
55 | /// | 55 | /// <remarks> |
56 | /// This method occurs asynchronously. If you want notification of when it has completed then subscribe to | 56 | /// This method occurs asynchronously. If you want notification of when it has completed then subscribe to |
57 | /// the EventManager.OnOarFileSaved event. | 57 | /// the EventManager.OnOarFileSaved event. |
58 | /// | 58 | /// </remarks> |
59 | /// <param name="savePath"></param> | 59 | /// <param name="savePath"></param> |
60 | /// <param name="requestId">If supplied, this request Id is later returned in the saved event</param> | 60 | /// <param name="requestId">If supplied, this request Id is later returned in the saved event</param> |
61 | /// <param name="options">Options for the save</param> | ||
61 | void ArchiveRegion(string savePath, Guid requestId, Dictionary<string, object> options); | 62 | void ArchiveRegion(string savePath, Guid requestId, Dictionary<string, object> options); |
62 | 63 | ||
63 | /// <summary> | 64 | /// <summary> |
64 | /// Archive the region to a stream. | 65 | /// Archive the region to a stream. |
65 | /// </summary> | 66 | /// </summary> |
66 | /// | 67 | /// <remarks> |
67 | /// This method occurs asynchronously. If you want notification of when it has completed then subscribe to | 68 | /// This method occurs asynchronously. If you want notification of when it has completed then subscribe to |
68 | /// the EventManager.OnOarFileSaved event. | 69 | /// the EventManager.OnOarFileSaved event. |
69 | /// | 70 | /// </remarks> |
70 | /// <param name="saveStream"></param> | 71 | /// <param name="saveStream"></param> |
71 | /// <param name="requestId">If supplied, this request Id is later returned in the saved event</param> | 72 | /// <param name="requestId">If supplied, this request Id is later returned in the saved event</param> |
72 | void ArchiveRegion(Stream saveStream, Guid requestId); | 73 | void ArchiveRegion(Stream saveStream, Guid requestId); |
73 | 74 | ||
74 | /// <summary> | 75 | /// <summary> |
76 | /// Archive the region to a stream. | ||
77 | /// </summary> | ||
78 | /// <remarks> | ||
79 | /// This method occurs asynchronously. If you want notification of when it has completed then subscribe to | ||
80 | /// the EventManager.OnOarFileSaved event. | ||
81 | /// </remarks> | ||
82 | /// <param name="saveStream"></param> | ||
83 | /// <param name="requestId">If supplied, this request Id is later returned in the saved event</param> | ||
84 | /// <param name="options">Options for the save</param> | ||
85 | void ArchiveRegion(Stream saveStream, Guid requestId, Dictionary<string, object> options); | ||
86 | |||
87 | /// <summary> | ||
75 | /// Dearchive the given region archive. This replaces the existing scene. | 88 | /// Dearchive the given region archive. This replaces the existing scene. |
76 | /// </summary> | 89 | /// </summary> |
77 | /// | 90 | /// <remarks> |
78 | /// If you want notification of when it has completed then subscribe to the EventManager.OnOarFileLoaded event. | 91 | /// If you want notification of when it has completed then subscribe to the EventManager.OnOarFileLoaded event. |
79 | /// | 92 | /// </remarks> |
80 | /// <param name="loadPath"></param> | 93 | /// <param name="loadPath"></param> |
81 | void DearchiveRegion(string loadPath); | 94 | void DearchiveRegion(string loadPath); |
82 | 95 | ||
diff --git a/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs b/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs index 8589901..7304145 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs | |||
@@ -95,8 +95,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance | |||
95 | scene.AddCommand( | 95 | scene.AddCommand( |
96 | this, "appearance show", | 96 | this, "appearance show", |
97 | "appearance show", | 97 | "appearance show", |
98 | "Show appearance information for each avatar in the simulator. At the moment, ", | 98 | "Show appearance information for each avatar in the simulator.", |
99 | ShowAppearanceInfo); | 99 | "At the moment this actually just checks that we have all the required baked textures. If not, then appearance is 'corrupt' and other avatars will continue to see a cloud.", |
100 | ShowAppearanceInfo); | ||
100 | } | 101 | } |
101 | 102 | ||
102 | protected void ShowAppearanceInfo(string module, string[] cmd) | 103 | protected void ShowAppearanceInfo(string module, string[] cmd) |