123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- using System.Collections.Generic;
- using System.Linq;
- using AssetBank.Editor.SchemaConverter.Data.Input;
- using AssetBank.Settings;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
- using UnityEngine;
- namespace AssetBank.Editor.Tools
- {
- public static class JsonDataTrimmer
- {
- public static string Process(string rawJsonText, ProjectExporterSettings settings)
- {
- var processedJson = ShortenBrokenPrefabGameObjects(rawJsonText);
-
- var allComponents = JArray.Parse(processedJson);
-
- // Pass 1: Build the definitive ruleset
- var rules = settings.SafeComponents.ToDictionary(r => r.Component, r => r.ExposedFields.ToHashSet());
- if (!settings.OverrideHardcodedDefaults)
- {
- foreach (var hardcodedRule in settings.HardcodedComponents)
- {
- if (rules.TryGetValue(hardcodedRule.Component, out var userRules))
- {
- userRules.UnionWith(hardcodedRule.ExposedFields);
- }
- else
- {
- rules[hardcodedRule.Component] = hardcodedRule.ExposedFields.ToHashSet();
- }
- }
- }
- // Pass 2: Create a fully trimmed copy of every component
- var trimmedComponentMap = new Dictionary<string, JObject>();
- foreach (var component in allComponents.Children<JObject>())
- {
- var anchorId = component["anchor_id"]?.ToString();
- if (string.IsNullOrEmpty(anchorId)) continue;
- var trimmedComponent = component.DeepClone() as JObject;
- var dataBlock = trimmedComponent["data"] as JObject;
- if (dataBlock == null || !dataBlock.HasValues)
- {
- trimmedComponentMap[anchorId] = trimmedComponent;
- continue;
- }
- var componentProperty = dataBlock.Properties().FirstOrDefault();
- if (componentProperty == null)
- {
- trimmedComponentMap[anchorId] = trimmedComponent;
- continue;
- }
- var componentName = componentProperty.Name;
- var componentJObject = componentProperty.Value as JObject;
- if (componentJObject == null)
- {
- trimmedComponentMap[anchorId] = trimmedComponent;
- continue;
- }
- if (rules.TryGetValue(componentName, out var exposedFields))
- {
- if (exposedFields.Contains("*"))
- {
- trimmedComponentMap[anchorId] = trimmedComponent;
- continue;
- }
- var propertiesToKeep = new HashSet<string>(exposedFields);
- var currentKeys = componentJObject.Properties().Select(p => p.Name).ToList();
- foreach (var key in currentKeys)
- {
- if (!propertiesToKeep.Contains(key))
- {
- componentJObject.Remove(key);
- }
- }
- }
- else
- {
- componentJObject.RemoveAll();
- }
- trimmedComponentMap[anchorId] = trimmedComponent;
- }
-
- // Pass 3: Final Assembly
- var finalOutput = new JArray();
- var embeddedIds = new HashSet<string>();
- // First, identify all components that will be embedded into GameObjects
- foreach (var originalComponent in allComponents.Children<JObject>())
- {
- if (originalComponent["data"]?["GameObject"]?["m_Component"] is JArray componentList)
- {
- foreach (var compRef in componentList.Children<JObject>())
- {
- if (compRef["component"]?["fileID"]?.ToString() is var fileID && !string.IsNullOrEmpty(fileID))
- {
- embeddedIds.Add(fileID);
- }
- }
- }
- }
- // Now, build the final output, preserving order
- foreach (var originalComponent in allComponents.Children<JObject>())
- {
- var anchorId = originalComponent["anchor_id"]?.ToString();
- if (string.IsNullOrEmpty(anchorId) || embeddedIds.Contains(anchorId))
- {
- // If a component has no ID, or it's a child of a GameObject, skip it.
- // Children will be added via their parent GameObject.
- continue;
- }
- // Get the trimmed version of the current component
- if (!trimmedComponentMap.TryGetValue(anchorId, out var trimmedComponent))
- {
- // Should not happen, but as a safeguard, add the original.
- finalOutput.Add(originalComponent.DeepClone());
- continue;
- }
- // If it's a GameObject, embed its already-trimmed children
- if (originalComponent["data"]?["GameObject"] is JObject originalGameObjectData)
- {
- var trimmedGameObjectData = trimmedComponent["data"]["GameObject"] as JObject;
- var componentList = originalGameObjectData["m_Component"] as JArray;
- if (trimmedGameObjectData != null && componentList != null)
- {
- var newComponentList = new JArray();
- var gameObjectName = originalGameObjectData["m_Name"]?.ToString() ?? "Unnamed";
- foreach (var compRef in componentList.Children<JObject>())
- {
- var fileID = compRef["component"]?["fileID"]?.ToString();
- if (string.IsNullOrEmpty(fileID)) continue;
- if (trimmedComponentMap.TryGetValue(fileID, out var linkedComponent))
- {
- var clonedData = linkedComponent["data"].DeepClone() as JObject;
- (clonedData?.Properties().FirstOrDefault()?.Value as JObject)?.Remove("m_GameObject");
-
- // Re-inject the anchor_id to preserve the link information after trimming.
- if (clonedData != null)
- {
- clonedData.AddFirst(new JProperty("anchor_id", fileID));
- }
-
- newComponentList.Add(clonedData);
- }
- else
- {
- Debug.LogWarning($"JsonDataTrimmer: Could not find component with anchor_id '{fileID}' referenced by GameObject '{gameObjectName}'.");
- }
- }
- trimmedGameObjectData["m_Component"] = newComponentList;
- }
- }
-
- finalOutput.Add(trimmedComponent);
- }
- return finalOutput.ToString(Newtonsoft.Json.Formatting.None);
- }
- private static string ShortenBrokenPrefabGameObjects(string data)
- {
- var nodes = JsonConvert.DeserializeObject<List<OriginalNode>>(data);
-
- if (nodes == null)
- {
- return data;
- }
-
- const string gameObjectTypeId = "1";
- const string prefabTypeId = "1001";
- const string transformTypeId = "4";
- var allGameObjects = nodes.Where(n => n.type_id == gameObjectTypeId).ToList();
- var allTransforms = nodes.Where(n => n.type_id == transformTypeId).ToList();
- var allPrefabs = nodes.Where(n => n.type_id == prefabTypeId).ToList();
- var restOfComponents = nodes.Where(n => n.type_id != gameObjectTypeId && n.type_id != prefabTypeId && n.type_id != transformTypeId).ToList();
-
- foreach (var gameObject in allGameObjects)
- {
- var nodeIndex = nodes.IndexOf(gameObject);
- var prefabInstanceId = gameObject.data?["GameObject"]?["m_PrefabInstance"]?["fileID"]?.ToString();
- if (string.IsNullOrEmpty(prefabInstanceId) || prefabInstanceId == "0") continue;
- var prefab = allPrefabs.FirstOrDefault(p => p.anchor_id == prefabInstanceId);
- if (prefab == null)
- {
- Debug.LogWarning($"JsonDataTrimmer: Could not find prefab with anchor_id '{prefabInstanceId}' referenced by GameObject '{gameObject.data?["GameObject"]?["m_Name"]}'.");
- continue;
- }
- var prefabGuid = prefab.data?["PrefabInstance"]?["m_SourcePrefab"]?["guid"]?.ToString();
- if (string.IsNullOrEmpty(prefabGuid))
- {
- Debug.LogWarning($"JsonDataTrimmer: Could not find GUID for prefab with anchor_id '{prefabInstanceId}' referenced by GameObject '{gameObject.data?["GameObject"]?["m_Name"]}'.");
- continue;
- }
- var relatedTransform = allTransforms.FirstOrDefault(t => t.data?["Transform"]?["m_PrefabInstance"]?["fileID"]?.ToString() == prefabInstanceId);
- if (relatedTransform == null)
- {
- Debug.LogWarning($"JsonDataTrimmer: Could not find Transform with anchor_id '{prefabInstanceId}' referenced by GameObject '{gameObject.data?["GameObject"]?["m_Name"]}'.");
- continue;
- }
- var relatedComponents = restOfComponents.Where(c => c.data?.SelectToken("*.m_GameObject.fileID")?.ToString() == gameObject.anchor_id).ToList();
-
- var newNode = new OriginalNode
- {
- type_id = gameObject.type_id,
- anchor_id = gameObject.anchor_id,
- data = gameObject.data
- };
- var jArray = new JArray();
- foreach (var component in relatedComponents)
- {
- jArray.Add(JObject.FromObject(new
- {
- component = new
- {
- fileID = component.anchor_id
- }
- }));
- }
- jArray.Add(JObject.FromObject(new
- {
- component = new
- {
- fileID = relatedTransform.anchor_id
- }
- }));
- newNode.data["GameObject"]!["m_Component"] = jArray;
- nodes[nodeIndex] = newNode;
- }
- return JsonConvert.SerializeObject(nodes, Formatting.None);
- }
- }
- }
|