From 3a1ce2715a522dcb1971944af17ad10d2263c7ab Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 28 Oct 2014 23:00:49 +0000 Subject: [PATCH] Add "wearables check" console command This checks that all the wearable assets and any assets for a given logged in avatar exist in the asset service --- .../Region/Framework/Scenes/UuidGatherer.cs | 222 +++++++++++++----- .../Avatar/Appearance/AppearanceInfoModule.cs | 96 +++++++- 2 files changed, 245 insertions(+), 73 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/UuidGatherer.cs b/OpenSim/Region/Framework/Scenes/UuidGatherer.cs index d07cc6a4fb..20ff5b5585 100644 --- a/OpenSim/Region/Framework/Scenes/UuidGatherer.cs +++ b/OpenSim/Region/Framework/Scenes/UuidGatherer.cs @@ -72,6 +72,67 @@ namespace OpenSim.Region.Framework.Scenes { m_assetService = assetService; } + + /// + /// Gather all the asset uuids associated with the asset referenced by a given uuid + /// + /// + /// This includes both those directly associated with + /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained + /// within this object). + /// This method assumes that the asset type associated with this asset in persistent storage is correct (which + /// should always be the case). So with this method we always need to retrieve asset data even if the asset + /// is of a type which is known not to reference any other assets + /// + /// The uuid of the asset for which to gather referenced assets + /// The assets gathered + public void GatherAssetUuids(UUID assetUuid, IDictionary assetUuids) + { + // avoid infinite loops + if (assetUuids.ContainsKey(assetUuid)) + return; + + try + { + AssetBase assetBase = GetAsset(assetUuid); + + if (null != assetBase) + { + sbyte assetType = assetBase.Type; + assetUuids[assetUuid] = assetType; + + if ((sbyte)AssetType.Bodypart == assetType || (sbyte)AssetType.Clothing == assetType) + { + GetWearableAssetUuids(assetBase, assetUuids); + } + else if ((sbyte)AssetType.Gesture == assetType) + { + GetGestureAssetUuids(assetBase, assetUuids); + } + else if ((sbyte)AssetType.Notecard == assetType) + { + GetTextEmbeddedAssetUuids(assetBase, assetUuids); + } + else if ((sbyte)AssetType.LSLText == assetType) + { + GetTextEmbeddedAssetUuids(assetBase, assetUuids); + } + else if ((sbyte)OpenSimAssetType.Material == assetType) + { + GetMaterialAssetUuids(assetBase, assetUuids); + } + else if ((sbyte)AssetType.Object == assetType) + { + GetSceneObjectAssetUuids(assetBase, assetUuids); + } + } + } + catch (Exception) + { + m_log.ErrorFormat("[UUID GATHERER]: Failed to gather uuids for asset id {0}", assetUuid); + throw; + } + } /// /// Gather all the asset uuids associated with the asset referenced by a given uuid @@ -246,19 +307,6 @@ namespace OpenSim.Region.Framework.Scenes } } -// /// -// /// The callback made when we request the asset for an object from the asset service. -// /// -// private void AssetReceived(string id, Object sender, AssetBase asset) -// { -// lock (this) -// { -// m_requestedObjectAsset = asset; -// m_waitingForObjectAsset = false; -// Monitor.Pulse(this); -// } -// } - /// /// Gather all of the texture asset UUIDs used to reference "Materials" such as normal and specular maps /// stored in legacy format in part.DynAttrs @@ -362,32 +410,42 @@ namespace OpenSim.Region.Framework.Scenes } /// - /// Record the asset uuids embedded within the given script. + /// Record the asset uuids embedded within the given text (e.g. a script). /// - /// + /// /// Dictionary in which to record the references - private void GetTextEmbeddedAssetUuids(UUID embeddingAssetId, IDictionary assetUuids) + private void GetTextEmbeddedAssetUuids(UUID textAssetUuid, IDictionary assetUuids) { // m_log.DebugFormat("[ASSET GATHERER]: Getting assets for uuid references in asset {0}", embeddingAssetId); - AssetBase embeddingAsset = GetAsset(embeddingAssetId); + AssetBase textAsset = GetAsset(textAssetUuid); - if (null != embeddingAsset) + if (null != textAsset) + GetTextEmbeddedAssetUuids(textAsset, assetUuids); + } + + /// + /// Record the asset uuids embedded within the given text (e.g. a script). + /// + /// + /// Dictionary in which to record the references + private void GetTextEmbeddedAssetUuids(AssetBase textAsset, IDictionary assetUuids) + { + // m_log.DebugFormat("[ASSET GATHERER]: Getting assets for uuid references in asset {0}", embeddingAssetId); + + string script = Utils.BytesToString(textAsset.Data); + // m_log.DebugFormat("[ARCHIVER]: Script {0}", script); + MatchCollection uuidMatches = Util.PermissiveUUIDPattern.Matches(script); + // m_log.DebugFormat("[ARCHIVER]: Found {0} matches in text", uuidMatches.Count); + + foreach (Match uuidMatch in uuidMatches) { - string script = Utils.BytesToString(embeddingAsset.Data); -// m_log.DebugFormat("[ARCHIVER]: Script {0}", script); - MatchCollection uuidMatches = Util.PermissiveUUIDPattern.Matches(script); -// m_log.DebugFormat("[ARCHIVER]: Found {0} matches in text", uuidMatches.Count); + UUID uuid = new UUID(uuidMatch.Value); + // m_log.DebugFormat("[ARCHIVER]: Recording {0} in text", uuid); - foreach (Match uuidMatch in uuidMatches) - { - UUID uuid = new UUID(uuidMatch.Value); -// m_log.DebugFormat("[ARCHIVER]: Recording {0} in text", uuid); - - // Embedded asset references (if not false positives) could be for many types of asset, so we will - // label these as unknown. - assetUuids[uuid] = (sbyte)AssetType.Unknown; - } + // Embedded asset references (if not false positives) could be for many types of asset, so we will + // label these as unknown. + assetUuids[uuid] = (sbyte)AssetType.Unknown; } } @@ -401,18 +459,26 @@ namespace OpenSim.Region.Framework.Scenes AssetBase assetBase = GetAsset(wearableAssetUuid); if (null != assetBase) + GetWearableAssetUuids(assetBase, assetUuids); + } + + /// + /// Record the uuids referenced by the given wearable asset + /// + /// + /// Dictionary in which to record the references + private void GetWearableAssetUuids(AssetBase assetBase, IDictionary assetUuids) + { + //m_log.Debug(new System.Text.ASCIIEncoding().GetString(bodypartAsset.Data)); + AssetWearable wearableAsset = new AssetBodypart(assetBase.FullID, assetBase.Data); + wearableAsset.Decode(); + + //m_log.DebugFormat( + // "[ARCHIVER]: Wearable asset {0} references {1} assets", wearableAssetUuid, wearableAsset.Textures.Count); + + foreach (UUID uuid in wearableAsset.Textures.Values) { - //m_log.Debug(new System.Text.ASCIIEncoding().GetString(bodypartAsset.Data)); - AssetWearable wearableAsset = new AssetBodypart(wearableAssetUuid, assetBase.Data); - wearableAsset.Decode(); - - //m_log.DebugFormat( - // "[ARCHIVER]: Wearable asset {0} references {1} assets", wearableAssetUuid, wearableAsset.Textures.Count); - - foreach (UUID uuid in wearableAsset.Textures.Values) - { - assetUuids[uuid] = (sbyte)AssetType.Texture; - } + assetUuids[uuid] = (sbyte)AssetType.Texture; } } @@ -425,25 +491,35 @@ namespace OpenSim.Region.Framework.Scenes /// private void GetSceneObjectAssetUuids(UUID sceneObjectUuid, IDictionary assetUuids) { - AssetBase objectAsset = GetAsset(sceneObjectUuid); + AssetBase sceneObjectAsset = GetAsset(sceneObjectUuid); - if (null != objectAsset) + if (null != sceneObjectAsset) + GetSceneObjectAssetUuids(sceneObjectAsset, assetUuids); + } + + /// + /// Get all the asset uuids associated with a given object. This includes both those directly associated with + /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained + /// within this object). + /// + /// + /// + private void GetSceneObjectAssetUuids(AssetBase sceneObjectAsset, IDictionary assetUuids) + { + string xml = Utils.BytesToString(sceneObjectAsset.Data); + + CoalescedSceneObjects coa; + if (CoalescedSceneObjectsSerializer.TryFromXml(xml, out coa)) { - string xml = Utils.BytesToString(objectAsset.Data); - - CoalescedSceneObjects coa; - if (CoalescedSceneObjectsSerializer.TryFromXml(xml, out coa)) - { - foreach (SceneObjectGroup sog in coa.Objects) - GatherAssetUuids(sog, assetUuids); - } - else - { - SceneObjectGroup sog = SceneObjectSerializer.FromOriginalXmlFormat(xml); - - if (null != sog) - GatherAssetUuids(sog, assetUuids); - } + foreach (SceneObjectGroup sog in coa.Objects) + GatherAssetUuids(sog, assetUuids); + } + else + { + SceneObjectGroup sog = SceneObjectSerializer.FromOriginalXmlFormat(xml); + + if (null != sog) + GatherAssetUuids(sog, assetUuids); } } @@ -454,12 +530,22 @@ namespace OpenSim.Region.Framework.Scenes /// private void GetGestureAssetUuids(UUID gestureUuid, IDictionary assetUuids) { - AssetBase assetBase = GetAsset(gestureUuid); - if (null == assetBase) + AssetBase gestureAsset = GetAsset(gestureUuid); + if (null == gestureAsset) return; - using (MemoryStream ms = new MemoryStream(assetBase.Data)) - using (StreamReader sr = new StreamReader(ms)) + GetGestureAssetUuids(gestureAsset, assetUuids); + } + + /// + /// Get the asset uuid associated with a gesture + /// + /// + /// + private void GetGestureAssetUuids(AssetBase gestureAsset, IDictionary assetUuids) + { + using (MemoryStream ms = new MemoryStream(gestureAsset.Data)) + using (StreamReader sr = new StreamReader(ms)) { sr.ReadLine(); // Unknown (Version?) sr.ReadLine(); // Unknown @@ -500,7 +586,15 @@ namespace OpenSim.Region.Framework.Scenes if (null == assetBase) return; - OSDMap mat = (OSDMap)OSDParser.DeserializeLLSDXml(assetBase.Data); + GetMaterialAssetUuids(assetBase, assetUuids); + } + + /// + /// Get the asset uuid's referenced in a material. + /// + private void GetMaterialAssetUuids(AssetBase materialAsset, IDictionary assetUuids) + { + OSDMap mat = (OSDMap)OSDParser.DeserializeLLSDXml(materialAsset.Data); UUID normMap = mat["NormMap"].AsUUID(); if (normMap != UUID.Zero) diff --git a/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs b/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs index 51dfd47516..f67f6131d4 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs @@ -51,7 +51,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private Dictionary m_scenes = new Dictionary(); + private List m_scenes = new List(); + // private IAvatarFactoryModule m_avatarFactory; public string Name { get { return "Appearance Information Module"; } } @@ -83,7 +84,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance // m_log.DebugFormat("[APPEARANCE INFO MODULE]: REGION {0} REMOVED", scene.RegionInfo.RegionName); lock (m_scenes) - m_scenes.Remove(scene.RegionInfo.RegionID); + m_scenes.Remove(scene); } public void RegionLoaded(Scene scene) @@ -91,7 +92,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance // m_log.DebugFormat("[APPEARANCE INFO MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName); lock (m_scenes) - m_scenes[scene.RegionInfo.RegionID] = scene; + m_scenes.Add(scene); scene.AddCommand( "Users", this, "show appearance", @@ -140,6 +141,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance "If no avatar name is given then a general summary for all avatars in the scene is shown.\n" + "If an avatar name is given then specific information about current wearables is shown.", HandleShowWearablesCommand); + + scene.AddCommand( + "Users", this, "wearables check", + "wearables check ", + "Check that the wearables of a given avatar in the scene are valid.", + "This currently checks that the wearable assets themselves and any assets referenced by them exist.", + HandleCheckWearablesCommand); } private void HandleSendAppearanceCommand(string module, string[] cmd) @@ -163,7 +171,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance lock (m_scenes) { - foreach (Scene scene in m_scenes.Values) + foreach (Scene scene in m_scenes) { if (targetNameSupplied) { @@ -215,7 +223,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance lock (m_scenes) { - foreach (Scene scene in m_scenes.Values) + foreach (Scene scene in m_scenes) { if (targetNameSupplied) { @@ -251,7 +259,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance lock (m_scenes) { - foreach (Scene scene in m_scenes.Values) + foreach (Scene scene in m_scenes) { ScenePresence sp = scene.GetScenePresence(firstname, lastname); if (sp != null && !sp.IsChildAgent) @@ -285,7 +293,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance lock (m_scenes) { - foreach (Scene scene in m_scenes.Values) + foreach (Scene scene in m_scenes) { scene.ForEachRootScenePresence( sp => @@ -338,7 +346,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance { lock (m_scenes) { - foreach (Scene scene in m_scenes.Values) + foreach (Scene scene in m_scenes) { ScenePresence sp = scene.GetScenePresence(optionalTargetFirstName, optionalTargetLastName); if (sp != null && !sp.IsChildAgent) @@ -354,7 +362,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance lock (m_scenes) { - foreach (Scene scene in m_scenes.Values) + foreach (Scene scene in m_scenes) { scene.ForEachRootScenePresence( sp => @@ -376,6 +384,76 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance MainConsole.Instance.Output(sb.ToString()); } + private void HandleCheckWearablesCommand(string module, string[] cmd) + { + if (cmd.Length != 4) + { + MainConsole.Instance.OutputFormat("Usage: wearables check "); + return; + } + + string firstname = cmd[2]; + string lastname = cmd[3]; + + StringBuilder sb = new StringBuilder(); + UuidGatherer uuidGatherer = new UuidGatherer(m_scenes[0].AssetService); + + lock (m_scenes) + { + foreach (Scene scene in m_scenes) + { + ScenePresence sp = scene.GetScenePresence(firstname, lastname); + if (sp != null && !sp.IsChildAgent) + { + sb.AppendFormat("Wearables checks for {0}\n\n", sp.Name); + + for (int i = (int)WearableType.Shape; i < (int)WearableType.Physics; i++) + { + AvatarWearable aw = sp.Appearance.Wearables[i]; + + if (aw.Count > 0) + { + sb.Append(Enum.GetName(typeof(WearableType), i)); + sb.Append("\n"); + + for (int j = 0; j < aw.Count; j++) + { + WearableItem wi = aw[j]; + + ConsoleDisplayList cdl = new ConsoleDisplayList(); + cdl.Indent = 2; + cdl.AddRow("Item UUID", wi.ItemID); + cdl.AddRow("Assets", ""); + sb.Append(cdl.ToString()); + + Dictionary assetUuids = new Dictionary(); + uuidGatherer.GatherAssetUuids(wi.AssetID, assetUuids); + string[] assetStrings + = Array.ConvertAll(assetUuids.Keys.ToArray(), u => u.ToString()); + + bool[] existChecks = scene.AssetService.AssetsExist(assetStrings); + + ConsoleDisplayTable cdt = new ConsoleDisplayTable(); + cdt.Indent = 4; + cdt.AddColumn("Type", 10); + cdt.AddColumn("UUID", ConsoleDisplayUtil.UuidSize); + cdt.AddColumn("Found", 5); + + for (int k = 0; k < existChecks.Length; k++) + cdt.AddRow((AssetType)assetUuids[new UUID(assetStrings[k])], assetStrings[k], existChecks[k] ? "yes" : "no"); + + sb.Append(cdt.ToString()); + sb.Append("\n"); + } + } + } + } + } + } + + MainConsole.Instance.Output(sb.ToString()); + } + private void AppendWearablesDetailReport(ScenePresence sp, StringBuilder sb) { sb.AppendFormat("\nWearables for {0}\n", sp.Name);