diff options
Diffstat (limited to 'OpenSim/Region/CoreModules')
6 files changed, 330 insertions, 91 deletions
diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index 49b459a..6b0af6d 100644 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs | |||
@@ -129,18 +129,18 @@ namespace Flotsam.RegionModules.AssetCache | |||
129 | if (name == Name) | 129 | if (name == Name) |
130 | { | 130 | { |
131 | m_Enabled = true; | 131 | m_Enabled = true; |
132 | m_log.InfoFormat("[ASSET CACHE]: {0} enabled", this.Name); | 132 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} enabled", this.Name); |
133 | 133 | ||
134 | IConfig assetConfig = source.Configs["AssetCache"]; | 134 | IConfig assetConfig = source.Configs["AssetCache"]; |
135 | if (assetConfig == null) | 135 | if (assetConfig == null) |
136 | { | 136 | { |
137 | m_log.Warn("[ASSET CACHE]: AssetCache missing from OpenSim.ini, using defaults."); | 137 | m_log.Warn("[FLOTSAM ASSET CACHE]: AssetCache missing from OpenSim.ini, using defaults."); |
138 | m_log.InfoFormat("[ASSET CACHE]: Cache Directory", m_DefaultCacheDirectory); | 138 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory", m_DefaultCacheDirectory); |
139 | return; | 139 | return; |
140 | } | 140 | } |
141 | 141 | ||
142 | m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory); | 142 | m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory); |
143 | m_log.InfoFormat("[ASSET CACHE]: Cache Directory", m_DefaultCacheDirectory); | 143 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory", m_DefaultCacheDirectory); |
144 | 144 | ||
145 | m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", true); | 145 | m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", true); |
146 | m_MemoryExpiration = TimeSpan.FromHours(assetConfig.GetDouble("MemoryCacheTimeout", m_DefaultMemoryExpiration)); | 146 | m_MemoryExpiration = TimeSpan.FromHours(assetConfig.GetDouble("MemoryCacheTimeout", m_DefaultMemoryExpiration)); |
@@ -191,6 +191,8 @@ namespace Flotsam.RegionModules.AssetCache | |||
191 | } | 191 | } |
192 | 192 | ||
193 | m_CacheWarnAt = assetConfig.GetInt("CacheWarnAt", 30000); | 193 | m_CacheWarnAt = assetConfig.GetInt("CacheWarnAt", 30000); |
194 | |||
195 | |||
194 | } | 196 | } |
195 | } | 197 | } |
196 | } | 198 | } |
@@ -206,7 +208,14 @@ namespace Flotsam.RegionModules.AssetCache | |||
206 | public void AddRegion(Scene scene) | 208 | public void AddRegion(Scene scene) |
207 | { | 209 | { |
208 | if (m_Enabled) | 210 | if (m_Enabled) |
211 | { | ||
209 | scene.RegisterModuleInterface<IImprovedAssetCache>(this); | 212 | scene.RegisterModuleInterface<IImprovedAssetCache>(this); |
213 | |||
214 | //scene.AddCommand(this, "flotsamcache", "", "Display a list of console commands for the Flotsam Asset Cache", HandleConsoleCommand); | ||
215 | scene.AddCommand(this, "flotsamcache counts", "flotsamcache counts", "Display the number of cached assets", HandleConsoleCommand); | ||
216 | scene.AddCommand(this, "flotsamcache clearmem", "flotsamcache clearmem", "Remove all assets cached in memory", HandleConsoleCommand); | ||
217 | scene.AddCommand(this, "flotsamcache clearfile", "flotsamcache clearfile", "Remove all assets cached on disk", HandleConsoleCommand); | ||
218 | } | ||
210 | } | 219 | } |
211 | 220 | ||
212 | public void RemoveRegion(Scene scene) | 221 | public void RemoveRegion(Scene scene) |
@@ -368,16 +377,16 @@ namespace Flotsam.RegionModules.AssetCache | |||
368 | { | 377 | { |
369 | m_HitRateFile = (double)m_DiskHits / m_Requests * 100.0; | 378 | m_HitRateFile = (double)m_DiskHits / m_Requests * 100.0; |
370 | 379 | ||
371 | m_log.InfoFormat("[ASSET CACHE]: Cache Get :: {0} :: {1}", id, asset == null ? "Miss" : "Hit"); | 380 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Get :: {0} :: {1}", id, asset == null ? "Miss" : "Hit"); |
372 | m_log.InfoFormat("[ASSET CACHE]: File Hit Rate {0}% for {1} requests", m_HitRateFile.ToString("0.00"), m_Requests); | 381 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File Hit Rate {0}% for {1} requests", m_HitRateFile.ToString("0.00"), m_Requests); |
373 | 382 | ||
374 | if (m_MemoryCacheEnabled) | 383 | if (m_MemoryCacheEnabled) |
375 | { | 384 | { |
376 | m_HitRateMemory = (double)m_MemoryHits / m_Requests * 100.0; | 385 | m_HitRateMemory = (double)m_MemoryHits / m_Requests * 100.0; |
377 | m_log.InfoFormat("[ASSET CACHE]: Memory Hit Rate {0}% for {1} requests", m_HitRateMemory.ToString("0.00"), m_Requests); | 386 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Memory Hit Rate {0}% for {1} requests", m_HitRateMemory.ToString("0.00"), m_Requests); |
378 | } | 387 | } |
379 | 388 | ||
380 | m_log.InfoFormat("[ASSET CACHE]: {0} unnessesary requests due to requests for assets that are currently downloading.", m_RequestsForInprogress); | 389 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} unnessesary requests due to requests for assets that are currently downloading.", m_RequestsForInprogress); |
381 | 390 | ||
382 | } | 391 | } |
383 | 392 | ||
@@ -387,7 +396,7 @@ namespace Flotsam.RegionModules.AssetCache | |||
387 | public void Expire(string id) | 396 | public void Expire(string id) |
388 | { | 397 | { |
389 | if (m_LogLevel >= 2) | 398 | if (m_LogLevel >= 2) |
390 | m_log.DebugFormat("[ASSET CACHE]: Expiring Asset {0}.", id); | 399 | m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Expiring Asset {0}.", id); |
391 | 400 | ||
392 | try | 401 | try |
393 | { | 402 | { |
@@ -409,7 +418,7 @@ namespace Flotsam.RegionModules.AssetCache | |||
409 | public void Clear() | 418 | public void Clear() |
410 | { | 419 | { |
411 | if (m_LogLevel >= 2) | 420 | if (m_LogLevel >= 2) |
412 | m_log.Debug("[ASSET CACHE]: Clearing Cache."); | 421 | m_log.Debug("[FLOTSAM ASSET CACHE]: Clearing Cache."); |
413 | 422 | ||
414 | foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) | 423 | foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) |
415 | { | 424 | { |
@@ -423,29 +432,42 @@ namespace Flotsam.RegionModules.AssetCache | |||
423 | private void CleanupExpiredFiles(object source, ElapsedEventArgs e) | 432 | private void CleanupExpiredFiles(object source, ElapsedEventArgs e) |
424 | { | 433 | { |
425 | if (m_LogLevel >= 2) | 434 | if (m_LogLevel >= 2) |
426 | m_log.DebugFormat("[ASSET CACHE]: Checking for expired files older then {0}.", m_FileExpiration.ToString()); | 435 | m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Checking for expired files older then {0}.", m_FileExpiration.ToString()); |
427 | 436 | ||
428 | foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) | 437 | foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) |
429 | { | 438 | { |
430 | foreach (string file in Directory.GetFiles(dir)) | 439 | CleanExpiredFiles(dir); |
431 | { | 440 | } |
432 | if (DateTime.Now - File.GetLastAccessTime(file) > m_FileExpiration) | 441 | } |
433 | { | ||
434 | File.Delete(file); | ||
435 | } | ||
436 | } | ||
437 | 442 | ||
438 | int dirSize = Directory.GetFiles(dir).Length; | 443 | /// <summary> |
439 | if (dirSize == 0) | 444 | /// Recurses through specified directory checking for expired asset files and deletes them. Also removes empty directories. |
440 | { | 445 | /// </summary> |
441 | Directory.Delete(dir); | 446 | /// <param name="dir"></param> |
442 | } | 447 | private void CleanExpiredFiles(string dir) |
443 | else if (dirSize >= m_CacheWarnAt) | 448 | { |
449 | foreach (string file in Directory.GetFiles(dir)) | ||
450 | { | ||
451 | if (DateTime.Now - File.GetLastAccessTime(file) > m_FileExpiration) | ||
444 | { | 452 | { |
445 | m_log.WarnFormat("[ASSET CACHE]: Cache folder exceeded CacheWarnAt limit {0} {1}. Suggest increasing tiers, tier length, or reducing cache expiration", dir, dirSize); | 453 | File.Delete(file); |
446 | } | 454 | } |
447 | |||
448 | } | 455 | } |
456 | |||
457 | foreach (string subdir in Directory.GetDirectories(dir)) | ||
458 | { | ||
459 | CleanExpiredFiles(subdir); | ||
460 | } | ||
461 | |||
462 | int dirSize = Directory.GetFiles(dir).Length + Directory.GetDirectories(dir).Length; | ||
463 | if (dirSize == 0) | ||
464 | { | ||
465 | Directory.Delete(dir); | ||
466 | } | ||
467 | else if (dirSize >= m_CacheWarnAt) | ||
468 | { | ||
469 | m_log.WarnFormat("[FLOTSAM ASSET CACHE]: Cache folder exceeded CacheWarnAt limit {0} {1}. Suggest increasing tiers, tier length, or reducing cache expiration", dir, dirSize); | ||
470 | } | ||
449 | } | 471 | } |
450 | 472 | ||
451 | private string GetFileName(string id) | 473 | private string GetFileName(string id) |
@@ -489,7 +511,7 @@ namespace Flotsam.RegionModules.AssetCache | |||
489 | File.Move(tempname, filename); | 511 | File.Move(tempname, filename); |
490 | 512 | ||
491 | if (m_LogLevel >= 2) | 513 | if (m_LogLevel >= 2) |
492 | m_log.DebugFormat("[ASSET CACHE]: Cache Stored :: {0}", asset.ID); | 514 | m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Cache Stored :: {0}", asset.ID); |
493 | } | 515 | } |
494 | catch (Exception e) | 516 | catch (Exception e) |
495 | { | 517 | { |
@@ -525,8 +547,88 @@ namespace Flotsam.RegionModules.AssetCache | |||
525 | string[] text = e.ToString().Split(new char[] { '\n' }); | 547 | string[] text = e.ToString().Split(new char[] { '\n' }); |
526 | foreach (string t in text) | 548 | foreach (string t in text) |
527 | { | 549 | { |
528 | m_log.ErrorFormat("[ASSET CACHE]: {0} ", t); | 550 | m_log.ErrorFormat("[FLOTSAM ASSET CACHE]: {0} ", t); |
551 | } | ||
552 | } | ||
553 | |||
554 | private int GetFileCacheCount(string dir) | ||
555 | { | ||
556 | int count = Directory.GetFiles(dir).Length; | ||
557 | |||
558 | foreach (string subdir in Directory.GetDirectories(dir)) | ||
559 | { | ||
560 | count += GetFileCacheCount(subdir); | ||
529 | } | 561 | } |
562 | |||
563 | return count; | ||
530 | } | 564 | } |
565 | |||
566 | #region Console Commands | ||
567 | private void HandleConsoleCommand(string module, string[] cmdparams) | ||
568 | { | ||
569 | if (cmdparams.Length == 2) | ||
570 | { | ||
571 | string cmd = cmdparams[1]; | ||
572 | switch (cmd) | ||
573 | { | ||
574 | case "count": | ||
575 | case "counts": | ||
576 | m_log.InfoFormat("[FLOTSAM ASSET CACHE] Memory Cache : {0}", m_MemoryCache.Count); | ||
577 | |||
578 | int fileCount = GetFileCacheCount(m_CacheDirectory); | ||
579 | m_log.InfoFormat("[FLOTSAM ASSET CACHE] File Cache : {0}", fileCount); | ||
580 | |||
581 | break; | ||
582 | |||
583 | case "clearmem": | ||
584 | m_MemoryCache.Clear(); | ||
585 | m_log.InfoFormat("[FLOTSAM ASSET CACHE] Memory Cache Cleared, there are now {0} items in the memory cache", m_MemoryCache.Count); | ||
586 | break; | ||
587 | |||
588 | case "clearfile": | ||
589 | foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) | ||
590 | { | ||
591 | try | ||
592 | { | ||
593 | Directory.Delete(dir, true); | ||
594 | } | ||
595 | catch (Exception e) | ||
596 | { | ||
597 | LogException(e); | ||
598 | } | ||
599 | } | ||
600 | |||
601 | foreach (string file in Directory.GetFiles(m_CacheDirectory)) | ||
602 | { | ||
603 | try | ||
604 | { | ||
605 | File.Delete(file); | ||
606 | } | ||
607 | catch (Exception e) | ||
608 | { | ||
609 | LogException(e); | ||
610 | } | ||
611 | } | ||
612 | |||
613 | break; | ||
614 | |||
615 | default: | ||
616 | m_log.InfoFormat("[FLOTSAM ASSET CACHE] Unknown command {0}", cmd); | ||
617 | break; | ||
618 | } | ||
619 | } | ||
620 | else if (cmdparams.Length == 1) | ||
621 | { | ||
622 | m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache counts - Display the number of cached assets"); | ||
623 | m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache clearmem - Remove all assets cached in memory"); | ||
624 | m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache clearfile - Remove all assets cached on disk"); | ||
625 | |||
626 | } | ||
627 | |||
628 | |||
629 | } | ||
630 | |||
631 | #endregion | ||
632 | |||
531 | } | 633 | } |
532 | } | 634 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs index ff583e5..2a1c82e 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs | |||
@@ -177,6 +177,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
177 | UUID ospResolvedId = OspResolver.ResolveOspa(item.CreatorId, m_scene.CommsManager); | 177 | UUID ospResolvedId = OspResolver.ResolveOspa(item.CreatorId, m_scene.CommsManager); |
178 | if (UUID.Zero != ospResolvedId) | 178 | if (UUID.Zero != ospResolvedId) |
179 | item.CreatorIdAsUuid = ospResolvedId; | 179 | item.CreatorIdAsUuid = ospResolvedId; |
180 | else | ||
181 | item.CreatorIdAsUuid = m_userInfo.UserProfile.ID; | ||
180 | 182 | ||
181 | item.Owner = m_userInfo.UserProfile.ID; | 183 | item.Owner = m_userInfo.UserProfile.ID; |
182 | 184 | ||
@@ -206,7 +208,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
206 | /// <summary> | 208 | /// <summary> |
207 | /// Replicate the inventory paths in the archive to the user's inventory as necessary. | 209 | /// Replicate the inventory paths in the archive to the user's inventory as necessary. |
208 | /// </summary> | 210 | /// </summary> |
209 | /// <param name="fsPath"></param> | 211 | /// <param name="archivePath">The item archive path to replicate</param> |
210 | /// <param name="isDir">Is the path we're dealing with a directory?</param> | 212 | /// <param name="isDir">Is the path we're dealing with a directory?</param> |
211 | /// <param name="rootDestinationFolder">The root folder for the inventory load</param> | 213 | /// <param name="rootDestinationFolder">The root folder for the inventory load</param> |
212 | /// <param name="foldersCreated"> | 214 | /// <param name="foldersCreated"> |
@@ -218,49 +220,51 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
218 | /// </param> | 220 | /// </param> |
219 | /// <returns>The last user inventory folder created or found for the archive path</returns> | 221 | /// <returns>The last user inventory folder created or found for the archive path</returns> |
220 | public InventoryFolderBase ReplicateArchivePathToUserInventory( | 222 | public InventoryFolderBase ReplicateArchivePathToUserInventory( |
221 | string fsPath, | 223 | string archivePath, |
222 | bool isDir, | 224 | bool isDir, |
223 | InventoryFolderBase rootDestFolder, | 225 | InventoryFolderBase rootDestFolder, |
224 | Dictionary <string, InventoryFolderBase> foldersCreated, | 226 | Dictionary <string, InventoryFolderBase> foldersCreated, |
225 | List<InventoryNodeBase> nodesLoaded) | 227 | List<InventoryNodeBase> nodesLoaded) |
226 | { | 228 | { |
227 | fsPath = fsPath.Substring(ArchiveConstants.INVENTORY_PATH.Length); | 229 | archivePath = archivePath.Substring(ArchiveConstants.INVENTORY_PATH.Length); |
228 | 230 | ||
229 | // Remove the file portion if we aren't already dealing with a directory path | 231 | // Remove the file portion if we aren't already dealing with a directory path |
230 | if (!isDir) | 232 | if (!isDir) |
231 | fsPath = fsPath.Remove(fsPath.LastIndexOf("/") + 1); | 233 | archivePath = archivePath.Remove(archivePath.LastIndexOf("/") + 1); |
232 | 234 | ||
233 | string originalFsPath = fsPath; | 235 | string originalArchivePath = archivePath; |
234 | 236 | ||
235 | m_log.DebugFormat("[INVENTORY ARCHIVER]: Loading to folder {0}", fsPath); | 237 | m_log.DebugFormat( |
238 | "[INVENTORY ARCHIVER]: Loading to folder {0} {1}", rootDestFolder.Name, rootDestFolder.ID); | ||
236 | 239 | ||
237 | InventoryFolderBase destFolder = null; | 240 | InventoryFolderBase destFolder = null; |
238 | 241 | ||
239 | // XXX: Nasty way of dealing with a path that has no directory component | 242 | // XXX: Nasty way of dealing with a path that has no directory component |
240 | if (fsPath.Length > 0) | 243 | if (archivePath.Length > 0) |
241 | { | 244 | { |
242 | while (null == destFolder && fsPath.Length > 0) | 245 | while (null == destFolder && archivePath.Length > 0) |
243 | { | 246 | { |
244 | if (foldersCreated.ContainsKey(fsPath)) | 247 | if (foldersCreated.ContainsKey(archivePath)) |
245 | { | 248 | { |
246 | m_log.DebugFormat("[INVENTORY ARCHIVER]: Found previously created fs path {0}", fsPath); | 249 | m_log.DebugFormat( |
247 | destFolder = foldersCreated[fsPath]; | 250 | "[INVENTORY ARCHIVER]: Found previously created folder from archive path {0}", archivePath); |
251 | destFolder = foldersCreated[archivePath]; | ||
248 | } | 252 | } |
249 | else | 253 | else |
250 | { | 254 | { |
251 | // Don't include the last slash | 255 | // Don't include the last slash |
252 | int penultimateSlashIndex = fsPath.LastIndexOf("/", fsPath.Length - 2); | 256 | int penultimateSlashIndex = archivePath.LastIndexOf("/", archivePath.Length - 2); |
253 | 257 | ||
254 | if (penultimateSlashIndex >= 0) | 258 | if (penultimateSlashIndex >= 0) |
255 | { | 259 | { |
256 | fsPath = fsPath.Remove(penultimateSlashIndex + 1); | 260 | archivePath = archivePath.Remove(penultimateSlashIndex + 1); |
257 | } | 261 | } |
258 | else | 262 | else |
259 | { | 263 | { |
260 | m_log.DebugFormat( | 264 | m_log.DebugFormat( |
261 | "[INVENTORY ARCHIVER]: Found no previously created fs path for {0}", | 265 | "[INVENTORY ARCHIVER]: Found no previously created folder for archive path {0}", |
262 | originalFsPath); | 266 | originalArchivePath); |
263 | fsPath = string.Empty; | 267 | archivePath = string.Empty; |
264 | destFolder = rootDestFolder; | 268 | destFolder = rootDestFolder; |
265 | } | 269 | } |
266 | } | 270 | } |
@@ -271,14 +275,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
271 | destFolder = rootDestFolder; | 275 | destFolder = rootDestFolder; |
272 | } | 276 | } |
273 | 277 | ||
274 | string fsPathSectionToCreate = originalFsPath.Substring(fsPath.Length); | 278 | string archivePathSectionToCreate = originalArchivePath.Substring(archivePath.Length); |
275 | string[] rawDirsToCreate | 279 | string[] rawDirsToCreate |
276 | = fsPathSectionToCreate.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); | 280 | = archivePathSectionToCreate.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); |
277 | int i = 0; | 281 | int i = 0; |
278 | 282 | ||
279 | while (i < rawDirsToCreate.Length) | 283 | while (i < rawDirsToCreate.Length) |
280 | { | 284 | { |
281 | m_log.DebugFormat("[INVENTORY ARCHIVER]: Creating folder {0}", rawDirsToCreate[i]); | 285 | m_log.DebugFormat("[INVENTORY ARCHIVER]: Loading archived folder {0}", rawDirsToCreate[i]); |
282 | 286 | ||
283 | int identicalNameIdentifierIndex | 287 | int identicalNameIdentifierIndex |
284 | = rawDirsToCreate[i].LastIndexOf( | 288 | = rawDirsToCreate[i].LastIndexOf( |
@@ -305,9 +309,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
305 | // foundFolder.Name, foundFolder.ID); | 309 | // foundFolder.Name, foundFolder.ID); |
306 | 310 | ||
307 | // Record that we have now created this folder | 311 | // Record that we have now created this folder |
308 | fsPath += rawDirsToCreate[i] + "/"; | 312 | archivePath += rawDirsToCreate[i] + "/"; |
309 | m_log.DebugFormat("[INVENTORY ARCHIVER]: Recording creation of fs path {0}", fsPath); | 313 | m_log.DebugFormat("[INVENTORY ARCHIVER]: Loaded archive path {0}", archivePath); |
310 | foldersCreated[fsPath] = destFolder; | 314 | foldersCreated[archivePath] = destFolder; |
311 | 315 | ||
312 | if (0 == i) | 316 | if (0 == i) |
313 | nodesLoaded.Add(destFolder); | 317 | nodesLoaded.Add(destFolder); |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveUtils.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveUtils.cs index 2eeb637..5ebf2fa 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveUtils.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveUtils.cs | |||
@@ -106,8 +106,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
106 | if (path == PATH_DELIMITER) | 106 | if (path == PATH_DELIMITER) |
107 | return startFolder; | 107 | return startFolder; |
108 | 108 | ||
109 | InventoryFolderBase foundFolder = null; | ||
110 | |||
111 | string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None); | 109 | string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None); |
112 | InventoryCollection contents = inventoryService.GetFolderContent(startFolder.Owner, startFolder.ID); | 110 | InventoryCollection contents = inventoryService.GetFolderContent(startFolder.Owner, startFolder.ID); |
113 | 111 | ||
@@ -116,7 +114,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
116 | if (folder.Name == components[0]) | 114 | if (folder.Name == components[0]) |
117 | { | 115 | { |
118 | if (components.Length > 1) | 116 | if (components.Length > 1) |
119 | return FindFolderByPath(inventoryService, foundFolder, components[1]); | 117 | return FindFolderByPath(inventoryService, folder, components[1]); |
120 | else | 118 | else |
121 | return folder; | 119 | return folder; |
122 | } | 120 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs index dee4a5d..b178772 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs | |||
@@ -138,7 +138,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
138 | 138 | ||
139 | protected void SaveInvItem(InventoryItemBase inventoryItem, string path) | 139 | protected void SaveInvItem(InventoryItemBase inventoryItem, string path) |
140 | { | 140 | { |
141 | string filename = string.Format("{0}{1}_{2}.xml", path, inventoryItem.Name, inventoryItem.ID); | 141 | string filename = path + CreateArchiveItemName(inventoryItem); |
142 | 142 | ||
143 | // Record the creator of this item for user record purposes (which might go away soon) | 143 | // Record the creator of this item for user record purposes (which might go away soon) |
144 | m_userUuids[inventoryItem.CreatorIdAsUuid] = 1; | 144 | m_userUuids[inventoryItem.CreatorIdAsUuid] = 1; |
@@ -162,12 +162,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
162 | { | 162 | { |
163 | if (saveThisFolderItself) | 163 | if (saveThisFolderItself) |
164 | { | 164 | { |
165 | path += | 165 | path += CreateArchiveFolderName(inventoryFolder); |
166 | string.Format( | ||
167 | "{0}{1}{2}/", | ||
168 | inventoryFolder.Name, | ||
169 | ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR, | ||
170 | inventoryFolder.ID); | ||
171 | 166 | ||
172 | // We need to make sure that we record empty folders | 167 | // We need to make sure that we record empty folders |
173 | m_archiveWriter.WriteDir(path); | 168 | m_archiveWriter.WriteDir(path); |
@@ -356,5 +351,63 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
356 | } | 351 | } |
357 | } | 352 | } |
358 | } | 353 | } |
354 | |||
355 | /// <summary> | ||
356 | /// Create the archive name for a particular folder. | ||
357 | /// </summary> | ||
358 | /// | ||
359 | /// These names are prepended with an inventory folder's UUID so that more than one folder can have the | ||
360 | /// same name | ||
361 | /// | ||
362 | /// <param name="folder"></param> | ||
363 | /// <returns></returns> | ||
364 | public static string CreateArchiveFolderName(InventoryFolderBase folder) | ||
365 | { | ||
366 | return CreateArchiveFolderName(folder.Name, folder.ID); | ||
367 | } | ||
368 | |||
369 | /// <summary> | ||
370 | /// Create the archive name for a particular item. | ||
371 | /// </summary> | ||
372 | /// | ||
373 | /// These names are prepended with an inventory item's UUID so that more than one item can have the | ||
374 | /// same name | ||
375 | /// | ||
376 | /// <param name="item"></param> | ||
377 | /// <returns></returns> | ||
378 | public static string CreateArchiveItemName(InventoryItemBase item) | ||
379 | { | ||
380 | return CreateArchiveItemName(item.Name, item.ID); | ||
381 | } | ||
382 | |||
383 | /// <summary> | ||
384 | /// Create an archive folder name given its constituent components | ||
385 | /// </summary> | ||
386 | /// <param name="name"></param> | ||
387 | /// <param name="id"></param> | ||
388 | /// <returns></returns> | ||
389 | public static string CreateArchiveFolderName(string name, UUID id) | ||
390 | { | ||
391 | return string.Format( | ||
392 | "{0}{1}{2}/", | ||
393 | name, | ||
394 | ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR, | ||
395 | id); | ||
396 | } | ||
397 | |||
398 | /// <summary> | ||
399 | /// Create an archive item name given its constituent components | ||
400 | /// </summary> | ||
401 | /// <param name="name"></param> | ||
402 | /// <param name="id"></param> | ||
403 | /// <returns></returns> | ||
404 | public static string CreateArchiveItemName(string name, UUID id) | ||
405 | { | ||
406 | return string.Format( | ||
407 | "{0}{1}{2}.xml", | ||
408 | name, | ||
409 | ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR, | ||
410 | id); | ||
411 | } | ||
359 | } | 412 | } |
360 | } | 413 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs index d579a81..9c5f8f3 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs | |||
@@ -81,7 +81,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
81 | public void TestSaveIarV0_1() | 81 | public void TestSaveIarV0_1() |
82 | { | 82 | { |
83 | TestHelper.InMethod(); | 83 | TestHelper.InMethod(); |
84 | log4net.Config.XmlConfigurator.Configure(); | 84 | //log4net.Config.XmlConfigurator.Configure(); |
85 | 85 | ||
86 | InventoryArchiverModule archiverModule = new InventoryArchiverModule(true); | 86 | InventoryArchiverModule archiverModule = new InventoryArchiverModule(true); |
87 | 87 | ||
@@ -153,19 +153,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
153 | //bool gotControlFile = false; | 153 | //bool gotControlFile = false; |
154 | bool gotObject1File = false; | 154 | bool gotObject1File = false; |
155 | //bool gotObject2File = false; | 155 | //bool gotObject2File = false; |
156 | string expectedObject1FileName = InventoryArchiveWriteRequest.CreateArchiveItemName(item1); | ||
156 | string expectedObject1FilePath = string.Format( | 157 | string expectedObject1FilePath = string.Format( |
157 | "{0}{1}/{2}_{3}.xml", | 158 | "{0}{1}{2}", |
158 | ArchiveConstants.INVENTORY_PATH, | 159 | ArchiveConstants.INVENTORY_PATH, |
159 | string.Format( | 160 | InventoryArchiveWriteRequest.CreateArchiveFolderName(objsFolder), |
160 | "Objects{0}{1}", ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR, objsFolder.ID), | 161 | expectedObject1FileName); |
161 | item1.Name, | ||
162 | item1Id); | ||
163 | |||
164 | // string expectedObject2FileName = string.Format( | ||
165 | // "{0}_{1:000}-{2:000}-{3:000}__{4}.xml", | ||
166 | // part2.Name, | ||
167 | // Math.Round(part2.GroupPosition.X), Math.Round(part2.GroupPosition.Y), Math.Round(part2.GroupPosition.Z), | ||
168 | // part2.UUID); | ||
169 | 162 | ||
170 | string filePath; | 163 | string filePath; |
171 | TarArchiveReader.TarEntryType tarEntryType; | 164 | TarArchiveReader.TarEntryType tarEntryType; |
@@ -187,7 +180,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
187 | // | 180 | // |
188 | // if (fileName.StartsWith(part1.Name)) | 181 | // if (fileName.StartsWith(part1.Name)) |
189 | // { | 182 | // { |
190 | Assert.That(filePath, Is.EqualTo(expectedObject1FilePath)); | 183 | Assert.That(expectedObject1FilePath, Is.EqualTo(filePath)); |
191 | gotObject1File = true; | 184 | gotObject1File = true; |
192 | // } | 185 | // } |
193 | // else if (fileName.StartsWith(part2.Name)) | 186 | // else if (fileName.StartsWith(part2.Name)) |
@@ -202,19 +195,21 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
202 | Assert.That(gotObject1File, Is.True, "No item1 file in archive"); | 195 | Assert.That(gotObject1File, Is.True, "No item1 file in archive"); |
203 | // Assert.That(gotObject2File, Is.True, "No object2 file in archive"); | 196 | // Assert.That(gotObject2File, Is.True, "No object2 file in archive"); |
204 | 197 | ||
205 | // TODO: Test presence of more files and contents of files. | 198 | // TODO: Test presence of more files and contents of files. |
206 | } | 199 | } |
207 | 200 | ||
208 | /// <summary> | 201 | /// <summary> |
209 | /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where | 202 | /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where |
210 | /// an account exists with the creator name. | 203 | /// an account exists with the creator name. |
211 | /// </summary> | 204 | /// </summary> |
205 | /// | ||
206 | /// This test also does some deeper probing of loading into nested inventory structures | ||
212 | [Test] | 207 | [Test] |
213 | public void TestLoadIarV0_1ExistingUsers() | 208 | public void TestLoadIarV0_1ExistingUsers() |
214 | { | 209 | { |
215 | TestHelper.InMethod(); | 210 | TestHelper.InMethod(); |
216 | 211 | ||
217 | log4net.Config.XmlConfigurator.Configure(); | 212 | //log4net.Config.XmlConfigurator.Configure(); |
218 | 213 | ||
219 | string userFirstName = "Mr"; | 214 | string userFirstName = "Mr"; |
220 | string userLastName = "Tiddles"; | 215 | string userLastName = "Tiddles"; |
@@ -264,24 +259,114 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
264 | CachedUserInfo userInfo | 259 | CachedUserInfo userInfo |
265 | = scene.CommsManager.UserProfileCacheService.GetUserDetails(userFirstName, userLastName); | 260 | = scene.CommsManager.UserProfileCacheService.GetUserDetails(userFirstName, userLastName); |
266 | 261 | ||
267 | InventoryItemBase foundItem | 262 | InventoryItemBase foundItem1 |
268 | = InventoryArchiveUtils.FindItemByPath(scene.InventoryService, userInfo.UserProfile.ID, itemName); | 263 | = InventoryArchiveUtils.FindItemByPath(scene.InventoryService, userInfo.UserProfile.ID, itemName); |
269 | 264 | ||
270 | Assert.That(foundItem, Is.Not.Null, "Didn't find loaded item"); | 265 | Assert.That(foundItem1, Is.Not.Null, "Didn't find loaded item 1"); |
271 | Assert.That( | 266 | Assert.That( |
272 | foundItem.CreatorId, Is.EqualTo(item1.CreatorId), | 267 | foundItem1.CreatorId, Is.EqualTo(item1.CreatorId), |
273 | "Loaded item non-uuid creator doesn't match original"); | 268 | "Loaded item non-uuid creator doesn't match original"); |
274 | Assert.That( | 269 | Assert.That( |
275 | foundItem.CreatorIdAsUuid, Is.EqualTo(userItemCreatorUuid), | 270 | foundItem1.CreatorIdAsUuid, Is.EqualTo(userItemCreatorUuid), |
276 | "Loaded item uuid creator doesn't match original"); | 271 | "Loaded item uuid creator doesn't match original"); |
277 | Assert.That(foundItem.Owner, Is.EqualTo(userUuid), | 272 | Assert.That(foundItem1.Owner, Is.EqualTo(userUuid), |
278 | "Loaded item owner doesn't match inventory reciever"); | 273 | "Loaded item owner doesn't match inventory reciever"); |
274 | |||
275 | // Now try loading to a root child folder | ||
276 | UserInventoryTestUtils.CreateInventoryFolder(scene.InventoryService, userInfo.UserProfile.ID, "xA"); | ||
277 | archiveReadStream = new MemoryStream(archiveReadStream.ToArray()); | ||
278 | archiverModule.DearchiveInventory(userFirstName, userLastName, "xA", archiveReadStream); | ||
279 | |||
280 | InventoryItemBase foundItem2 | ||
281 | = InventoryArchiveUtils.FindItemByPath(scene.InventoryService, userInfo.UserProfile.ID, "xA/" + itemName); | ||
282 | Assert.That(foundItem2, Is.Not.Null, "Didn't find loaded item 2"); | ||
283 | |||
284 | // Now try loading to a more deeply nested folder | ||
285 | UserInventoryTestUtils.CreateInventoryFolder(scene.InventoryService, userInfo.UserProfile.ID, "xB/xC"); | ||
286 | archiveReadStream = new MemoryStream(archiveReadStream.ToArray()); | ||
287 | archiverModule.DearchiveInventory(userFirstName, userLastName, "xB/xC", archiveReadStream); | ||
288 | |||
289 | InventoryItemBase foundItem3 | ||
290 | = InventoryArchiveUtils.FindItemByPath(scene.InventoryService, userInfo.UserProfile.ID, "xB/xC/" + itemName); | ||
291 | Assert.That(foundItem3, Is.Not.Null, "Didn't find loaded item 3"); | ||
279 | } | 292 | } |
280 | 293 | ||
281 | /// <summary> | 294 | /// <summary> |
282 | /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where | 295 | /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where |
296 | /// embedded creators do not exist in the system | ||
297 | /// </summary> | ||
298 | /// | ||
299 | /// This may possibly one day get overtaken by the as yet incomplete temporary profiles feature | ||
300 | /// (as tested in the a later commented out test) | ||
301 | [Test] | ||
302 | public void TestLoadIarV0_1AbsentUsers() | ||
303 | { | ||
304 | TestHelper.InMethod(); | ||
305 | |||
306 | log4net.Config.XmlConfigurator.Configure(); | ||
307 | |||
308 | string userFirstName = "Charlie"; | ||
309 | string userLastName = "Chan"; | ||
310 | UUID userUuid = UUID.Parse("00000000-0000-0000-0000-000000000999"); | ||
311 | string userItemCreatorFirstName = "Bat"; | ||
312 | string userItemCreatorLastName = "Man"; | ||
313 | //UUID userItemCreatorUuid = UUID.Parse("00000000-0000-0000-0000-000000008888"); | ||
314 | |||
315 | string itemName = "b.lsl"; | ||
316 | string archiveItemName | ||
317 | = string.Format("{0}{1}{2}", itemName, "_", UUID.Random()); | ||
318 | |||
319 | MemoryStream archiveWriteStream = new MemoryStream(); | ||
320 | TarArchiveWriter tar = new TarArchiveWriter(archiveWriteStream); | ||
321 | |||
322 | InventoryItemBase item1 = new InventoryItemBase(); | ||
323 | item1.Name = itemName; | ||
324 | item1.AssetID = UUID.Random(); | ||
325 | item1.GroupID = UUID.Random(); | ||
326 | item1.CreatorId = OspResolver.MakeOspa(userItemCreatorFirstName, userItemCreatorLastName); | ||
327 | //item1.CreatorId = userUuid.ToString(); | ||
328 | //item1.CreatorId = "00000000-0000-0000-0000-000000000444"; | ||
329 | item1.Owner = UUID.Zero; | ||
330 | |||
331 | string item1FileName | ||
332 | = string.Format("{0}{1}", ArchiveConstants.INVENTORY_PATH, archiveItemName); | ||
333 | tar.WriteFile(item1FileName, UserInventoryItemSerializer.Serialize(item1)); | ||
334 | tar.Close(); | ||
335 | |||
336 | MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray()); | ||
337 | SerialiserModule serialiserModule = new SerialiserModule(); | ||
338 | InventoryArchiverModule archiverModule = new InventoryArchiverModule(true); | ||
339 | |||
340 | // Annoyingly, we have to set up a scene even though inventory loading has nothing to do with a scene | ||
341 | Scene scene = SceneSetupHelpers.SetupScene("inventory"); | ||
342 | IUserAdminService userAdminService = scene.CommsManager.UserAdminService; | ||
343 | |||
344 | SceneSetupHelpers.SetupSceneModules(scene, serialiserModule, archiverModule); | ||
345 | userAdminService.AddUser( | ||
346 | userFirstName, userLastName, "meowfood", String.Empty, 1000, 1000, userUuid); | ||
347 | |||
348 | archiverModule.DearchiveInventory(userFirstName, userLastName, "/", archiveReadStream); | ||
349 | |||
350 | CachedUserInfo userInfo | ||
351 | = scene.CommsManager.UserProfileCacheService.GetUserDetails(userFirstName, userLastName); | ||
352 | |||
353 | InventoryItemBase foundItem1 | ||
354 | = InventoryArchiveUtils.FindItemByPath(scene.InventoryService, userInfo.UserProfile.ID, itemName); | ||
355 | |||
356 | Assert.That(foundItem1, Is.Not.Null, "Didn't find loaded item 1"); | ||
357 | // Assert.That( | ||
358 | // foundItem1.CreatorId, Is.EqualTo(userUuid), | ||
359 | // "Loaded item non-uuid creator doesn't match that of the loading user"); | ||
360 | Assert.That( | ||
361 | foundItem1.CreatorIdAsUuid, Is.EqualTo(userUuid), | ||
362 | "Loaded item uuid creator doesn't match that of the loading user"); | ||
363 | } | ||
364 | |||
365 | /// <summary> | ||
366 | /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where | ||
283 | /// no account exists with the creator name | 367 | /// no account exists with the creator name |
284 | /// </summary> | 368 | /// </summary> |
369 | /// Disabled since temporary profiles have not yet been implemented. | ||
285 | //[Test] | 370 | //[Test] |
286 | public void TestLoadIarV0_1TempProfiles() | 371 | public void TestLoadIarV0_1TempProfiles() |
287 | { | 372 | { |
@@ -364,7 +449,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
364 | { | 449 | { |
365 | TestHelper.InMethod(); | 450 | TestHelper.InMethod(); |
366 | 451 | ||
367 | log4net.Config.XmlConfigurator.Configure(); | 452 | //log4net.Config.XmlConfigurator.Configure(); |
368 | 453 | ||
369 | Scene scene = SceneSetupHelpers.SetupScene("inventory"); | 454 | Scene scene = SceneSetupHelpers.SetupScene("inventory"); |
370 | CommunicationsManager commsManager = scene.CommsManager; | 455 | CommunicationsManager commsManager = scene.CommsManager; |
@@ -385,16 +470,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
385 | string folder2Name = "b"; | 470 | string folder2Name = "b"; |
386 | string itemName = "c.lsl"; | 471 | string itemName = "c.lsl"; |
387 | 472 | ||
388 | string folder1ArchiveName | 473 | string folder1ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder1Name, UUID.Random()); |
389 | = string.Format( | 474 | string folder2ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder2Name, UUID.Random()); |
390 | "{0}{1}{2}", folder1Name, ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR, UUID.Random()); | 475 | string itemArchiveName = InventoryArchiveWriteRequest.CreateArchiveItemName(itemName, UUID.Random()); |
391 | string folder2ArchiveName | 476 | |
392 | = string.Format( | ||
393 | "{0}{1}{2}", folder2Name, ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR, UUID.Random()); | ||
394 | string itemArchivePath | 477 | string itemArchivePath |
395 | = string.Format( | 478 | = string.Format( |
396 | "{0}{1}/{2}/{3}", | 479 | "{0}{1}{2}{3}", |
397 | ArchiveConstants.INVENTORY_PATH, folder1ArchiveName, folder2ArchiveName, itemName); | 480 | ArchiveConstants.INVENTORY_PATH, folder1ArchiveName, folder2ArchiveName, itemArchiveName); |
398 | 481 | ||
399 | //Console.WriteLine("userInfo.RootFolder 2: {0}", userInfo.RootFolder); | 482 | //Console.WriteLine("userInfo.RootFolder 2: {0}", userInfo.RootFolder); |
400 | 483 | ||
diff --git a/OpenSim/Region/CoreModules/World/Land/RegionCombinerModule.cs b/OpenSim/Region/CoreModules/World/Land/RegionCombinerModule.cs index 9da869c..f513d68 100644 --- a/OpenSim/Region/CoreModules/World/Land/RegionCombinerModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/RegionCombinerModule.cs | |||
@@ -588,10 +588,9 @@ namespace OpenSim.Region.CoreModules.World.Land | |||
588 | RegionConnections connectiondata, ScenePresence rootPresence) | 588 | RegionConnections connectiondata, ScenePresence rootPresence) |
589 | { | 589 | { |
590 | RegionData[] rdata = connectiondata.ConnectedRegions.ToArray(); | 590 | RegionData[] rdata = connectiondata.ConnectedRegions.ToArray(); |
591 | List<IClientAPI> clients = new List<IClientAPI>(); | 591 | //List<IClientAPI> clients = new List<IClientAPI>(); |
592 | Dictionary<Vector2, RegionCourseLocationStruct> updates = new Dictionary<Vector2, RegionCourseLocationStruct>(); | 592 | Dictionary<Vector2, RegionCourseLocationStruct> updates = new Dictionary<Vector2, RegionCourseLocationStruct>(); |
593 | 593 | ||
594 | |||
595 | // Root Region entry | 594 | // Root Region entry |
596 | RegionCourseLocationStruct rootupdatedata = new RegionCourseLocationStruct(); | 595 | RegionCourseLocationStruct rootupdatedata = new RegionCourseLocationStruct(); |
597 | rootupdatedata.Locations = new List<Vector3>(); | 596 | rootupdatedata.Locations = new List<Vector3>(); |