|
@@ -2,108 +2,156 @@ using System.Collections.Generic;
|
|
|
using System.Linq;
|
|
|
using AssetBank.Settings;
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
+using UnityEngine;
|
|
|
|
|
|
namespace AssetBank.Editor.Tools
|
|
|
{
|
|
|
public static class JsonDataTrimmer
|
|
|
{
|
|
|
- // The list of default MonoBehaviour properties to be used for comparison.
|
|
|
- private static readonly HashSet<string> DefaultMonoBehaviourProperties = new HashSet<string>
|
|
|
- {
|
|
|
- "m_ObjectHideFlags",
|
|
|
- "m_CorrespondingSourceObject",
|
|
|
- "m_PrefabInstance",
|
|
|
- "m_PrefabAsset",
|
|
|
- "m_GameObject",
|
|
|
- "m_Enabled",
|
|
|
- "m_EditorHideFlags",
|
|
|
- "m_Script",
|
|
|
- "m_Name",
|
|
|
- "m_EditorClassIdentifier"
|
|
|
- };
|
|
|
-
|
|
|
public static string Process(string rawJsonText, ProjectExporterSettings settings)
|
|
|
{
|
|
|
var allComponents = JArray.Parse(rawJsonText);
|
|
|
- var rules = settings.SafeComponents.ToDictionary(r => r.Component, r => r.ExposedFields);
|
|
|
-
|
|
|
- // Pass 1: Index all components by their anchor_id
|
|
|
- var componentMap = allComponents.Children<JObject>()
|
|
|
- .Where(c => c["anchor_id"] != null)
|
|
|
- .ToDictionary(c => c["anchor_id"].ToString(), c => c);
|
|
|
+
|
|
|
+ // 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: Process and trim each component in the map
|
|
|
- foreach (var component in componentMap.Values)
|
|
|
+ // Pass 2: Create a fully trimmed copy of every component
|
|
|
+ var trimmedComponentMap = new Dictionary<string, JObject>();
|
|
|
+ foreach (var component in allComponents.Children<JObject>())
|
|
|
{
|
|
|
- var dataBlock = (JObject)component["data"];
|
|
|
- if (dataBlock == null || !dataBlock.HasValues) continue;
|
|
|
+ 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) continue;
|
|
|
+ if (componentProperty == null)
|
|
|
+ {
|
|
|
+ trimmedComponentMap[anchorId] = trimmedComponent;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
var componentName = componentProperty.Name;
|
|
|
- var componentData = (JObject)componentProperty.Value;
|
|
|
+ var componentJObject = componentProperty.Value as JObject;
|
|
|
+ if (componentJObject == null)
|
|
|
+ {
|
|
|
+ trimmedComponentMap[anchorId] = trimmedComponent;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
if (rules.TryGetValue(componentName, out var exposedFields))
|
|
|
{
|
|
|
- if (exposedFields.Contains("*")) continue; // Keep everything
|
|
|
-
|
|
|
- var newComponentData = new JObject();
|
|
|
- if (componentName == "MonoBehaviour")
|
|
|
+ if (exposedFields.Contains("*"))
|
|
|
{
|
|
|
- foreach (var field in exposedFields)
|
|
|
- {
|
|
|
- if (componentData.TryGetValue(field, out var value))
|
|
|
- newComponentData.Add(field, value);
|
|
|
- }
|
|
|
- foreach (var property in componentData.Properties())
|
|
|
- {
|
|
|
- if (!DefaultMonoBehaviourProperties.Contains(property.Name) && newComponentData[property.Name] == null)
|
|
|
- newComponentData.Add(property.Name, property.Value);
|
|
|
- }
|
|
|
+ trimmedComponentMap[anchorId] = trimmedComponent;
|
|
|
+ continue;
|
|
|
}
|
|
|
- else
|
|
|
+
|
|
|
+ var propertiesToKeep = new HashSet<string>(exposedFields);
|
|
|
+ var currentKeys = componentJObject.Properties().Select(p => p.Name).ToList();
|
|
|
+ foreach (var key in currentKeys)
|
|
|
{
|
|
|
- foreach (var field in exposedFields)
|
|
|
+ if (!propertiesToKeep.Contains(key))
|
|
|
{
|
|
|
- if (componentData.TryGetValue(field, out var value))
|
|
|
- newComponentData.Add(field, value);
|
|
|
+ componentJObject.Remove(key);
|
|
|
}
|
|
|
}
|
|
|
- dataBlock[componentName] = newComponentData;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- dataBlock[componentName] = new JObject();
|
|
|
+ componentJObject.RemoveAll();
|
|
|
}
|
|
|
+ trimmedComponentMap[anchorId] = trimmedComponent;
|
|
|
}
|
|
|
-
|
|
|
- // Pass 3: Build the final list of GameObjects with embedded components
|
|
|
+
|
|
|
+ // Pass 3: Final Assembly
|
|
|
var finalOutput = new JArray();
|
|
|
- foreach (var component in componentMap.Values)
|
|
|
+ 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 dataBlock = (JObject)component["data"];
|
|
|
- if (dataBlock?["GameObject"] == null) continue; // We only want GameObjects in the final list
|
|
|
+ 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;
|
|
|
+ }
|
|
|
|
|
|
- var gameObjectData = (JObject)dataBlock["GameObject"];
|
|
|
- var componentList = (JArray)gameObjectData["m_Component"];
|
|
|
-
|
|
|
- if (componentList == null) 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;
|
|
|
+ }
|
|
|
|
|
|
- var newComponentList = new JArray();
|
|
|
- foreach (var compRef in componentList.Children<JObject>())
|
|
|
+ // If it's a GameObject, embed its already-trimmed children
|
|
|
+ if (originalComponent["data"]?["GameObject"] is JObject originalGameObjectData)
|
|
|
{
|
|
|
- var fileID = compRef["component"]?["fileID"]?.ToString();
|
|
|
- if (fileID != null && componentMap.TryGetValue(fileID, out var linkedComponent))
|
|
|
+ var trimmedGameObjectData = trimmedComponent["data"]["GameObject"] as JObject;
|
|
|
+ var componentList = originalGameObjectData["m_Component"] as JArray;
|
|
|
+ if (trimmedGameObjectData != null && componentList != null)
|
|
|
{
|
|
|
- // Add a clone of the linked component's 'data' object
|
|
|
- newComponentList.Add(linkedComponent["data"].DeepClone());
|
|
|
+ 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");
|
|
|
+ newComponentList.Add(clonedData);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Debug.LogWarning($"JsonDataTrimmer: Could not find component with anchor_id '{fileID}' referenced by GameObject '{gameObjectName}'.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ trimmedGameObjectData["m_Component"] = newComponentList;
|
|
|
}
|
|
|
}
|
|
|
- // Replace the old list of fileIDs with the new list of embedded data
|
|
|
- gameObjectData["m_Component"] = newComponentList;
|
|
|
|
|
|
- finalOutput.Add(component);
|
|
|
+ finalOutput.Add(trimmedComponent);
|
|
|
}
|
|
|
|
|
|
return finalOutput.ToString(Newtonsoft.Json.Formatting.None);
|