diff options
author | Justin Clark-Casey (justincc) | 2014-11-05 19:36:53 +0000 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2014-11-25 23:23:11 +0000 |
commit | 06a5d6e9ef163abaadbf81c98b54c042751fae89 (patch) | |
tree | 535d0f508e2844326cacefb3f40db670fd11f5b7 /OpenSim | |
parent | Add incoming packet async handling engine to queue some inbound udp async req... (diff) | |
download | opensim-SC-06a5d6e9ef163abaadbf81c98b54c042751fae89.zip opensim-SC-06a5d6e9ef163abaadbf81c98b54c042751fae89.tar.gz opensim-SC-06a5d6e9ef163abaadbf81c98b54c042751fae89.tar.bz2 opensim-SC-06a5d6e9ef163abaadbf81c98b54c042751fae89.tar.xz |
Introduce an IteratingUuidGatherer where each fetch from the asset service (iteration) can be controlled by the caller.
This is to enable an imminent change where incoming HG scene object fetching can assess the time taken by each request rather than being forced to perform all requests in one call.
Soon, this will replace the existing UuidGatherer since it is both simpler and more flexible.
Diffstat (limited to 'OpenSim')
-rw-r--r-- | OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs | 19 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/UuidGatherer.cs | 607 |
2 files changed, 579 insertions, 47 deletions
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs index 522de79..be409bb 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs | |||
@@ -571,9 +571,22 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
571 | "[HG ENTITY TRANSFER MODULE]: Incoming attachment {0} for HG user {1} with asset server {2}", | 571 | "[HG ENTITY TRANSFER MODULE]: Incoming attachment {0} for HG user {1} with asset server {2}", |
572 | so.Name, so.AttachedAvatar, url); | 572 | so.Name, so.AttachedAvatar, url); |
573 | 573 | ||
574 | Dictionary<UUID, sbyte> ids = new Dictionary<UUID, sbyte>(); | 574 | IteratingHGUuidGatherer uuidGatherer = new IteratingHGUuidGatherer(Scene.AssetService, url); |
575 | HGUuidGatherer uuidGatherer = new HGUuidGatherer(Scene.AssetService, url); | 575 | uuidGatherer.RecordAssetUuids(so); |
576 | uuidGatherer.GatherAssetUuids(so, ids); | 576 | |
577 | // XXX: We will shortly use this iterating mechanism to check if a fetch is taking too long | ||
578 | // but just for now we will simply fetch everything. If this was permanent could use | ||
579 | // GatherAll() | ||
580 | while (uuidGatherer.GatherNext()) | ||
581 | m_log.DebugFormat( | ||
582 | "[HG ENTITY TRANSFER]: Gathered attachment {0} for HG user {1} with asset server {2}", | ||
583 | so.Name, so.OwnerID, url); | ||
584 | |||
585 | IDictionary<UUID, sbyte> ids = uuidGatherer.GetGatheredUuids(); | ||
586 | |||
587 | m_log.DebugFormat( | ||
588 | "[HG ENTITY TRANSFER]: Fetching {0} assets for attachment {1} for HG user {2} with asset server {3}", | ||
589 | ids.Count, so.Name, so.OwnerID, url); | ||
577 | 590 | ||
578 | foreach (KeyValuePair<UUID, sbyte> kvp in ids) | 591 | foreach (KeyValuePair<UUID, sbyte> kvp in ids) |
579 | uuidGatherer.FetchAsset(kvp.Key); | 592 | uuidGatherer.FetchAsset(kvp.Key); |
diff --git a/OpenSim/Region/Framework/Scenes/UuidGatherer.cs b/OpenSim/Region/Framework/Scenes/UuidGatherer.cs index 20ff5b5..9c4e4c0 100644 --- a/OpenSim/Region/Framework/Scenes/UuidGatherer.cs +++ b/OpenSim/Region/Framework/Scenes/UuidGatherer.cs | |||
@@ -57,17 +57,17 @@ namespace OpenSim.Region.Framework.Scenes | |||
57 | 57 | ||
58 | protected IAssetService m_assetService; | 58 | protected IAssetService m_assetService; |
59 | 59 | ||
60 | // /// <summary> | 60 | // /// <summary> |
61 | // /// Used as a temporary store of an asset which represents an object. This can be a null if no appropriate | 61 | // /// Used as a temporary store of an asset which represents an object. This can be a null if no appropriate |
62 | // /// asset was found by the asset service. | 62 | // /// asset was found by the asset service. |
63 | // /// </summary> | 63 | // /// </summary> |
64 | // private AssetBase m_requestedObjectAsset; | 64 | // private AssetBase m_requestedObjectAsset; |
65 | // | 65 | // |
66 | // /// <summary> | 66 | // /// <summary> |
67 | // /// Signal whether we are currently waiting for the asset service to deliver an asset. | 67 | // /// Signal whether we are currently waiting for the asset service to deliver an asset. |
68 | // /// </summary> | 68 | // /// </summary> |
69 | // private bool m_waitingForObjectAsset; | 69 | // private bool m_waitingForObjectAsset; |
70 | 70 | ||
71 | public UuidGatherer(IAssetService assetService) | 71 | public UuidGatherer(IAssetService assetService) |
72 | { | 72 | { |
73 | m_assetService = assetService; | 73 | m_assetService = assetService; |
@@ -133,7 +133,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
133 | throw; | 133 | throw; |
134 | } | 134 | } |
135 | } | 135 | } |
136 | 136 | ||
137 | /// <summary> | 137 | /// <summary> |
138 | /// Gather all the asset uuids associated with the asset referenced by a given uuid | 138 | /// Gather all the asset uuids associated with the asset referenced by a given uuid |
139 | /// </summary> | 139 | /// </summary> |
@@ -154,7 +154,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
154 | try | 154 | try |
155 | { | 155 | { |
156 | assetUuids[assetUuid] = assetType; | 156 | assetUuids[assetUuid] = assetType; |
157 | 157 | ||
158 | if ((sbyte)AssetType.Bodypart == assetType || (sbyte)AssetType.Clothing == assetType) | 158 | if ((sbyte)AssetType.Bodypart == assetType || (sbyte)AssetType.Clothing == assetType) |
159 | { | 159 | { |
160 | GetWearableAssetUuids(assetUuid, assetUuids); | 160 | GetWearableAssetUuids(assetUuid, assetUuids); |
@@ -204,16 +204,16 @@ namespace OpenSim.Region.Framework.Scenes | |||
204 | /// </param> | 204 | /// </param> |
205 | public void GatherAssetUuids(SceneObjectGroup sceneObject, IDictionary<UUID, sbyte> assetUuids) | 205 | public void GatherAssetUuids(SceneObjectGroup sceneObject, IDictionary<UUID, sbyte> assetUuids) |
206 | { | 206 | { |
207 | // m_log.DebugFormat( | 207 | // m_log.DebugFormat( |
208 | // "[ASSET GATHERER]: Getting assets for object {0}, {1}", sceneObject.Name, sceneObject.UUID); | 208 | // "[ASSET GATHERER]: Getting assets for object {0}, {1}", sceneObject.Name, sceneObject.UUID); |
209 | 209 | ||
210 | SceneObjectPart[] parts = sceneObject.Parts; | 210 | SceneObjectPart[] parts = sceneObject.Parts; |
211 | for (int i = 0; i < parts.Length; i++) | 211 | for (int i = 0; i < parts.Length; i++) |
212 | { | 212 | { |
213 | SceneObjectPart part = parts[i]; | 213 | SceneObjectPart part = parts[i]; |
214 | 214 | ||
215 | // m_log.DebugFormat( | 215 | // m_log.DebugFormat( |
216 | // "[ARCHIVER]: Getting part {0}, {1} for object {2}", part.Name, part.UUID, sceneObject.UUID); | 216 | // "[ARCHIVER]: Getting part {0}, {1} for object {2}", part.Name, part.UUID, sceneObject.UUID); |
217 | 217 | ||
218 | try | 218 | try |
219 | { | 219 | { |
@@ -234,7 +234,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
234 | } | 234 | } |
235 | } | 235 | } |
236 | } | 236 | } |
237 | 237 | ||
238 | // If the prim is a sculpt then preserve this information too | 238 | // If the prim is a sculpt then preserve this information too |
239 | if (part.Shape.SculptTexture != UUID.Zero) | 239 | if (part.Shape.SculptTexture != UUID.Zero) |
240 | assetUuids[part.Shape.SculptTexture] = (sbyte)AssetType.Texture; | 240 | assetUuids[part.Shape.SculptTexture] = (sbyte)AssetType.Texture; |
@@ -262,13 +262,13 @@ namespace OpenSim.Region.Framework.Scenes | |||
262 | } | 262 | } |
263 | 263 | ||
264 | TaskInventoryDictionary taskDictionary = (TaskInventoryDictionary)part.TaskInventory.Clone(); | 264 | TaskInventoryDictionary taskDictionary = (TaskInventoryDictionary)part.TaskInventory.Clone(); |
265 | 265 | ||
266 | // Now analyze this prim's inventory items to preserve all the uuids that they reference | 266 | // Now analyze this prim's inventory items to preserve all the uuids that they reference |
267 | foreach (TaskInventoryItem tii in taskDictionary.Values) | 267 | foreach (TaskInventoryItem tii in taskDictionary.Values) |
268 | { | 268 | { |
269 | // m_log.DebugFormat( | 269 | // m_log.DebugFormat( |
270 | // "[ARCHIVER]: Analysing item {0} asset type {1} in {2} {3}", | 270 | // "[ARCHIVER]: Analysing item {0} asset type {1} in {2} {3}", |
271 | // tii.Name, tii.Type, part.Name, part.UUID); | 271 | // tii.Name, tii.Type, part.Name, part.UUID); |
272 | 272 | ||
273 | if (!assetUuids.ContainsKey(tii.AssetID)) | 273 | if (!assetUuids.ContainsKey(tii.AssetID)) |
274 | GatherAssetUuids(tii.AssetID, (sbyte)tii.Type, assetUuids); | 274 | GatherAssetUuids(tii.AssetID, (sbyte)tii.Type, assetUuids); |
@@ -278,7 +278,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
278 | // to be called with scene objects that are in a scene (e.g. in the case of hg asset mapping and | 278 | // to be called with scene objects that are in a scene (e.g. in the case of hg asset mapping and |
279 | // inventory transfer. There needs to be a way for a module to register a method without assuming a | 279 | // inventory transfer. There needs to be a way for a module to register a method without assuming a |
280 | // Scene.EventManager is present. | 280 | // Scene.EventManager is present. |
281 | // part.ParentGroup.Scene.EventManager.TriggerGatherUuids(part, assetUuids); | 281 | // part.ParentGroup.Scene.EventManager.TriggerGatherUuids(part, assetUuids); |
282 | 282 | ||
283 | 283 | ||
284 | // still needed to retrieve textures used as materials for any parts containing legacy materials stored in DynAttrs | 284 | // still needed to retrieve textures used as materials for any parts containing legacy materials stored in DynAttrs |
@@ -375,7 +375,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
375 | } | 375 | } |
376 | } | 376 | } |
377 | } | 377 | } |
378 | 378 | ||
379 | /// <summary> | 379 | /// <summary> |
380 | /// Get an asset synchronously, potentially using an asynchronous callback. If the | 380 | /// Get an asset synchronously, potentially using an asynchronous callback. If the |
381 | /// asynchronous callback is used, we will wait for it to complete. | 381 | /// asynchronous callback is used, we will wait for it to complete. |
@@ -388,25 +388,25 @@ namespace OpenSim.Region.Framework.Scenes | |||
388 | 388 | ||
389 | // XXX: Switching to do this synchronously where the call was async before but we always waited for it | 389 | // XXX: Switching to do this synchronously where the call was async before but we always waited for it |
390 | // to complete anyway! | 390 | // to complete anyway! |
391 | // m_waitingForObjectAsset = true; | 391 | // m_waitingForObjectAsset = true; |
392 | // m_assetCache.Get(uuid.ToString(), this, AssetReceived); | 392 | // m_assetCache.Get(uuid.ToString(), this, AssetReceived); |
393 | // | 393 | // |
394 | // // The asset cache callback can either | 394 | // // The asset cache callback can either |
395 | // // | 395 | // // |
396 | // // 1. Complete on the same thread (if the asset is already in the cache) or | 396 | // // 1. Complete on the same thread (if the asset is already in the cache) or |
397 | // // 2. Come in via a different thread (if we need to go fetch it). | 397 | // // 2. Come in via a different thread (if we need to go fetch it). |
398 | // // | 398 | // // |
399 | // // The code below handles both these alternatives. | 399 | // // The code below handles both these alternatives. |
400 | // lock (this) | 400 | // lock (this) |
401 | // { | 401 | // { |
402 | // if (m_waitingForObjectAsset) | 402 | // if (m_waitingForObjectAsset) |
403 | // { | 403 | // { |
404 | // Monitor.Wait(this); | 404 | // Monitor.Wait(this); |
405 | // m_waitingForObjectAsset = false; | 405 | // m_waitingForObjectAsset = false; |
406 | // } | 406 | // } |
407 | // } | 407 | // } |
408 | // | 408 | // |
409 | // return m_requestedObjectAsset; | 409 | // return m_requestedObjectAsset; |
410 | } | 410 | } |
411 | 411 | ||
412 | /// <summary> | 412 | /// <summary> |
@@ -416,7 +416,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
416 | /// <param name="assetUuids">Dictionary in which to record the references</param> | 416 | /// <param name="assetUuids">Dictionary in which to record the references</param> |
417 | private void GetTextEmbeddedAssetUuids(UUID textAssetUuid, IDictionary<UUID, sbyte> assetUuids) | 417 | private void GetTextEmbeddedAssetUuids(UUID textAssetUuid, IDictionary<UUID, sbyte> assetUuids) |
418 | { | 418 | { |
419 | // m_log.DebugFormat("[ASSET GATHERER]: Getting assets for uuid references in asset {0}", embeddingAssetId); | 419 | // m_log.DebugFormat("[ASSET GATHERER]: Getting assets for uuid references in asset {0}", embeddingAssetId); |
420 | 420 | ||
421 | AssetBase textAsset = GetAsset(textAssetUuid); | 421 | AssetBase textAsset = GetAsset(textAssetUuid); |
422 | 422 | ||
@@ -630,7 +630,526 @@ namespace OpenSim.Region.Framework.Scenes | |||
630 | 630 | ||
631 | public AssetBase FetchAsset(UUID assetID) | 631 | public AssetBase FetchAsset(UUID assetID) |
632 | { | 632 | { |
633 | // Test if it's already here | ||
634 | AssetBase asset = m_assetService.Get(assetID.ToString()); | ||
635 | if (asset == null) | ||
636 | { | ||
637 | // It's not, so fetch it from abroad | ||
638 | asset = m_assetService.Get(m_assetServerURL + assetID.ToString()); | ||
639 | if (asset != null) | ||
640 | m_log.DebugFormat("[HGUUIDGatherer]: Copied asset {0} from {1} to local asset server", assetID, m_assetServerURL); | ||
641 | else | ||
642 | m_log.DebugFormat("[HGUUIDGatherer]: Failed to fetch asset {0} from {1}", assetID, m_assetServerURL); | ||
643 | } | ||
644 | //else | ||
645 | // m_log.DebugFormat("[HGUUIDGatherer]: Asset {0} from {1} was already here", assetID, m_assetServerURL); | ||
646 | |||
647 | return asset; | ||
648 | } | ||
649 | } | ||
650 | |||
651 | public class IteratingUuidGatherer | ||
652 | { | ||
653 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
654 | |||
655 | /// <summary> | ||
656 | /// Is gathering complete? | ||
657 | /// </summary> | ||
658 | public bool GatheringComplete { get { return m_assetUuidsToInspect.Count <= 0; } } | ||
659 | |||
660 | protected IAssetService m_assetService; | ||
661 | |||
662 | protected IDictionary<UUID, sbyte> m_gatheredAssetUuids; | ||
663 | |||
664 | protected Queue<UUID> m_assetUuidsToInspect; | ||
665 | |||
666 | public IteratingUuidGatherer(IAssetService assetService) | ||
667 | { | ||
668 | m_assetService = assetService; | ||
669 | m_gatheredAssetUuids = new Dictionary<UUID, sbyte>(); | ||
670 | |||
671 | // FIXME: Not efficient for searching, can improve. | ||
672 | m_assetUuidsToInspect = new Queue<UUID>(); | ||
673 | } | ||
674 | |||
675 | public IDictionary<UUID, sbyte> GetGatheredUuids() | ||
676 | { | ||
677 | return new Dictionary<UUID, sbyte>(m_gatheredAssetUuids); | ||
678 | } | ||
679 | |||
680 | public bool AddAssetUuidToInspect(UUID uuid) | ||
681 | { | ||
682 | if (m_assetUuidsToInspect.Contains(uuid)) | ||
683 | return false; | ||
684 | |||
685 | m_assetUuidsToInspect.Enqueue(uuid); | ||
686 | |||
687 | return true; | ||
688 | } | ||
689 | |||
690 | /// <summary> | ||
691 | /// Gathers the next set of assets returned by the next uuid to get from the asset service. | ||
692 | /// </summary> | ||
693 | /// <returns>false if gathering is already complete, true otherwise</returns> | ||
694 | public bool GatherNext() | ||
695 | { | ||
696 | if (GatheringComplete) | ||
697 | return false; | ||
698 | |||
699 | GetAssetUuids(m_assetUuidsToInspect.Dequeue()); | ||
700 | |||
701 | return true; | ||
702 | } | ||
703 | |||
704 | /// <summary> | ||
705 | /// Gathers all remaining asset UUIDS no matter how many calls are required to the asset service. | ||
706 | /// </summary> | ||
707 | /// <returns>false if gathering is already complete, true otherwise</returns> | ||
708 | public bool GatherAll() | ||
709 | { | ||
710 | if (GatheringComplete) | ||
711 | return false; | ||
712 | |||
713 | while (GatherNext()); | ||
714 | |||
715 | return true; | ||
716 | } | ||
717 | |||
718 | /// <summary> | ||
719 | /// Gather all the asset uuids associated with the asset referenced by a given uuid | ||
720 | /// </summary> | ||
721 | /// <remarks> | ||
722 | /// This includes both those directly associated with | ||
723 | /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained | ||
724 | /// within this object). | ||
725 | /// This method assumes that the asset type associated with this asset in persistent storage is correct (which | ||
726 | /// should always be the case). So with this method we always need to retrieve asset data even if the asset | ||
727 | /// is of a type which is known not to reference any other assets | ||
728 | /// </remarks> | ||
729 | /// <param name="assetUuid">The uuid of the asset for which to gather referenced assets</param> | ||
730 | private void GetAssetUuids(UUID assetUuid) | ||
731 | { | ||
732 | // avoid infinite loops | ||
733 | if (m_gatheredAssetUuids.ContainsKey(assetUuid)) | ||
734 | return; | ||
633 | 735 | ||
736 | try | ||
737 | { | ||
738 | AssetBase assetBase = GetAsset(assetUuid); | ||
739 | |||
740 | if (null != assetBase) | ||
741 | { | ||
742 | sbyte assetType = assetBase.Type; | ||
743 | m_gatheredAssetUuids[assetUuid] = assetType; | ||
744 | |||
745 | if ((sbyte)AssetType.Bodypart == assetType || (sbyte)AssetType.Clothing == assetType) | ||
746 | { | ||
747 | RecordWearableAssetUuids(assetBase); | ||
748 | } | ||
749 | else if ((sbyte)AssetType.Gesture == assetType) | ||
750 | { | ||
751 | RecordGestureAssetUuids(assetBase); | ||
752 | } | ||
753 | else if ((sbyte)AssetType.Notecard == assetType) | ||
754 | { | ||
755 | RecordTextEmbeddedAssetUuids(assetBase); | ||
756 | } | ||
757 | else if ((sbyte)AssetType.LSLText == assetType) | ||
758 | { | ||
759 | RecordTextEmbeddedAssetUuids(assetBase); | ||
760 | } | ||
761 | else if ((sbyte)OpenSimAssetType.Material == assetType) | ||
762 | { | ||
763 | RecordMaterialAssetUuids(assetBase); | ||
764 | } | ||
765 | else if ((sbyte)AssetType.Object == assetType) | ||
766 | { | ||
767 | RecordSceneObjectAssetUuids(assetBase); | ||
768 | } | ||
769 | } | ||
770 | } | ||
771 | catch (Exception) | ||
772 | { | ||
773 | m_log.ErrorFormat("[UUID GATHERER]: Failed to gather uuids for asset id {0}", assetUuid); | ||
774 | throw; | ||
775 | } | ||
776 | } | ||
777 | |||
778 | private void RecordAssetUuids(UUID assetUuid, sbyte assetType) | ||
779 | { | ||
780 | // Here, we want to collect uuids which require further asset fetches but mark the others as gathered | ||
781 | try | ||
782 | { | ||
783 | m_gatheredAssetUuids[assetUuid] = assetType; | ||
784 | |||
785 | if ((sbyte)AssetType.Bodypart == assetType || (sbyte)AssetType.Clothing == assetType) | ||
786 | { | ||
787 | AddAssetUuidToInspect(assetUuid); | ||
788 | } | ||
789 | else if ((sbyte)AssetType.Gesture == assetType) | ||
790 | { | ||
791 | AddAssetUuidToInspect(assetUuid); | ||
792 | } | ||
793 | else if ((sbyte)AssetType.Notecard == assetType) | ||
794 | { | ||
795 | AddAssetUuidToInspect(assetUuid); | ||
796 | } | ||
797 | else if ((sbyte)AssetType.LSLText == assetType) | ||
798 | { | ||
799 | AddAssetUuidToInspect(assetUuid); | ||
800 | } | ||
801 | else if ((sbyte)OpenSimAssetType.Material == assetType) | ||
802 | { | ||
803 | AddAssetUuidToInspect(assetUuid); | ||
804 | } | ||
805 | else if ((sbyte)AssetType.Object == assetType) | ||
806 | { | ||
807 | AddAssetUuidToInspect(assetUuid); | ||
808 | } | ||
809 | } | ||
810 | catch (Exception) | ||
811 | { | ||
812 | m_log.ErrorFormat( | ||
813 | "[ITERATABLE UUID GATHERER]: Failed to gather uuids for asset id {0}, type {1}", | ||
814 | assetUuid, assetType); | ||
815 | throw; | ||
816 | } | ||
817 | } | ||
818 | |||
819 | /// <summary> | ||
820 | /// Gather all the asset uuids associated with a given object. | ||
821 | /// </summary> | ||
822 | /// <remarks> | ||
823 | /// This includes both those directly associated with | ||
824 | /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained | ||
825 | /// within this object). | ||
826 | /// </remarks> | ||
827 | /// <param name="sceneObject">The scene object for which to gather assets</param> | ||
828 | public void RecordAssetUuids(SceneObjectGroup sceneObject) | ||
829 | { | ||
830 | // m_log.DebugFormat( | ||
831 | // "[ASSET GATHERER]: Getting assets for object {0}, {1}", sceneObject.Name, sceneObject.UUID); | ||
832 | |||
833 | SceneObjectPart[] parts = sceneObject.Parts; | ||
834 | for (int i = 0; i < parts.Length; i++) | ||
835 | { | ||
836 | SceneObjectPart part = parts[i]; | ||
837 | |||
838 | // m_log.DebugFormat( | ||
839 | // "[ARCHIVER]: Getting part {0}, {1} for object {2}", part.Name, part.UUID, sceneObject.UUID); | ||
840 | |||
841 | try | ||
842 | { | ||
843 | Primitive.TextureEntry textureEntry = part.Shape.Textures; | ||
844 | if (textureEntry != null) | ||
845 | { | ||
846 | // Get the prim's default texture. This will be used for faces which don't have their own texture | ||
847 | if (textureEntry.DefaultTexture != null) | ||
848 | RecordTextureEntryAssetUuids(textureEntry.DefaultTexture); | ||
849 | |||
850 | if (textureEntry.FaceTextures != null) | ||
851 | { | ||
852 | // Loop through the rest of the texture faces (a non-null face means the face is different from DefaultTexture) | ||
853 | foreach (Primitive.TextureEntryFace texture in textureEntry.FaceTextures) | ||
854 | { | ||
855 | if (texture != null) | ||
856 | RecordTextureEntryAssetUuids(texture); | ||
857 | } | ||
858 | } | ||
859 | } | ||
860 | |||
861 | // If the prim is a sculpt then preserve this information too | ||
862 | if (part.Shape.SculptTexture != UUID.Zero) | ||
863 | m_gatheredAssetUuids[part.Shape.SculptTexture] = (sbyte)AssetType.Texture; | ||
864 | |||
865 | if (part.Shape.ProjectionTextureUUID != UUID.Zero) | ||
866 | m_gatheredAssetUuids[part.Shape.ProjectionTextureUUID] = (sbyte)AssetType.Texture; | ||
867 | |||
868 | if (part.CollisionSound != UUID.Zero) | ||
869 | m_gatheredAssetUuids[part.CollisionSound] = (sbyte)AssetType.Sound; | ||
870 | |||
871 | if (part.ParticleSystem.Length > 0) | ||
872 | { | ||
873 | try | ||
874 | { | ||
875 | Primitive.ParticleSystem ps = new Primitive.ParticleSystem(part.ParticleSystem, 0); | ||
876 | if (ps.Texture != UUID.Zero) | ||
877 | m_gatheredAssetUuids[ps.Texture] = (sbyte)AssetType.Texture; | ||
878 | } | ||
879 | catch (Exception) | ||
880 | { | ||
881 | m_log.WarnFormat( | ||
882 | "[UUID GATHERER]: Could not check particle system for part {0} {1} in object {2} {3} since it is corrupt. Continuing.", | ||
883 | part.Name, part.UUID, sceneObject.Name, sceneObject.UUID); | ||
884 | } | ||
885 | } | ||
886 | |||
887 | TaskInventoryDictionary taskDictionary = (TaskInventoryDictionary)part.TaskInventory.Clone(); | ||
888 | |||
889 | // Now analyze this prim's inventory items to preserve all the uuids that they reference | ||
890 | foreach (TaskInventoryItem tii in taskDictionary.Values) | ||
891 | { | ||
892 | // m_log.DebugFormat( | ||
893 | // "[ARCHIVER]: Analysing item {0} asset type {1} in {2} {3}", | ||
894 | // tii.Name, tii.Type, part.Name, part.UUID); | ||
895 | |||
896 | if (!m_gatheredAssetUuids.ContainsKey(tii.AssetID)) | ||
897 | RecordAssetUuids(tii.AssetID, (sbyte)tii.Type); | ||
898 | } | ||
899 | |||
900 | // FIXME: We need to make gathering modular but we cannot yet, since gatherers are not guaranteed | ||
901 | // to be called with scene objects that are in a scene (e.g. in the case of hg asset mapping and | ||
902 | // inventory transfer. There needs to be a way for a module to register a method without assuming a | ||
903 | // Scene.EventManager is present. | ||
904 | // part.ParentGroup.Scene.EventManager.TriggerGatherUuids(part, assetUuids); | ||
905 | |||
906 | |||
907 | // still needed to retrieve textures used as materials for any parts containing legacy materials stored in DynAttrs | ||
908 | RecordMaterialsUuids(part); | ||
909 | } | ||
910 | catch (Exception e) | ||
911 | { | ||
912 | m_log.ErrorFormat("[UUID GATHERER]: Failed to get part - {0}", e); | ||
913 | m_log.DebugFormat( | ||
914 | "[UUID GATHERER]: Texture entry length for prim was {0} (min is 46)", | ||
915 | part.Shape.TextureEntry.Length); | ||
916 | } | ||
917 | } | ||
918 | } | ||
919 | |||
920 | /// <summary> | ||
921 | /// Collect all the asset uuids found in one face of a Texture Entry. | ||
922 | /// </summary> | ||
923 | private void RecordTextureEntryAssetUuids(Primitive.TextureEntryFace texture) | ||
924 | { | ||
925 | m_gatheredAssetUuids[texture.TextureID] = (sbyte)AssetType.Texture; | ||
926 | |||
927 | if (texture.MaterialID != UUID.Zero) | ||
928 | AddAssetUuidToInspect(texture.MaterialID); | ||
929 | } | ||
930 | |||
931 | /// <summary> | ||
932 | /// Gather all of the texture asset UUIDs used to reference "Materials" such as normal and specular maps | ||
933 | /// stored in legacy format in part.DynAttrs | ||
934 | /// </summary> | ||
935 | /// <param name="part"></param> | ||
936 | public void RecordMaterialsUuids(SceneObjectPart part) | ||
937 | { | ||
938 | // scan thru the dynAttrs map of this part for any textures used as materials | ||
939 | OSD osdMaterials = null; | ||
940 | |||
941 | lock (part.DynAttrs) | ||
942 | { | ||
943 | if (part.DynAttrs.ContainsStore("OpenSim", "Materials")) | ||
944 | { | ||
945 | OSDMap materialsStore = part.DynAttrs.GetStore("OpenSim", "Materials"); | ||
946 | |||
947 | if (materialsStore == null) | ||
948 | return; | ||
949 | |||
950 | materialsStore.TryGetValue("Materials", out osdMaterials); | ||
951 | } | ||
952 | |||
953 | if (osdMaterials != null) | ||
954 | { | ||
955 | //m_log.Info("[UUID Gatherer]: found Materials: " + OSDParser.SerializeJsonString(osd)); | ||
956 | |||
957 | if (osdMaterials is OSDArray) | ||
958 | { | ||
959 | OSDArray matsArr = osdMaterials as OSDArray; | ||
960 | foreach (OSDMap matMap in matsArr) | ||
961 | { | ||
962 | try | ||
963 | { | ||
964 | if (matMap.ContainsKey("Material")) | ||
965 | { | ||
966 | OSDMap mat = matMap["Material"] as OSDMap; | ||
967 | if (mat.ContainsKey("NormMap")) | ||
968 | { | ||
969 | UUID normalMapId = mat["NormMap"].AsUUID(); | ||
970 | if (normalMapId != UUID.Zero) | ||
971 | { | ||
972 | m_gatheredAssetUuids[normalMapId] = (sbyte)AssetType.Texture; | ||
973 | //m_log.Info("[UUID Gatherer]: found normal map ID: " + normalMapId.ToString()); | ||
974 | } | ||
975 | } | ||
976 | if (mat.ContainsKey("SpecMap")) | ||
977 | { | ||
978 | UUID specularMapId = mat["SpecMap"].AsUUID(); | ||
979 | if (specularMapId != UUID.Zero) | ||
980 | { | ||
981 | m_gatheredAssetUuids[specularMapId] = (sbyte)AssetType.Texture; | ||
982 | //m_log.Info("[UUID Gatherer]: found specular map ID: " + specularMapId.ToString()); | ||
983 | } | ||
984 | } | ||
985 | } | ||
986 | |||
987 | } | ||
988 | catch (Exception e) | ||
989 | { | ||
990 | m_log.Warn("[UUID Gatherer]: exception getting materials: " + e.Message); | ||
991 | } | ||
992 | } | ||
993 | } | ||
994 | } | ||
995 | } | ||
996 | } | ||
997 | |||
998 | /// <summary> | ||
999 | /// Get an asset synchronously, potentially using an asynchronous callback. If the | ||
1000 | /// asynchronous callback is used, we will wait for it to complete. | ||
1001 | /// </summary> | ||
1002 | /// <param name="uuid"></param> | ||
1003 | /// <returns></returns> | ||
1004 | protected virtual AssetBase GetAsset(UUID uuid) | ||
1005 | { | ||
1006 | return m_assetService.Get(uuid.ToString()); | ||
1007 | } | ||
1008 | |||
1009 | /// <summary> | ||
1010 | /// Record the asset uuids embedded within the given text (e.g. a script). | ||
1011 | /// </summary> | ||
1012 | /// <param name="textAsset"></param> | ||
1013 | private void RecordTextEmbeddedAssetUuids(AssetBase textAsset) | ||
1014 | { | ||
1015 | // m_log.DebugFormat("[ASSET GATHERER]: Getting assets for uuid references in asset {0}", embeddingAssetId); | ||
1016 | |||
1017 | string script = Utils.BytesToString(textAsset.Data); | ||
1018 | // m_log.DebugFormat("[ARCHIVER]: Script {0}", script); | ||
1019 | MatchCollection uuidMatches = Util.PermissiveUUIDPattern.Matches(script); | ||
1020 | // m_log.DebugFormat("[ARCHIVER]: Found {0} matches in text", uuidMatches.Count); | ||
1021 | |||
1022 | foreach (Match uuidMatch in uuidMatches) | ||
1023 | { | ||
1024 | UUID uuid = new UUID(uuidMatch.Value); | ||
1025 | // m_log.DebugFormat("[ARCHIVER]: Recording {0} in text", uuid); | ||
1026 | |||
1027 | AddAssetUuidToInspect(uuid); | ||
1028 | } | ||
1029 | } | ||
1030 | |||
1031 | /// <summary> | ||
1032 | /// Record the uuids referenced by the given wearable asset | ||
1033 | /// </summary> | ||
1034 | /// <param name="assetBase"></param> | ||
1035 | private void RecordWearableAssetUuids(AssetBase assetBase) | ||
1036 | { | ||
1037 | //m_log.Debug(new System.Text.ASCIIEncoding().GetString(bodypartAsset.Data)); | ||
1038 | AssetWearable wearableAsset = new AssetBodypart(assetBase.FullID, assetBase.Data); | ||
1039 | wearableAsset.Decode(); | ||
1040 | |||
1041 | //m_log.DebugFormat( | ||
1042 | // "[ARCHIVER]: Wearable asset {0} references {1} assets", wearableAssetUuid, wearableAsset.Textures.Count); | ||
1043 | |||
1044 | foreach (UUID uuid in wearableAsset.Textures.Values) | ||
1045 | m_gatheredAssetUuids[uuid] = (sbyte)AssetType.Texture; | ||
1046 | } | ||
1047 | |||
1048 | /// <summary> | ||
1049 | /// Get all the asset uuids associated with a given object. This includes both those directly associated with | ||
1050 | /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained | ||
1051 | /// within this object). | ||
1052 | /// </summary> | ||
1053 | /// <param name="sceneObjectAsset"></param> | ||
1054 | private void RecordSceneObjectAssetUuids(AssetBase sceneObjectAsset) | ||
1055 | { | ||
1056 | string xml = Utils.BytesToString(sceneObjectAsset.Data); | ||
1057 | |||
1058 | CoalescedSceneObjects coa; | ||
1059 | if (CoalescedSceneObjectsSerializer.TryFromXml(xml, out coa)) | ||
1060 | { | ||
1061 | foreach (SceneObjectGroup sog in coa.Objects) | ||
1062 | RecordAssetUuids(sog); | ||
1063 | } | ||
1064 | else | ||
1065 | { | ||
1066 | SceneObjectGroup sog = SceneObjectSerializer.FromOriginalXmlFormat(xml); | ||
1067 | |||
1068 | if (null != sog) | ||
1069 | RecordAssetUuids(sog); | ||
1070 | } | ||
1071 | } | ||
1072 | |||
1073 | /// <summary> | ||
1074 | /// Get the asset uuid associated with a gesture | ||
1075 | /// </summary> | ||
1076 | /// <param name="gestureAsset"></param> | ||
1077 | private void RecordGestureAssetUuids(AssetBase gestureAsset) | ||
1078 | { | ||
1079 | using (MemoryStream ms = new MemoryStream(gestureAsset.Data)) | ||
1080 | using (StreamReader sr = new StreamReader(ms)) | ||
1081 | { | ||
1082 | sr.ReadLine(); // Unknown (Version?) | ||
1083 | sr.ReadLine(); // Unknown | ||
1084 | sr.ReadLine(); // Unknown | ||
1085 | sr.ReadLine(); // Name | ||
1086 | sr.ReadLine(); // Comment ? | ||
1087 | int count = Convert.ToInt32(sr.ReadLine()); // Item count | ||
1088 | |||
1089 | for (int i = 0 ; i < count ; i++) | ||
1090 | { | ||
1091 | string type = sr.ReadLine(); | ||
1092 | if (type == null) | ||
1093 | break; | ||
1094 | string name = sr.ReadLine(); | ||
1095 | if (name == null) | ||
1096 | break; | ||
1097 | string id = sr.ReadLine(); | ||
1098 | if (id == null) | ||
1099 | break; | ||
1100 | string unknown = sr.ReadLine(); | ||
1101 | if (unknown == null) | ||
1102 | break; | ||
1103 | |||
1104 | // If it can be parsed as a UUID, it is an asset ID | ||
1105 | UUID uuid; | ||
1106 | if (UUID.TryParse(id, out uuid)) | ||
1107 | m_gatheredAssetUuids[uuid] = (sbyte)AssetType.Animation; // the asset is either an Animation or a Sound, but this distinction isn't important | ||
1108 | } | ||
1109 | } | ||
1110 | } | ||
1111 | |||
1112 | /// <summary> | ||
1113 | /// Get the asset uuid's referenced in a material. | ||
1114 | /// </summary> | ||
1115 | private void RecordMaterialAssetUuids(AssetBase materialAsset) | ||
1116 | { | ||
1117 | OSDMap mat = (OSDMap)OSDParser.DeserializeLLSDXml(materialAsset.Data); | ||
1118 | |||
1119 | UUID normMap = mat["NormMap"].AsUUID(); | ||
1120 | if (normMap != UUID.Zero) | ||
1121 | m_gatheredAssetUuids[normMap] = (sbyte)AssetType.Texture; | ||
1122 | |||
1123 | UUID specMap = mat["SpecMap"].AsUUID(); | ||
1124 | if (specMap != UUID.Zero) | ||
1125 | m_gatheredAssetUuids[specMap] = (sbyte)AssetType.Texture; | ||
1126 | } | ||
1127 | } | ||
1128 | |||
1129 | public class IteratingHGUuidGatherer : IteratingUuidGatherer | ||
1130 | { | ||
1131 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
1132 | |||
1133 | protected string m_assetServerURL; | ||
1134 | |||
1135 | public IteratingHGUuidGatherer(IAssetService assetService, string assetServerURL) | ||
1136 | : base(assetService) | ||
1137 | { | ||
1138 | m_assetServerURL = assetServerURL; | ||
1139 | if (!m_assetServerURL.EndsWith("/") && !m_assetServerURL.EndsWith("=")) | ||
1140 | m_assetServerURL = m_assetServerURL + "/"; | ||
1141 | } | ||
1142 | |||
1143 | protected override AssetBase GetAsset(UUID uuid) | ||
1144 | { | ||
1145 | if (string.Empty == m_assetServerURL) | ||
1146 | return base.GetAsset(uuid); | ||
1147 | else | ||
1148 | return FetchAsset(uuid); | ||
1149 | } | ||
1150 | |||
1151 | public AssetBase FetchAsset(UUID assetID) | ||
1152 | { | ||
634 | // Test if it's already here | 1153 | // Test if it's already here |
635 | AssetBase asset = m_assetService.Get(assetID.ToString()); | 1154 | AssetBase asset = m_assetService.Get(assetID.ToString()); |
636 | if (asset == null) | 1155 | if (asset == null) |