aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region
diff options
context:
space:
mode:
authorJustin Clark-Casey (justincc)2010-07-14 20:43:35 +0100
committerJustin Clark-Casey (justincc)2010-07-14 20:43:35 +0100
commit8c10cb5ffe783d457184923d32e8baea5321ed1d (patch)
treedcebba549aeb5744e872c587814b48f309f6ccd4 /OpenSim/Region
parentMerge branch 'master' of ssh://opensimulator.org/var/git/opensim (diff)
downloadopensim-SC-8c10cb5ffe783d457184923d32e8baea5321ed1d.zip
opensim-SC-8c10cb5ffe783d457184923d32e8baea5321ed1d.tar.gz
opensim-SC-8c10cb5ffe783d457184923d32e8baea5321ed1d.tar.bz2
opensim-SC-8c10cb5ffe783d457184923d32e8baea5321ed1d.tar.xz
improve closing of load/save iar streams in the event of a problem
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs80
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs139
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverException.cs40
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs111
4 files changed, 213 insertions, 157 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs
index c8697fe..31dfe14 100644
--- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs
@@ -93,37 +93,37 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
93 /// </returns> 93 /// </returns>
94 public List<InventoryNodeBase> Execute() 94 public List<InventoryNodeBase> Execute()
95 { 95 {
96 string filePath = "ERROR";
97 int successfulAssetRestores = 0;
98 int failedAssetRestores = 0;
99 int successfulItemRestores = 0;
100
101 List<InventoryNodeBase> loadedNodes = new List<InventoryNodeBase>();
102
103 List<InventoryFolderBase> folderCandidates
104 = InventoryArchiveUtils.FindFolderByPath(
105 m_scene.InventoryService, m_userInfo.PrincipalID, m_invPath);
106
107 if (folderCandidates.Count == 0)
108 {
109 // Possibly provide an option later on to automatically create this folder if it does not exist
110 m_log.ErrorFormat("[INVENTORY ARCHIVER]: Inventory path {0} does not exist", m_invPath);
111
112 return loadedNodes;
113 }
114
115 InventoryFolderBase rootDestinationFolder = folderCandidates[0];
116 archive = new TarArchiveReader(m_loadStream);
117
118 // In order to load identically named folders, we need to keep track of the folders that we have already
119 // resolved
120 Dictionary <string, InventoryFolderBase> resolvedFolders = new Dictionary<string, InventoryFolderBase>();
121
122 byte[] data;
123 TarArchiveReader.TarEntryType entryType;
124
125 try 96 try
126 { 97 {
98 string filePath = "ERROR";
99 int successfulAssetRestores = 0;
100 int failedAssetRestores = 0;
101 int successfulItemRestores = 0;
102
103 List<InventoryNodeBase> loadedNodes = new List<InventoryNodeBase>();
104
105 List<InventoryFolderBase> folderCandidates
106 = InventoryArchiveUtils.FindFolderByPath(
107 m_scene.InventoryService, m_userInfo.PrincipalID, m_invPath);
108
109 if (folderCandidates.Count == 0)
110 {
111 // Possibly provide an option later on to automatically create this folder if it does not exist
112 m_log.ErrorFormat("[INVENTORY ARCHIVER]: Inventory path {0} does not exist", m_invPath);
113
114 return loadedNodes;
115 }
116
117 InventoryFolderBase rootDestinationFolder = folderCandidates[0];
118 archive = new TarArchiveReader(m_loadStream);
119
120 // In order to load identically named folders, we need to keep track of the folders that we have already
121 // resolved
122 Dictionary <string, InventoryFolderBase> resolvedFolders = new Dictionary<string, InventoryFolderBase>();
123
124 byte[] data;
125 TarArchiveReader.TarEntryType entryType;
126
127 while ((data = archive.ReadEntry(out filePath, out entryType)) != null) 127 while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
128 { 128 {
129 if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH)) 129 if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH))
@@ -166,18 +166,20 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
166 } 166 }
167 } 167 }
168 } 168 }
169
170 archive.Close();
171
172 m_log.DebugFormat(
173 "[INVENTORY ARCHIVER]: Successfully loaded {0} assets with {1} failures",
174 successfulAssetRestores, failedAssetRestores);
175 m_log.InfoFormat("[INVENTORY ARCHIVER]: Successfully loaded {0} items", successfulItemRestores);
176
177 return loadedNodes;
169 } 178 }
170 finally 179 finally
171 { 180 {
172 archive.Close(); 181 m_loadStream.Close();
173 } 182 }
174
175 m_log.DebugFormat(
176 "[INVENTORY ARCHIVER]: Successfully loaded {0} assets with {1} failures",
177 successfulAssetRestores, failedAssetRestores);
178 m_log.InfoFormat("[INVENTORY ARCHIVER]: Successfully loaded {0} items", successfulItemRestores);
179
180 return loadedNodes;
181 } 183 }
182 184
183 public void Close() 185 public void Close()
@@ -247,7 +249,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
247 ref string archivePath, 249 ref string archivePath,
248 Dictionary <string, InventoryFolderBase> resolvedFolders) 250 Dictionary <string, InventoryFolderBase> resolvedFolders)
249 { 251 {
250 string originalArchivePath = archivePath; 252// string originalArchivePath = archivePath;
251 253
252 InventoryFolderBase destFolder = null; 254 InventoryFolderBase destFolder = null;
253 255
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs
index 2c2724e..25a78ff 100644
--- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs
@@ -119,22 +119,24 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
119 protected void ReceivedAllAssets(ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids) 119 protected void ReceivedAllAssets(ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids)
120 { 120 {
121 Exception reportedException = null; 121 Exception reportedException = null;
122 bool succeeded = true; 122 bool succeeded = true;
123 123
124 try 124 try
125 { 125 {
126 // We're almost done. Just need to write out the control file now 126 // We're almost done. Just need to write out the control file now
127 m_archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, Create0p1ControlFile()); 127 m_archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, Create0p1ControlFile());
128 m_log.InfoFormat("[ARCHIVER]: Added control file to archive."); 128 m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");
129
130 m_archiveWriter.Close(); 129 m_archiveWriter.Close();
131 } 130 }
132 catch (Exception e) 131 catch (Exception e)
133 { 132 {
134 m_saveStream.Close();
135 reportedException = e; 133 reportedException = e;
136 succeeded = false; 134 succeeded = false;
137 } 135 }
136 finally
137 {
138 m_saveStream.Close();
139 }
138 140
139 m_module.TriggerInventoryArchiveSaved( 141 m_module.TriggerInventoryArchiveSaved(
140 m_id, succeeded, m_userInfo, m_invPath, m_saveStream, reportedException); 142 m_id, succeeded, m_userInfo, m_invPath, m_saveStream, reportedException);
@@ -213,70 +215,68 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
213 /// </summary> 215 /// </summary>
214 public void Execute() 216 public void Execute()
215 { 217 {
216 InventoryFolderBase inventoryFolder = null; 218 try
217 InventoryItemBase inventoryItem = null;
218 InventoryFolderBase rootFolder = m_scene.InventoryService.GetRootFolder(m_userInfo.PrincipalID);
219
220 bool foundStar = false;
221
222 // Eliminate double slashes and any leading / on the path.
223 string[] components
224 = m_invPath.Split(
225 new string[] { InventoryFolderImpl.PATH_DELIMITER }, StringSplitOptions.RemoveEmptyEntries);
226
227 int maxComponentIndex = components.Length - 1;
228
229 // If the path terminates with a STAR then later on we want to archive all nodes in the folder but not the
230 // folder itself. This may get more sophisicated later on
231 if (maxComponentIndex >= 0 && components[maxComponentIndex] == STAR_WILDCARD)
232 {
233 foundStar = true;
234 maxComponentIndex--;
235 }
236
237 m_invPath = String.Empty;
238 for (int i = 0; i <= maxComponentIndex; i++)
239 {
240 m_invPath += components[i] + InventoryFolderImpl.PATH_DELIMITER;
241 }
242
243 // Annoyingly Split actually returns the original string if the input string consists only of delimiters
244 // Therefore if we still start with a / after the split, then we need the root folder
245 if (m_invPath.Length == 0)
246 {
247 inventoryFolder = rootFolder;
248 }
249 else
250 {
251 m_invPath = m_invPath.Remove(m_invPath.LastIndexOf(InventoryFolderImpl.PATH_DELIMITER));
252 List<InventoryFolderBase> candidateFolders
253 = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, rootFolder, m_invPath);
254 if (candidateFolders.Count > 0)
255 inventoryFolder = candidateFolders[0];
256 }
257
258 // The path may point to an item instead
259 if (inventoryFolder == null)
260 {
261 inventoryItem = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, rootFolder, m_invPath);
262 //inventoryItem = m_userInfo.RootFolder.FindItemByPath(m_invPath);
263 }
264
265 if (null == inventoryFolder && null == inventoryItem)
266 { 219 {
267 // We couldn't find the path indicated 220 InventoryFolderBase inventoryFolder = null;
268 string errorMessage = string.Format("Aborted save. Could not find inventory path {0}", m_invPath); 221 InventoryItemBase inventoryItem = null;
269 m_log.ErrorFormat("[INVENTORY ARCHIVER]: {0}", errorMessage); 222 InventoryFolderBase rootFolder = m_scene.InventoryService.GetRootFolder(m_userInfo.PrincipalID);
270 m_module.TriggerInventoryArchiveSaved( 223
271 m_id, false, m_userInfo, m_invPath, m_saveStream, 224 bool foundStar = false;
272 new Exception(errorMessage)); 225
273 return; 226 // Eliminate double slashes and any leading / on the path.
274 } 227 string[] components
228 = m_invPath.Split(
229 new string[] { InventoryFolderImpl.PATH_DELIMITER }, StringSplitOptions.RemoveEmptyEntries);
230
231 int maxComponentIndex = components.Length - 1;
232
233 // If the path terminates with a STAR then later on we want to archive all nodes in the folder but not the
234 // folder itself. This may get more sophisicated later on
235 if (maxComponentIndex >= 0 && components[maxComponentIndex] == STAR_WILDCARD)
236 {
237 foundStar = true;
238 maxComponentIndex--;
239 }
240
241 m_invPath = String.Empty;
242 for (int i = 0; i <= maxComponentIndex; i++)
243 {
244 m_invPath += components[i] + InventoryFolderImpl.PATH_DELIMITER;
245 }
246
247 // Annoyingly Split actually returns the original string if the input string consists only of delimiters
248 // Therefore if we still start with a / after the split, then we need the root folder
249 if (m_invPath.Length == 0)
250 {
251 inventoryFolder = rootFolder;
252 }
253 else
254 {
255 m_invPath = m_invPath.Remove(m_invPath.LastIndexOf(InventoryFolderImpl.PATH_DELIMITER));
256 List<InventoryFolderBase> candidateFolders
257 = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, rootFolder, m_invPath);
258 if (candidateFolders.Count > 0)
259 inventoryFolder = candidateFolders[0];
260 }
261
262 // The path may point to an item instead
263 if (inventoryFolder == null)
264 {
265 inventoryItem = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, rootFolder, m_invPath);
266 //inventoryItem = m_userInfo.RootFolder.FindItemByPath(m_invPath);
267 }
268
269 if (null == inventoryFolder && null == inventoryItem)
270 {
271 // We couldn't find the path indicated
272 string errorMessage = string.Format("Aborted save. Could not find inventory path {0}", m_invPath);
273 Exception e = new InventoryArchiverException(errorMessage);
274 m_module.TriggerInventoryArchiveSaved(m_id, false, m_userInfo, m_invPath, m_saveStream, e);
275 throw e;
276 }
275 277
276 m_archiveWriter = new TarArchiveWriter(m_saveStream); 278 m_archiveWriter = new TarArchiveWriter(m_saveStream);
277 279
278 try
279 {
280 if (inventoryFolder != null) 280 if (inventoryFolder != null)
281 { 281 {
282 m_log.DebugFormat( 282 m_log.DebugFormat(
@@ -297,16 +297,15 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
297 297
298 // Don't put all this profile information into the archive right now. 298 // Don't put all this profile information into the archive right now.
299 //SaveUsers(); 299 //SaveUsers();
300
301 new AssetsRequest(
302 new AssetsArchiver(m_archiveWriter), m_assetUuids, m_scene.AssetService, ReceivedAllAssets).Execute();
300 } 303 }
301 catch (Exception) 304 catch (Exception)
302 { 305 {
303 m_archiveWriter.Close(); 306 m_saveStream.Close();
304 throw; 307 throw;
305 } 308 }
306
307 new AssetsRequest(
308 new AssetsArchiver(m_archiveWriter), m_assetUuids,
309 m_scene.AssetService, ReceivedAllAssets).Execute();
310 } 309 }
311 310
312 /// <summary> 311 /// <summary>
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverException.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverException.cs
new file mode 100644
index 0000000..e07e2ca
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverException.cs
@@ -0,0 +1,40 @@
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
28using System;
29
30namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
31{
32 /// <summary>
33 /// Signals an inventory archiving problem
34 /// </summary>
35 public class InventoryArchiverException : Exception
36 {
37 public InventoryArchiverException(string message) : base(message) {}
38 public InventoryArchiverException(string message, Exception e) : base(message, e) {}
39 }
40} \ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs
index cfefbe9..f7a2b09 100644
--- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs
@@ -322,34 +322,41 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
322 /// <param name="cmdparams"></param> 322 /// <param name="cmdparams"></param>
323 protected void HandleLoadInvConsoleCommand(string module, string[] cmdparams) 323 protected void HandleLoadInvConsoleCommand(string module, string[] cmdparams)
324 { 324 {
325 m_log.Info("[INVENTORY ARCHIVER]: PLEASE NOTE THAT THIS FACILITY IS EXPERIMENTAL. BUG REPORTS WELCOME."); 325 try
326 326 {
327 Dictionary<string, object> options = new Dictionary<string, object>(); 327 m_log.Info("[INVENTORY ARCHIVER]: PLEASE NOTE THAT THIS FACILITY IS EXPERIMENTAL. BUG REPORTS WELCOME.");
328 OptionSet optionSet = new OptionSet().Add("m|merge", delegate (string v) { options["merge"] = v != null; }); 328
329 329 Dictionary<string, object> options = new Dictionary<string, object>();
330 List<string> mainParams = optionSet.Parse(cmdparams); 330 OptionSet optionSet = new OptionSet().Add("m|merge", delegate (string v) { options["merge"] = v != null; });
331 331
332 if (mainParams.Count < 6) 332 List<string> mainParams = optionSet.Parse(cmdparams);
333 { 333
334 m_log.Error( 334 if (mainParams.Count < 6)
335 "[INVENTORY ARCHIVER]: usage is load iar <first name> <last name> <inventory path> <user password> [<load file path>]"); 335 {
336 return; 336 m_log.Error(
337 } 337 "[INVENTORY ARCHIVER]: usage is load iar <first name> <last name> <inventory path> <user password> [<load file path>]");
338 338 return;
339 string firstName = mainParams[2]; 339 }
340 string lastName = mainParams[3]; 340
341 string invPath = mainParams[4]; 341 string firstName = mainParams[2];
342 string pass = mainParams[5]; 342 string lastName = mainParams[3];
343 string loadPath = (mainParams.Count > 6 ? mainParams[6] : DEFAULT_INV_BACKUP_FILENAME); 343 string invPath = mainParams[4];
344 344 string pass = mainParams[5];
345 m_log.InfoFormat( 345 string loadPath = (mainParams.Count > 6 ? mainParams[6] : DEFAULT_INV_BACKUP_FILENAME);
346 "[INVENTORY ARCHIVER]: Loading archive {0} to inventory path {1} for {2} {3}", 346
347 loadPath, invPath, firstName, lastName);
348
349 if (DearchiveInventory(firstName, lastName, invPath, pass, loadPath, options))
350 m_log.InfoFormat( 347 m_log.InfoFormat(
351 "[INVENTORY ARCHIVER]: Loaded archive {0} for {1} {2}", 348 "[INVENTORY ARCHIVER]: Loading archive {0} to inventory path {1} for {2} {3}",
352 loadPath, firstName, lastName); 349 loadPath, invPath, firstName, lastName);
350
351 if (DearchiveInventory(firstName, lastName, invPath, pass, loadPath, options))
352 m_log.InfoFormat(
353 "[INVENTORY ARCHIVER]: Loaded archive {0} for {1} {2}",
354 loadPath, firstName, lastName);
355 }
356 catch (InventoryArchiverException e)
357 {
358 m_log.ErrorFormat("[INVENTORY ARCHIVER]: {0}", e.Message);
359 }
353 } 360 }
354 361
355 /// <summary> 362 /// <summary>
@@ -358,30 +365,38 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
358 /// <param name="cmdparams"></param> 365 /// <param name="cmdparams"></param>
359 protected void HandleSaveInvConsoleCommand(string module, string[] cmdparams) 366 protected void HandleSaveInvConsoleCommand(string module, string[] cmdparams)
360 { 367 {
361 if (cmdparams.Length < 6) 368 Guid id = Guid.NewGuid();
369
370 try
362 { 371 {
363 m_log.Error( 372 if (cmdparams.Length < 6)
364 "[INVENTORY ARCHIVER]: usage is save iar <first name> <last name> <inventory path> <user password> [<save file path>]"); 373 {
365 return; 374 m_log.Error(
375 "[INVENTORY ARCHIVER]: usage is save iar <first name> <last name> <inventory path> <user password> [<save file path>]");
376 return;
377 }
378
379 m_log.Info("[INVENTORY ARCHIVER]: PLEASE NOTE THAT THIS FACILITY IS EXPERIMENTAL. BUG REPORTS WELCOME.");
380
381 string firstName = cmdparams[2];
382 string lastName = cmdparams[3];
383 string invPath = cmdparams[4];
384 string pass = cmdparams[5];
385 string savePath = (cmdparams.Length > 6 ? cmdparams[6] : DEFAULT_INV_BACKUP_FILENAME);
386
387 m_log.InfoFormat(
388 "[INVENTORY ARCHIVER]: Saving archive {0} using inventory path {1} for {2} {3}",
389 savePath, invPath, firstName, lastName);
390
391 ArchiveInventory(id, firstName, lastName, invPath, pass, savePath, new Dictionary<string, object>());
366 } 392 }
367 393 catch (InventoryArchiverException e)
368 m_log.Info("[INVENTORY ARCHIVER]: PLEASE NOTE THAT THIS FACILITY IS EXPERIMENTAL. BUG REPORTS WELCOME."); 394 {
369 395 m_log.ErrorFormat("[INVENTORY ARCHIVER]: {0}", e.Message);
370 string firstName = cmdparams[2]; 396 }
371 string lastName = cmdparams[3]; 397
372 string invPath = cmdparams[4];
373 string pass = cmdparams[5];
374 string savePath = (cmdparams.Length > 6 ? cmdparams[6] : DEFAULT_INV_BACKUP_FILENAME);
375
376 m_log.InfoFormat(
377 "[INVENTORY ARCHIVER]: Saving archive {0} using inventory path {1} for {2} {3}",
378 savePath, invPath, firstName, lastName);
379
380 Guid id = Guid.NewGuid();
381 ArchiveInventory(id, firstName, lastName, invPath, pass, savePath, new Dictionary<string, object>());
382
383 lock (m_pendingConsoleSaves) 398 lock (m_pendingConsoleSaves)
384 m_pendingConsoleSaves.Add(id); 399 m_pendingConsoleSaves.Add(id);
385 } 400 }
386 401
387 private void SaveInvConsoleCommandCompleted( 402 private void SaveInvConsoleCommandCompleted(