From 88e9c0e11b93a1276e2cf663e37a3fc1e6ba0b3a Mon Sep 17 00:00:00 2001 From: Lexi Date: Sat, 5 Jul 2025 21:05:00 -0400 Subject: [PATCH 1/2] Speed up patch manager by not applying every patch to every asset, and instead use dictionaries on labels and names where-ever possible --- Runtime/Core/Assets/PatchingManager.cs | 127 +++++---- Runtime/Core/CoreModule.cs | 4 +- .../SassyPatching/Rulesets/JsonRuleset.cs | 11 + Runtime/Missions/Rulesets/MissionRuleset.cs | 12 +- .../Missions/Selectables/ActionsSelectable.cs | 15 +- .../Selectables/ConditionSetSelectable.cs | 15 +- .../Selectables/ContentBranchSelectable.cs | 12 +- .../Selectables/ContentBranchesSelectable.cs | 13 +- .../Selectables/MissionRewardSelectable.cs | 14 +- .../Missions/Selectables/MissionSelectable.cs | 14 +- .../Missions/Selectables/StageSelectable.cs | 14 +- .../Missions/Selectables/StagesSelectable.cs | 12 +- Runtime/Parts/Rulesets/PartsRuleset.cs | 14 +- .../Parts/Selectables/DataEngineSelectable.cs | 14 +- Runtime/Parts/Selectables/ModuleSelectable.cs | 14 +- Runtime/Parts/Selectables/PartSelectable.cs | 14 +- .../ResourceContainersSelectable.cs | 11 +- .../Rulesets/ResourceRuleset.cs | 20 +- .../Rulesets/ResourceUnitRuleset.cs | 14 +- .../Selectables/RecipeSelectable.cs | 15 +- .../Selectables/ResourceSelectable.cs | 14 +- .../Rulesets/AtmosphereOverrideRuleset.cs | 11 +- .../Planets/Rulesets/CelestialBodyRuleset.cs | 13 +- Runtime/Planets/Rulesets/GalaxyRuleset.cs | 17 +- .../Rulesets/VolumeCloudOverrideRuleset.cs | 12 +- .../AtmosphereOverrideSelectable.cs | 13 +- .../Selectables/CelestialBodySelectable.cs | 15 +- .../Planets/Selectables/GalaxySelectable.cs | 15 +- .../Selectables/VolumeCloudSelectable.cs | 15 +- Runtime/SassyPatching/BaseSelectable.cs | 6 +- .../SassyPatching/Execution/SassyGenerator.cs | 8 +- .../Execution/SassyTextPatcher.cs | 99 +++++-- Runtime/SassyPatching/Execution/Universe.cs | 249 ++++++++++++++++-- .../Interfaces/IPatcherRuleSet.cs | 23 +- .../SassyPatching/Interfaces/ISelectable.cs | 4 + .../Nodes/Selectors/ChildSelector.cs | 4 +- .../Nodes/Selectors/ClassCaptureSelector.cs | 3 +- .../Nodes/Selectors/ClassSelector.cs | 3 +- .../Nodes/Selectors/CombinationSelector.cs | 17 +- .../Selectors/ElementAdditionSelector.cs | 3 +- .../Nodes/Selectors/ElementSelector.cs | 3 +- .../Nodes/Selectors/EnsureSelector.cs | 3 +- .../Nodes/Selectors/IntersectionSelector.cs | 4 +- .../Nodes/Selectors/NameSelector.cs | 3 +- .../Nodes/Selectors/RulesetSelector.cs | 31 +-- .../SassyPatching/Nodes/Selectors/Selector.cs | 7 +- .../Nodes/Selectors/WildcardSelector.cs | 3 +- .../Nodes/Selectors/WithoutClassSelector.cs | 3 +- .../Nodes/Selectors/WithoutNameSelector.cs | 3 +- .../Nodes/Statements/SelectionBlock.cs | 17 +- .../Selectables/JTokenSelectable.cs | 14 +- .../Science/Rulesets/DiscoverablesRuleset.cs | 16 +- Runtime/Science/Rulesets/ExperimentRuleset.cs | 12 +- Runtime/Science/Rulesets/RegionsRuleset.cs | 15 +- Runtime/Science/Rulesets/ScienceRuleset.cs | 16 +- .../Selectables/DiscoverablesSelectable.cs | 14 +- .../Selectables/ExperimentSelectable.cs | 14 +- .../Science/Selectables/RegionsSelectable.cs | 14 +- .../Science/Selectables/ScienceSelectable.cs | 14 +- Runtime/Shared/Interfaces.meta | 8 - .../Shared/Interfaces/ITextAssetGenerator.cs | 21 -- .../Interfaces/ITextAssetGenerator.cs.meta | 11 - Runtime/Shared/Interfaces/ITextPatcher.cs | 22 -- .../Shared/Interfaces/ITextPatcher.cs.meta | 11 - 64 files changed, 834 insertions(+), 368 deletions(-) delete mode 100644 Runtime/Shared/Interfaces.meta delete mode 100644 Runtime/Shared/Interfaces/ITextAssetGenerator.cs delete mode 100644 Runtime/Shared/Interfaces/ITextAssetGenerator.cs.meta delete mode 100644 Runtime/Shared/Interfaces/ITextPatcher.cs delete mode 100644 Runtime/Shared/Interfaces/ITextPatcher.cs.meta diff --git a/Runtime/Core/Assets/PatchingManager.cs b/Runtime/Core/Assets/PatchingManager.cs index c19d113..e36bf5b 100644 --- a/Runtime/Core/Assets/PatchingManager.cs +++ b/Runtime/Core/Assets/PatchingManager.cs @@ -8,8 +8,8 @@ using PatchManager.Core.Cache.Json; using PatchManager.Core.Utility; using PatchManager.SassyPatching.Execution; +using PatchManager.SassyPatching.Interfaces; using PatchManager.Shared; -using PatchManager.Shared.Interfaces; using SpaceWarp.API.Mods; using UniLinq; using UnityEngine; @@ -20,14 +20,12 @@ namespace PatchManager.Core.Assets { internal static class PatchingManager { - internal static readonly List Patchers = new(); - internal static readonly List Generators = new(); internal static Universe Universe; private static readonly PatchHashes CurrentPatchHashes = PatchHashes.CreateDefault(); private static int _initialLibraryCount; - private static Dictionary> _createdAssets = new(); + private static Dictionary> _createdAssets = new(); internal static int TotalPatchCount; internal static int TotalErrorCount; @@ -36,81 +34,74 @@ public static void GenerateUniverse(HashSet singleFileModIds) { var loadedPlugins = PluginList.AllEnabledAndActivePlugins.Select(x => x.Guid).ToList(); loadedPlugins.AddRange(singleFileModIds); - Universe = new(RegisterPatcher, Logging.LogError, Logging.LogMessage, RegisterGenerator, + Universe = new(Logging.LogError, Logging.LogMessage, loadedPlugins); _initialLibraryCount = Universe.AllLibraries.Count; } - private static void RegisterPatcher(ITextPatcher patcher) - { - for (var index = 0; index < Patchers.Count; index++) - { - if (Patchers[index].Priority <= patcher.Priority) - { - continue; - } + // private static void RegisterPatcher(ITextPatcher patcher) + // { + // for (var index = 0; index < Patchers.Count; index++) + // { + // if (Patchers[index].Priority <= patcher.Priority) + // { + // continue; + // } + // + // Patchers.Insert(index, patcher); + // return; + // } + // + // Patchers.Add(patcher); + // } + // + // private static void RegisterGenerator(ITextAssetGenerator generator) + // { + // for (var index = 0; index < Generators.Count; index++) + // { + // if (Generators[index].Priority <= generator.Priority) + // { + // continue; + // } + // + // Generators.Insert(index, generator); + // return; + // } + // + // Generators.Add(generator); + // } - Patchers.Insert(index, patcher); - return; - } - - Patchers.Add(patcher); - } - - private static void RegisterGenerator(ITextAssetGenerator generator) + private static string PatchJson(string label, string assetName, string text) { - for (var index = 0; index < Generators.Count; index++) - { - if (Generators[index].Priority <= generator.Priority) - { - continue; - } + Logging.LogInfo($"Patching {label}:{assetName}"); - Generators.Insert(index, generator); - return; + text = Universe.RunAllPatchesFor(label, assetName, text, out var patchCount, out var errorCount); + TotalErrorCount += errorCount; + TotalPatchCount += patchCount; + if (patchCount > 0) + { + Logging.LogInfo($"Patched {label}:{assetName} with {patchCount} patches. Total: {TotalPatchCount}"); } - Generators.Add(generator); + return text; } - - private static string PatchJson(string label, string assetName, string text) + + private static string PatchJson(string label, string assetName, ISelectable data) { - Console.WriteLine($"Patching {label}:{assetName}"); - var patchCount = 0; - - foreach (var patcher in Patchers) - { - var backup = text; - try - { - var wasPatched = patcher.TryPatch(label, assetName, ref text); - if (wasPatched) - { - patchCount++; - } - } - catch (Exception e) - { - TotalErrorCount += 1; - Console.WriteLine($"Patch of {label}:{assetName} errored due to: {e}"); - text = backup; - } - - if (text == "") - { - break; - } - } + Logging.LogInfo($"Patching {label}:{assetName}"); + var text = Universe.RunAllPatchesFor(label, assetName, data, out var patchCount, out var errorCount); + TotalErrorCount += errorCount; TotalPatchCount += patchCount; if (patchCount > 0) { - Console.WriteLine($"Patched {label}:{assetName} with {patchCount} patches. Total: {TotalPatchCount}"); + Logging.LogInfo($"Patched {label}:{assetName} with {patchCount} patches. Total: {TotalPatchCount}"); } return text; } + private static int _previousLibraryCount = -1; public static void ImportModPatches(string modName, string modFolder) @@ -150,8 +141,8 @@ public static void RegisterPatches() { Logging.LogInfo($"Registering all patches!"); Universe.RegisterAllPatches(); - Logging.LogInfo($"{Patchers.Count} patchers registered!"); - Logging.LogInfo($"{Generators.Count} generators registered!"); + Logging.LogInfo($"{Universe.TotalPatchCount} patchers registered!"); + Logging.LogInfo($"{Universe.Generators.Count} generators registered!"); } /// @@ -241,7 +232,7 @@ private static AsyncOperationHandle> RebuildCache(string label) } catch (Exception e) { - Console.WriteLine($"Unable to patch {asset.name} due to: {e.Message}"); + Logging.LogError($"Unable to patch {asset.name} due to: {e.Message}"); } }); @@ -261,7 +252,7 @@ void SaveArchive() CacheManager.Inventory.CacheEntries.AddRangeUnique(assetsCacheEntries); CacheManager.SaveInventory(); - Console.WriteLine($"Cache for label '{label}' rebuilt."); + Logging.LogInfo($"Cache for label '{label}' rebuilt."); } if (handle.Status == AsyncOperationStatus.Failed && !unchanged) @@ -285,21 +276,21 @@ void SaveArchive() public static void CreateNewAssets(Action resolve, Action reject) { - foreach (var generator in Generators) + foreach (var generator in Universe.Generators) { try { - var text = generator.Create(out var label, out var name); - Logging.LogDebug($"Generated an asset with the label {label}, and name {name}:\n{text}"); + var data = generator.Create(out var label, out var name); + Logging.LogDebug($"Generated an asset with the label {label}, and name {name}:\n{data}"); if (!_createdAssets.ContainsKey(label)) { - _createdAssets[label] = new List<(string name, string text)>(); + _createdAssets[label] = new List<(string name, ISelectable data)>(); } if (!_createdAssets[label].Any(x => x.name == name)) { - _createdAssets[label].Add((name, text)); + _createdAssets[label].Add((name, data)); } } diff --git a/Runtime/Core/CoreModule.cs b/Runtime/Core/CoreModule.cs index 1033cf6..2f36933 100644 --- a/Runtime/Core/CoreModule.cs +++ b/Runtime/Core/CoreModule.cs @@ -171,8 +171,8 @@ public override VisualElement GetDetails() visible = true }; var text = new TextElement(); - text.text += $"Amount of loaded patchers: {PatchingManager.Patchers.Count}\n"; - text.text += $"Amount of loaded generators: {PatchingManager.Generators.Count}\n"; + text.text += $"Amount of loaded patchers: {PatchingManager.Universe.TotalPatchCount}\n"; + text.text += $"Amount of loaded generators: {PatchingManager.Universe.Generators.Count}\n"; text.text += $"Amount of loaded libraries: {PatchingManager.Universe.AllLibraries.Count}\n"; if (_wasCacheInvalidated) { diff --git a/Runtime/Generic/SassyPatching/Rulesets/JsonRuleset.cs b/Runtime/Generic/SassyPatching/Rulesets/JsonRuleset.cs index 86098ae..dd0f087 100644 --- a/Runtime/Generic/SassyPatching/Rulesets/JsonRuleset.cs +++ b/Runtime/Generic/SassyPatching/Rulesets/JsonRuleset.cs @@ -20,12 +20,23 @@ public bool Matches(string label) return true; } + public string[] Labels => null; + /// public ISelectable ConvertToSelectable(string type, string name, string jsonData) { return new JTokenSelectable(() => { }, JToken.Parse(jsonData), name, type); } + public bool CanIngestSelectable(ISelectable selectable) => selectable is JTokenSelectable; + + public bool CanGetAssetNameFromSelectableName => false; + + public string SelectableNameToAssetName(string selectableName) + { + throw new System.NotImplementedException(); + } + /// public INewAsset CreateNew(List dataValues) { diff --git a/Runtime/Missions/Rulesets/MissionRuleset.cs b/Runtime/Missions/Rulesets/MissionRuleset.cs index faa91d6..2de4e35 100644 --- a/Runtime/Missions/Rulesets/MissionRuleset.cs +++ b/Runtime/Missions/Rulesets/MissionRuleset.cs @@ -16,12 +16,22 @@ namespace PatchManager.Missions.Rulesets public class MissionRuleset : IPatcherRuleSet { /// - public bool Matches(string label) => label == "missions"; + public string[] Labels => new[] { "missions" }; /// public ISelectable ConvertToSelectable(string type, string name, string jsonData) => new MissionSelectable(JObject.Parse(jsonData)); + public bool CanIngestSelectable(ISelectable selectable) => selectable is MissionSelectable + { + Deleted: false + }; + + // TODO: Validate + public bool CanGetAssetNameFromSelectableName => true; + + public string SelectableNameToAssetName(string selectableName) => selectableName; + /// public INewAsset CreateNew(List dataValues) { diff --git a/Runtime/Missions/Selectables/ActionsSelectable.cs b/Runtime/Missions/Selectables/ActionsSelectable.cs index 5ff7cf5..0ac73e0 100644 --- a/Runtime/Missions/Selectables/ActionsSelectable.cs +++ b/Runtime/Missions/Selectables/ActionsSelectable.cs @@ -23,6 +23,16 @@ public sealed class ActionsSelectable : BaseSelectable /// public JArray Actions; + public void SetModified() + { + Selectable.SetModified(); + } + + public override bool WasModified => Selectable.WasModified; + public override void ClearModified() + { + } + private static string TrimTypeName(string typeName) { var comma = typeName.IndexOf(','); @@ -97,11 +107,12 @@ public override bool IsSameAs(ISelectable other) => other is ActionsSelectable actionsSelectable && actionsSelectable.Actions == Actions; /// - public override IModifiable OpenModification() => new JTokenModifiable(Actions, Selectable.SetModified); + public override IModifiable OpenModification() => new JTokenModifiable(Actions, SetModified); /// public override ISelectable AddElement(string elementType) { + SetModified(); var actualType = MissionsTypes.Actions[elementType]; var elementObject = new JObject() { @@ -112,7 +123,7 @@ public override ISelectable AddElement(string elementType) elementObject[key] = value; } - var selectable = new JTokenSelectable(Selectable.SetModified, elementObject, + var selectable = new JTokenSelectable(SetModified, elementObject, token => TrimTypeName(((JObject)token)!.Value()!), elementType); Children.Add(selectable); Classes.Add(elementType); diff --git a/Runtime/Missions/Selectables/ConditionSetSelectable.cs b/Runtime/Missions/Selectables/ConditionSetSelectable.cs index 3ea1497..5f9a4c9 100644 --- a/Runtime/Missions/Selectables/ConditionSetSelectable.cs +++ b/Runtime/Missions/Selectables/ConditionSetSelectable.cs @@ -29,6 +29,16 @@ public sealed class ConditionSetSelectable : BaseSelectable /// private JArray _children; + public void SetModified() + { + MissionSelectable.SetModified(); + } + + public override bool WasModified => MissionSelectable.WasModified; + public override void ClearModified() + { + } + /// /// Create a new condition set selectable /// @@ -97,11 +107,12 @@ public override bool IsSameAs(ISelectable other) => other is ConditionSetSelecta conditionSetSelectable.ConditionSet == ConditionSet; /// - public override IModifiable OpenModification() => new JTokenModifiable(ConditionSet, MissionSelectable.SetModified); + public override IModifiable OpenModification() => new JTokenModifiable(ConditionSet, SetModified); /// public override ISelectable AddElement(string elementType) { + SetModified(); var conditionType = MissionsTypes.Conditions[elementType]; var conditionObject = new JObject() { @@ -121,7 +132,7 @@ public override ISelectable AddElement(string elementType) } else { - var selectable = new JTokenSelectable(MissionSelectable.SetModified, conditionObject, + var selectable = new JTokenSelectable(SetModified, conditionObject, "scriptableCondition", "scriptableCondition"); Children.Add(selectable); return selectable; diff --git a/Runtime/Missions/Selectables/ContentBranchSelectable.cs b/Runtime/Missions/Selectables/ContentBranchSelectable.cs index 03bbcd5..60d1fbc 100644 --- a/Runtime/Missions/Selectables/ContentBranchSelectable.cs +++ b/Runtime/Missions/Selectables/ContentBranchSelectable.cs @@ -26,6 +26,15 @@ public sealed class ContentBranchSelectable : BaseSelectable /// private ActionsSelectable _actionsSelectable; + public void SetModified() + { + Selectable.SetModified(); + } + public override bool WasModified => Selectable.WasModified; + public override void ClearModified() + { + } + /// /// Creates a new content branch selectable. /// @@ -61,11 +70,12 @@ public override bool IsSameAs(ISelectable other) => other is ContentBranchSelect contentBranchSelectable.ContentBranch == ContentBranch; /// - public override IModifiable OpenModification() => new JTokenModifiable(ContentBranch, Selectable.SetModified); + public override IModifiable OpenModification() => new JTokenModifiable(ContentBranch, SetModified); /// public override ISelectable AddElement(string elementType) { + SetModified(); var result = _actionsSelectable.AddElement(elementType); Children.Add(result); Classes.Add(elementType); diff --git a/Runtime/Missions/Selectables/ContentBranchesSelectable.cs b/Runtime/Missions/Selectables/ContentBranchesSelectable.cs index fa29d19..7754211 100644 --- a/Runtime/Missions/Selectables/ContentBranchesSelectable.cs +++ b/Runtime/Missions/Selectables/ContentBranchesSelectable.cs @@ -22,6 +22,16 @@ public sealed class ContentBranchesSelectable : BaseSelectable /// public JArray ContentBranches; + public void SetModified() + { + Selectable.SetModified(); + } + public override bool WasModified => Selectable.WasModified; + public override void ClearModified() + { + + } + /// /// Create a new ContentBranchesSelectable /// @@ -73,11 +83,12 @@ public override bool IsSameAs(ISelectable other) => other is ContentBranchesSele contentBranchesSelectable.ContentBranches == ContentBranches; /// - public override IModifiable OpenModification() => new JTokenModifiable(ContentBranches, Selectable.SetModified); + public override IModifiable OpenModification() => new JTokenModifiable(ContentBranches, SetModified); /// public override ISelectable AddElement(string elementType) { + SetModified(); var branch = new MissionContentBranch { ID = elementType diff --git a/Runtime/Missions/Selectables/MissionRewardSelectable.cs b/Runtime/Missions/Selectables/MissionRewardSelectable.cs index 9187678..d093b5a 100644 --- a/Runtime/Missions/Selectables/MissionRewardSelectable.cs +++ b/Runtime/Missions/Selectables/MissionRewardSelectable.cs @@ -27,6 +27,15 @@ public sealed class MissionRewardSelectable : BaseSelectable /// private JArray _missionRewardDefinitions; + public void SetModified() + { + MissionSelectable.SetModified(); + } + public override bool WasModified => MissionSelectable.WasModified; + public override void ClearModified() + { + } + /// /// Creates a new mission reward selectable. /// @@ -84,17 +93,18 @@ public override bool IsSameAs(ISelectable other) => other is MissionRewardSelect /// public override IModifiable OpenModification() => - new JTokenModifiable(MissionReward, MissionSelectable.SetModified); + new JTokenModifiable(MissionReward, SetModified); /// public override ISelectable AddElement(string elementType) { + SetModified(); var obj = new JObject { ["MissionRewardType"] = elementType }; Classes.Add(elementType); - var selectable = new JTokenSelectable(MissionSelectable.SetModified, obj, + var selectable = new JTokenSelectable(SetModified, obj, token => ((JObject)token)["MissionRewardType"]!.Value(), elementType); Children.Add(selectable); _missionRewardDefinitions.Add(obj); diff --git a/Runtime/Missions/Selectables/MissionSelectable.cs b/Runtime/Missions/Selectables/MissionSelectable.cs index 6e1dfa9..d80718c 100644 --- a/Runtime/Missions/Selectables/MissionSelectable.cs +++ b/Runtime/Missions/Selectables/MissionSelectable.cs @@ -13,10 +13,8 @@ namespace PatchManager.Missions.Selectables /// public sealed class MissionSelectable : BaseSelectable { -#pragma warning disable CS0414 // Field is assigned but its value is never used private bool _modified; -#pragma warning restore CS0414 // Field is assigned but its value is never used - private bool _deleted; + public bool Deleted; /// /// Marks this part selectable as having been modified any level down @@ -32,7 +30,7 @@ public void SetModified() public void SetDeleted() { SetModified(); - _deleted = true; + Deleted = true; } /// @@ -101,11 +99,17 @@ public override bool IsSameAs(ISelectable other) => "You cannot add elements to the main body of the mission, try using ContentBranches, or missionStages for that"); /// - public override string Serialize() => _deleted ? "" : MissionObject.ToString(); + public override string Serialize() => Deleted ? "" : MissionObject.ToString(); /// public override DataValue GetValue() => DataValue.FromJToken(MissionObject); + public override bool WasModified => _modified; + public override void ClearModified() + { + _modified = false; + } + /// public override string ElementType => "missions"; } diff --git a/Runtime/Missions/Selectables/StageSelectable.cs b/Runtime/Missions/Selectables/StageSelectable.cs index ad7c981..fbb9f34 100644 --- a/Runtime/Missions/Selectables/StageSelectable.cs +++ b/Runtime/Missions/Selectables/StageSelectable.cs @@ -26,6 +26,15 @@ public sealed class StageSelectable : BaseSelectable private int _conditionIndex = -1; + public void SetModified() + { + MissionSelectable.SetModified(); + } + public override bool WasModified => MissionSelectable.WasModified; + public override void ClearModified() + { + } + /// /// Create a new stage selectable. /// @@ -97,11 +106,12 @@ public override bool IsSameAs(ISelectable other) => other is StageSelectable stageSelectable && stageSelectable.StageObject == StageObject; /// - public override IModifiable OpenModification() => new JTokenModifiable(StageObject, MissionSelectable.SetModified); + public override IModifiable OpenModification() => new JTokenModifiable(StageObject, SetModified); /// public override ISelectable AddElement(string elementType) { + SetModified(); var conditionType = MissionsTypes.Conditions[elementType]; // var conditionObject = JObject.FromObject(Activator.CreateInstance(conditionType)); var conditionObject = new JObject(); @@ -126,7 +136,7 @@ public override ISelectable AddElement(string elementType) else { var selectable = new JTokenSelectable( - MissionSelectable.SetModified, + SetModified, conditionObject, "scriptableCondition", "scriptableCondition" diff --git a/Runtime/Missions/Selectables/StagesSelectable.cs b/Runtime/Missions/Selectables/StagesSelectable.cs index 3656ed0..8646426 100644 --- a/Runtime/Missions/Selectables/StagesSelectable.cs +++ b/Runtime/Missions/Selectables/StagesSelectable.cs @@ -22,6 +22,15 @@ public sealed class StagesSelectable : BaseSelectable /// public JArray Stages; + public void SetModified() + { + MissionSelectable.SetModified(); + } + public override bool WasModified => MissionSelectable.WasModified; + public override void ClearModified() + { + } + /// /// Creates a new stages selectable. /// @@ -73,11 +82,12 @@ public override bool IsSameAs(ISelectable other) => other is StagesSelectable stagesSelectable && stagesSelectable.Stages == Stages; /// - public override IModifiable OpenModification() => new JTokenModifiable(Stages, MissionSelectable.SetModified); + public override IModifiable OpenModification() => new JTokenModifiable(Stages, SetModified); /// public override ISelectable AddElement(string elementType) { + SetModified(); var num = long.Parse(elementType[1..]); var obj = new MissionStage { diff --git a/Runtime/Parts/Rulesets/PartsRuleset.cs b/Runtime/Parts/Rulesets/PartsRuleset.cs index 2ac6b6f..3b6ee87 100644 --- a/Runtime/Parts/Rulesets/PartsRuleset.cs +++ b/Runtime/Parts/Rulesets/PartsRuleset.cs @@ -21,8 +21,8 @@ namespace PatchManager.Parts.Rulesets [PatcherRuleset("parts", "parts_data")] public class PartsRuleset : IPatcherRuleSet { - /// - public bool Matches(string label) => label == "parts_data"; + + public string[] Labels => new[] { "parts_data" }; /// /// Converts the part json to an ISelectable following this ruleset @@ -35,6 +35,16 @@ public ISelectable ConvertToSelectable(string type, string name, string jsonData { return new PartSelectable(jsonData); } + + public bool CanIngestSelectable(ISelectable selectable) => selectable is PartSelectable + { + Deleted: false + }; + + public bool CanGetAssetNameFromSelectableName => true; + + public string SelectableNameToAssetName(string selectableName) => selectableName; + /// /// /// Create a new part asset diff --git a/Runtime/Parts/Selectables/DataEngineSelectable.cs b/Runtime/Parts/Selectables/DataEngineSelectable.cs index db7c9ba..2f6d170 100644 --- a/Runtime/Parts/Selectables/DataEngineSelectable.cs +++ b/Runtime/Parts/Selectables/DataEngineSelectable.cs @@ -27,6 +27,15 @@ public sealed class DataEngineSelectable : BaseSelectable /// public readonly PartSelectable Selectable; + public void SetModified() + { + Selectable.SetModified(); + } + public override bool WasModified => Selectable.WasModified; + public override void ClearModified() + { + } + /// /// Initialize the selectable /// @@ -109,18 +118,19 @@ public override bool IsSameAs(ISelectable other) => SerializedData == dataEngineSelectable.SerializedData; /// - public override IModifiable OpenModification() => new JTokenModifiable(SerializedData, Selectable.SetModified); + public override IModifiable OpenModification() => new JTokenModifiable(SerializedData, SetModified); /// public override ISelectable AddElement(string elementType) { + SetModified(); var engineModeData = new Data_Engine.EngineMode() { engineID = elementType }; var json = JObject.FromObject(engineModeData); ((JArray)SerializedData["engineModes"]).Add(json); - return new JTokenSelectable(Selectable.SetModified, json, mode => mode["engineID"].Value(), + return new JTokenSelectable(SetModified, json, mode => mode["engineID"].Value(), "engine_mode"); } diff --git a/Runtime/Parts/Selectables/ModuleSelectable.cs b/Runtime/Parts/Selectables/ModuleSelectable.cs index 2bdd429..6ba8926 100644 --- a/Runtime/Parts/Selectables/ModuleSelectable.cs +++ b/Runtime/Parts/Selectables/ModuleSelectable.cs @@ -27,6 +27,15 @@ public sealed class ModuleSelectable : BaseSelectable private Dictionary _dataIndices; + public void SetModified() + { + Selectable.SetModified(); + } + public override bool WasModified => Selectable.WasModified; + public override void ClearModified() + { + } + /// public ModuleSelectable(JToken token, PartSelectable selectable) { @@ -58,7 +67,7 @@ private ISelectable GetSelectable(JObject moduleData) { return (ISelectable)Activator.CreateInstance(adapterType, moduleData, this); } - return new JTokenSelectable(Selectable.SetModified, moduleData["DataObject"], moduleData["Name"].Value()); + return new JTokenSelectable(SetModified, moduleData["DataObject"], moduleData["Name"].Value()); } /// @@ -94,12 +103,13 @@ public override bool IsSameAs(ISelectable other) => /// public override IModifiable OpenModification() { - return new JTokenModifiable(SerializedData, Selectable.SetModified); + return new JTokenModifiable(SerializedData, SetModified); } /// public override ISelectable AddElement(string elementType) { + SetModified(); if (!PartsUtilities.DataModules.TryGetValue(elementType, out var dataModuleType)) { throw new Exception($"Unknown data module {elementType}"); diff --git a/Runtime/Parts/Selectables/PartSelectable.cs b/Runtime/Parts/Selectables/PartSelectable.cs index c8d524d..6f040e1 100644 --- a/Runtime/Parts/Selectables/PartSelectable.cs +++ b/Runtime/Parts/Selectables/PartSelectable.cs @@ -15,24 +15,32 @@ namespace PatchManager.Parts.Selectables /// public sealed class PartSelectable : BaseSelectable { + private bool _needToReconstruct; private bool _modified; - private bool _deleted; + public bool Deleted; /// /// Marks this part selectable as having been modified any level down /// public void SetModified() { + _needToReconstruct = true; _modified = true; } + public override bool WasModified => _modified; + public override void ClearModified() + { + _modified = false; + } + /// /// Marks this part as goneso /// public void SetDeleted() { SetModified(); - _deleted = true; + Deleted = true; } private readonly string _originalData; @@ -151,7 +159,7 @@ public override ISelectable AddElement(string elementType) /// - public override string Serialize() => _modified ? _deleted ? "" : JObject.ToString() : _originalData; + public override string Serialize() => _needToReconstruct ? Deleted ? "" : JObject.ToString() : _originalData; /// public override DataValue GetValue() => OpenModification().Get(); diff --git a/Runtime/Parts/Selectables/ResourceContainersSelectable.cs b/Runtime/Parts/Selectables/ResourceContainersSelectable.cs index 0668883..e090eb7 100644 --- a/Runtime/Parts/Selectables/ResourceContainersSelectable.cs +++ b/Runtime/Parts/Selectables/ResourceContainersSelectable.cs @@ -14,6 +14,15 @@ public sealed class ResourceContainersSelectable : BaseSelectable private PartSelectable _selectable; private Dictionary _resourceIndices; + public void SetModified() + { + _selectable.SetModified(); + } + public override bool WasModified => _selectable.WasModified; + public override void ClearModified() + { + } + internal ResourceContainersSelectable(JArray containers, PartSelectable selectable) { _containers = containers; @@ -68,7 +77,7 @@ other is ResourceContainersSelectable resourceContainersSelectable && /// public override ISelectable AddElement(string elementType) { - _selectable.SetModified(); + SetModified(); var obj = JObject.FromObject(new { name = elementType, diff --git a/Runtime/PatchManager.Resources/Rulesets/ResourceRuleset.cs b/Runtime/PatchManager.Resources/Rulesets/ResourceRuleset.cs index 4a83f71..72f942e 100644 --- a/Runtime/PatchManager.Resources/Rulesets/ResourceRuleset.cs +++ b/Runtime/PatchManager.Resources/Rulesets/ResourceRuleset.cs @@ -14,8 +14,8 @@ namespace PatchManager.Resources.Rulesets [PatcherRuleset("resources", "resources")] public class ResourceRuleset : IPatcherRuleSet { - /// - public bool Matches(string label) => label == "resources"; + + public string[] Labels => new string[] { "resources" }; /// public ISelectable ConvertToSelectable(string type, string name, string jsonData) @@ -28,6 +28,22 @@ public ISelectable ConvertToSelectable(string type, string name, string jsonData return new ResourceSelectable(jsonData); } + public bool CanIngestSelectable(ISelectable selectable) => selectable is RecipeSelectable + { + Deleted: false + } or ResourceSelectable + { + Deleted: false + }; + + // TODO: Validate + public bool CanGetAssetNameFromSelectableName => false; + + public string SelectableNameToAssetName(string selectableName) + { + throw new System.NotImplementedException(); + } + /// public INewAsset CreateNew(List dataValues) { diff --git a/Runtime/PatchManager.Resources/Rulesets/ResourceUnitRuleset.cs b/Runtime/PatchManager.Resources/Rulesets/ResourceUnitRuleset.cs index 7d112f9..22a6de7 100644 --- a/Runtime/PatchManager.Resources/Rulesets/ResourceUnitRuleset.cs +++ b/Runtime/PatchManager.Resources/Rulesets/ResourceUnitRuleset.cs @@ -14,14 +14,24 @@ namespace PatchManager.Resources.Rulesets [PatcherRuleset("resource_units","resource_units")] public class ResourceUnitRuleset : IPatcherRuleSet { - public bool Matches(string label) => label == "resource_units"; + + public string[] Labels => new [] {"resource_units"}; public ISelectable ConvertToSelectable(string type, string name, string jsonData) => new JTokenSelectable(() => { }, JObject.Parse(jsonData), name, type); + public bool CanIngestSelectable(ISelectable selectable) => selectable is JTokenSelectable; + + public bool CanGetAssetNameFromSelectableName => false; + private static int _globallyIncrementingId = 0; - + + public string SelectableNameToAssetName(string selectableName) + { + throw new System.NotImplementedException(); + } + public INewAsset CreateNew(List dataValues) { var id = _globallyIncrementingId++; diff --git a/Runtime/PatchManager.Resources/Selectables/RecipeSelectable.cs b/Runtime/PatchManager.Resources/Selectables/RecipeSelectable.cs index 5465f44..24dab12 100644 --- a/Runtime/PatchManager.Resources/Selectables/RecipeSelectable.cs +++ b/Runtime/PatchManager.Resources/Selectables/RecipeSelectable.cs @@ -12,9 +12,15 @@ namespace PatchManager.Resources.Selectables /// public sealed class RecipeSelectable : BaseSelectable { - + private bool _needToReconstruct; private bool _modified; - private bool _deleted; + public override bool WasModified => _modified; + public override void ClearModified() + { + _modified = false; + } + + public bool Deleted; private readonly string _originalData; internal readonly JObject JObject; internal readonly JArray Ingredients; @@ -25,6 +31,7 @@ public sealed class RecipeSelectable : BaseSelectable /// public void SetModified() { + _needToReconstruct = true; _modified = true; } @@ -34,7 +41,7 @@ public void SetModified() public void SetDeleted() { SetModified(); - _deleted = true; + Deleted = true; } internal RecipeSelectable(string data) @@ -110,7 +117,7 @@ public override ISelectable AddElement(string elementType) } /// - public override string Serialize() => _modified ? _deleted ? "" : JObject.ToString() : _originalData; + public override string Serialize() => _needToReconstruct ? Deleted ? "" : JObject.ToString() : _originalData; /// public override DataValue GetValue() => OpenModification().Get(); diff --git a/Runtime/PatchManager.Resources/Selectables/ResourceSelectable.cs b/Runtime/PatchManager.Resources/Selectables/ResourceSelectable.cs index 45d37a9..1e0a74b 100644 --- a/Runtime/PatchManager.Resources/Selectables/ResourceSelectable.cs +++ b/Runtime/PatchManager.Resources/Selectables/ResourceSelectable.cs @@ -12,8 +12,15 @@ namespace PatchManager.Resources.Selectables /// public class ResourceSelectable : BaseSelectable { + private bool _needToReconstruct; private bool _modified; - private bool _deleted; + public override bool WasModified => _modified; + public override void ClearModified() + { + _modified = false; + } + + public bool Deleted; /// @@ -21,6 +28,7 @@ public class ResourceSelectable : BaseSelectable /// public void SetModified() { + _needToReconstruct = true; _modified = true; } @@ -30,7 +38,7 @@ public void SetModified() public void SetDeleted() { SetModified(); - _deleted = true; + Deleted = true; } private readonly string _originalData; @@ -81,7 +89,7 @@ public override bool MatchesClass(string @class, out DataValue classValue) public override ISelectable AddElement(string elementType) => throw new InvalidOperationException(); /// - public override string Serialize() => _modified ? _deleted ? "" : JObject.ToString() : _originalData; + public override string Serialize() => _needToReconstruct ? Deleted ? "" : JObject.ToString() : _originalData; /// public override DataValue GetValue() => OpenModification().Get(); diff --git a/Runtime/Planets/Rulesets/AtmosphereOverrideRuleset.cs b/Runtime/Planets/Rulesets/AtmosphereOverrideRuleset.cs index 6de348f..78ed150 100644 --- a/Runtime/Planets/Rulesets/AtmosphereOverrideRuleset.cs +++ b/Runtime/Planets/Rulesets/AtmosphereOverrideRuleset.cs @@ -12,11 +12,20 @@ namespace PatchManager.Planets.Rulesets [PatcherRuleset("atmosphere-override","atmosphere_overrides")] public class AtmosphereOverrideRuleset : IPatcherRuleSet { - public bool Matches(string label) => label == "atmosphere_overrides"; + public string[] Labels => new[] { "atmosphere_overrides" }; public ISelectable ConvertToSelectable(string type, string name, string jsonData) => new AtmosphereOverrideSelectable(JObject.Parse(jsonData)); + public bool CanIngestSelectable(ISelectable selectable) => selectable is AtmosphereOverrideSelectable + { + Deleted: false + }; + + public bool CanGetAssetNameFromSelectableName => true; + + public string SelectableNameToAssetName(string selectableName) => $"atmosphere_override_{selectableName.ToLowerInvariant()}"; + public INewAsset CreateNew(List dataValues) { var planetName = dataValues[0].String.ToLowerInvariant(); diff --git a/Runtime/Planets/Rulesets/CelestialBodyRuleset.cs b/Runtime/Planets/Rulesets/CelestialBodyRuleset.cs index 2f92d7b..5064765 100644 --- a/Runtime/Planets/Rulesets/CelestialBodyRuleset.cs +++ b/Runtime/Planets/Rulesets/CelestialBodyRuleset.cs @@ -12,11 +12,20 @@ namespace PatchManager.Planets.Rulesets [PatcherRuleset("body","celestial_bodies")] public class CelestialBodyRuleset : IPatcherRuleSet { - public bool Matches(string label) => label == "celestial_bodies"; + public string[] Labels => new[] { "celestial_bodies" }; public ISelectable ConvertToSelectable(string type, string name, string jsonData) => new CelestialBodySelectable(JObject.Parse(jsonData)); + public bool CanIngestSelectable(ISelectable selectable) => selectable is CelestialBodySelectable + { + Deleted: false + }; + + public bool CanGetAssetNameFromSelectableName => true; + + public string SelectableNameToAssetName(string selectableName) => selectableName; + public INewAsset CreateNew(List dataValues) { var bodyName = dataValues[0].String; @@ -28,7 +37,7 @@ public INewAsset CreateNew(List dataValues) bodyName = bodyName } }; - return new NewGenericAsset("celestial_bodies", "bodyName", + return new NewGenericAsset("celestial_bodies", bodyName, new CelestialBodySelectable(JObject.FromObject(core))); } } diff --git a/Runtime/Planets/Rulesets/GalaxyRuleset.cs b/Runtime/Planets/Rulesets/GalaxyRuleset.cs index b927bcb..a7db573 100644 --- a/Runtime/Planets/Rulesets/GalaxyRuleset.cs +++ b/Runtime/Planets/Rulesets/GalaxyRuleset.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using JetBrains.Annotations; using KSP.Sim; using Newtonsoft.Json.Linq; using PatchManager.SassyPatching; @@ -13,8 +14,8 @@ namespace PatchManager.Planets.Rulesets [PatcherRuleset("galaxy", "GalaxyDefinition_Default")] public class GalaxyRuleset : IPatcherRuleSet { - /// - public bool Matches(string label) => true; + + public string[] Labels => null; /// public ISelectable ConvertToSelectable(string type, string name, string jsonData) @@ -25,6 +26,16 @@ public ISelectable ConvertToSelectable(string type, string name, string jsonData return new GalaxySelectable(obj, type); } + + public bool CanIngestSelectable(ISelectable selectable) => selectable is GalaxySelectable + { + Deleted: false + }; + + public bool CanGetAssetNameFromSelectableName => false; + + public string SelectableNameToAssetName(string selectableName) => $"GalaxyDefinition_{selectableName}"; + /// public INewAsset CreateNew(List dataValues) { @@ -36,7 +47,7 @@ public INewAsset CreateNew(List dataValues) Version = version, CelestialBodies = new List() }; - return new NewGenericAsset(name, name, new GalaxySelectable(JObject.FromObject(def), name)); + return new NewGenericAsset($"GalaxyDefinition_{name}", $"GalaxyDefinition_{name}", new GalaxySelectable(JObject.FromObject(def), name)); } } } \ No newline at end of file diff --git a/Runtime/Planets/Rulesets/VolumeCloudOverrideRuleset.cs b/Runtime/Planets/Rulesets/VolumeCloudOverrideRuleset.cs index 118457f..bc21c3f 100644 --- a/Runtime/Planets/Rulesets/VolumeCloudOverrideRuleset.cs +++ b/Runtime/Planets/Rulesets/VolumeCloudOverrideRuleset.cs @@ -12,10 +12,18 @@ namespace PatchManager.Planets.Rulesets [PatcherRuleset("volume-cloud-override","volume_cloud_overrides")] public class VolumeCloudOverrideRuleset : IPatcherRuleSet { - public bool Matches(string label) => label == "volume_cloud_overrides"; - + public string[] Labels => new [] {"volume_cloud_overrides"}; public ISelectable ConvertToSelectable(string type, string name, string jsonData) => new VolumeCloudSelectable(JObject.Parse(jsonData)); + public bool CanIngestSelectable(ISelectable selectable) => selectable is VolumeCloudSelectable + { + Deleted: false + }; + + public bool CanGetAssetNameFromSelectableName => true; + + public string SelectableNameToAssetName(string selectableName) => $"volume_cloud_override_{selectableName.ToLowerInvariant()}"; + public INewAsset CreateNew(List dataValues) { var planetName = dataValues[0].String.ToLowerInvariant(); diff --git a/Runtime/Planets/Selectables/AtmosphereOverrideSelectable.cs b/Runtime/Planets/Selectables/AtmosphereOverrideSelectable.cs index ab1d959..9c06699 100644 --- a/Runtime/Planets/Selectables/AtmosphereOverrideSelectable.cs +++ b/Runtime/Planets/Selectables/AtmosphereOverrideSelectable.cs @@ -9,10 +9,13 @@ namespace PatchManager.Planets.Selectables { public sealed class AtmosphereOverrideSelectable : BaseSelectable { - #pragma warning disable CS0414 // Field is assigned but its value is never used private bool _modified; - #pragma warning restore CS0414 // Field is assigned but its value is never used - private bool _deleted; + public bool Deleted; + public override bool WasModified => _modified; + public override void ClearModified() + { + _modified = false; + } /// /// Marks this part selectable as having been modified any level down @@ -28,7 +31,7 @@ public void SetModified() public void SetDeleted() { SetModified(); - _deleted = true; + Deleted = true; } public readonly JObject AtmosphereOverrideObject; @@ -68,7 +71,7 @@ public override bool IsSameAs(ISelectable other) => public override ISelectable AddElement(string elementType) => throw new Exception("Adding elements to atmosphere overrides is not allowed"); /// - public override string Serialize() => _deleted ? "" : AtmosphereOverrideObject.ToString(); + public override string Serialize() => Deleted ? "" : AtmosphereOverrideObject.ToString(); /// public override DataValue GetValue() => DataValue.FromJToken(AtmosphereOverrideObject); diff --git a/Runtime/Planets/Selectables/CelestialBodySelectable.cs b/Runtime/Planets/Selectables/CelestialBodySelectable.cs index de17918..1d5e196 100644 --- a/Runtime/Planets/Selectables/CelestialBodySelectable.cs +++ b/Runtime/Planets/Selectables/CelestialBodySelectable.cs @@ -9,10 +9,14 @@ namespace PatchManager.Planets.Selectables { public sealed class CelestialBodySelectable : BaseSelectable { - #pragma warning disable CS0414 // Field is assigned but its value is never used private bool _modified; - #pragma warning restore CS0414 // Field is assigned but its value is never used - private bool _deleted; + public override bool WasModified => _modified; + public override void ClearModified() + { + _modified = false; + } + + public bool Deleted; /// /// Marks this part selectable as having been modified any level down @@ -28,7 +32,7 @@ public void SetModified() public void SetDeleted() { SetModified(); - _deleted = true; + Deleted = true; } public JObject CelestialBodyObject; @@ -67,6 +71,7 @@ public override bool IsSameAs(ISelectable other) => other is CelestialBodySelect public override ISelectable AddElement(string elementType) { + SetModified(); var obj = new JObject(); DataObject[elementType] = obj; var n = new JTokenSelectable(SetModified, obj, elementType); @@ -74,7 +79,7 @@ public override ISelectable AddElement(string elementType) return n; } - public override string Serialize() => _deleted ? "" : CelestialBodyObject.ToString(); + public override string Serialize() => Deleted ? "" : CelestialBodyObject.ToString(); public override DataValue GetValue() => DataValue.FromJToken(DataObject); diff --git a/Runtime/Planets/Selectables/GalaxySelectable.cs b/Runtime/Planets/Selectables/GalaxySelectable.cs index b046f0e..d97e3df 100644 --- a/Runtime/Planets/Selectables/GalaxySelectable.cs +++ b/Runtime/Planets/Selectables/GalaxySelectable.cs @@ -10,10 +10,14 @@ namespace PatchManager.Planets.Selectables { public sealed class GalaxySelectable : BaseSelectable { - #pragma warning disable CS0414 // Field is assigned but its value is never used private bool _modified; - #pragma warning restore CS0414 // Field is assigned but its value is never used - private bool _deleted; + public override bool WasModified => _modified; + public override void ClearModified() + { + _modified = false; + } + + public bool Deleted; /// /// Marks this part selectable as having been modified any level down @@ -29,7 +33,7 @@ public void SetModified() public void SetDeleted() { SetModified(); - _deleted = true; + Deleted = true; } /// @@ -96,6 +100,7 @@ public override bool IsSameAs(ISelectable other) => /// public override ISelectable AddElement(string elementType) { + SetModified(); var obj = new SerializedCelestialBody { GUID = elementType, @@ -110,7 +115,7 @@ public override ISelectable AddElement(string elementType) } /// - public override string Serialize() => _deleted ? "" : GalaxyObject.ToString(); + public override string Serialize() => Deleted ? "" : GalaxyObject.ToString(); /// public override DataValue GetValue() => DataValue.FromJToken(GalaxyObject); diff --git a/Runtime/Planets/Selectables/VolumeCloudSelectable.cs b/Runtime/Planets/Selectables/VolumeCloudSelectable.cs index 9da1b0e..b0e38f5 100644 --- a/Runtime/Planets/Selectables/VolumeCloudSelectable.cs +++ b/Runtime/Planets/Selectables/VolumeCloudSelectable.cs @@ -10,10 +10,14 @@ namespace PatchManager.Planets.Selectables { public sealed class VolumeCloudSelectable : BaseSelectable { - #pragma warning disable CS0414 // Field is assigned but its value is never used private bool _modified; - #pragma warning restore CS0414 // Field is assigned but its value is never used - private bool _deleted; + public override bool WasModified => false; + public override void ClearModified() + { + _modified = false; + } + + public bool Deleted; /// /// Marks this part selectable as having been modified any level down/// @@ -29,7 +33,7 @@ public void SetModified() public void SetDeleted() { SetModified(); - _deleted = true; + Deleted = true; } public readonly JObject VolumeCloudOverrideObject; @@ -86,6 +90,7 @@ public override bool IsSameAs(ISelectable other) => public override ISelectable AddElement(string elementType) { + SetModified(); var newLayer = new CloudsDataOverride { layerName = elementType @@ -98,7 +103,7 @@ public override ISelectable AddElement(string elementType) return selectable; } /// - public override string Serialize() => _deleted ? "" : VolumeCloudOverrideObject.ToString(); + public override string Serialize() => Deleted ? "" : VolumeCloudOverrideObject.ToString(); /// public override DataValue GetValue() => DataValue.FromJToken(VolumeCloudOverrideObject); diff --git a/Runtime/SassyPatching/BaseSelectable.cs b/Runtime/SassyPatching/BaseSelectable.cs index aed0a55..a26fd31 100644 --- a/Runtime/SassyPatching/BaseSelectable.cs +++ b/Runtime/SassyPatching/BaseSelectable.cs @@ -15,7 +15,7 @@ public abstract class BaseSelectable : ISelectable /// The children of this selectable /// public abstract List Children { get; } - + /// public List SelectEverything() => Children; @@ -69,5 +69,9 @@ public abstract class BaseSelectable : ISelectable /// public abstract DataValue GetValue(); + + public abstract bool WasModified { get; } + + public abstract void ClearModified(); } } \ No newline at end of file diff --git a/Runtime/SassyPatching/Execution/SassyGenerator.cs b/Runtime/SassyPatching/Execution/SassyGenerator.cs index 415df6c..b4dce84 100644 --- a/Runtime/SassyPatching/Execution/SassyGenerator.cs +++ b/Runtime/SassyPatching/Execution/SassyGenerator.cs @@ -1,12 +1,12 @@ using System.Collections.Generic; +using PatchManager.SassyPatching.Interfaces; using PatchManager.SassyPatching.Nodes.Attributes; using PatchManager.SassyPatching.Nodes.Statements; -using PatchManager.Shared.Interfaces; using UniLinq; namespace PatchManager.SassyPatching.Execution { - public class SassyGenerator : ITextAssetGenerator + public class SassyGenerator {// This is a snapshot of the environment before the patch was registered, note it will still reference the same global environment as its file, as that is only mutated by function declarations // Same w/ universe environment, as that should only contain stage definitions and such private Environment _environmentSnapshot; @@ -43,12 +43,12 @@ internal SassyGenerator(Environment environmentSnapshot, SelectionBlock rootSele /// public ulong Priority { get; } - public string Create(out string label, out string name) + public ISelectable Create(out string label, out string name) { var val = _rootSelectionBlock.ExecuteCreation(_environmentSnapshot, _arguments); label = val.Label; name = val.Name; - return val.Text; + return val.Selectable; } } } \ No newline at end of file diff --git a/Runtime/SassyPatching/Execution/SassyTextPatcher.cs b/Runtime/SassyPatching/Execution/SassyTextPatcher.cs index 344febf..91ef091 100644 --- a/Runtime/SassyPatching/Execution/SassyTextPatcher.cs +++ b/Runtime/SassyPatching/Execution/SassyTextPatcher.cs @@ -1,7 +1,12 @@ -using PatchManager.SassyPatching.Nodes.Attributes; +using System; +using Castle.Core.Internal; +using JetBrains.Annotations; +using PatchManager.SassyPatching.Exceptions; +using PatchManager.SassyPatching.Interfaces; +using PatchManager.SassyPatching.Nodes.Attributes; +using PatchManager.SassyPatching.Nodes.Selectors; using PatchManager.SassyPatching.Nodes.Statements; using PatchManager.SassyPatching.Utility; -using PatchManager.Shared.Interfaces; using UniLinq; namespace PatchManager.SassyPatching.Execution @@ -9,46 +14,66 @@ namespace PatchManager.SassyPatching.Execution /// /// This is the class that all sassy patches get converted to /// - public class SassyTextPatcher : ITextPatcher + public class SassyTextPatcher { // This is a snapshot of the environment before the patch was registered, note it will still reference the same global environment as its file, as that is only mutated by function declarations // Same w/ universe environment, as that should only contain stage definitions and such private Environment _environmentSnapshot; private SelectionBlock _rootSelectionBlock; - + public IPatcherRuleSet RuleSet; + [CanBeNull] public string AssetName; internal SassyTextPatcher(Environment environmentSnapshot, SelectionBlock rootSelectionBlock) { _environmentSnapshot = environmentSnapshot; _rootSelectionBlock = rootSelectionBlock; - // var stage = rootSelectionBlock.Attributes.OfType().FirstOrDefault(); - // if (stage == null) - // { - // Priority = ulong.MaxValue; - // } - // else - // { - // var global = environmentSnapshot.GlobalEnvironment; - // string stageName; - // if (stage.Stage.Contains(':')) - // { - // stageName = stage.Stage; - // } - // else - // { - // stageName = global.ModGuid + ":" + stage.Stage; - // } - // - // // If this errors then we don't register the patch, but we should give a more friendly thing to this at some point - // Priority = global.Universe.AllStages[stageName]; - // } OriginalGuid = environmentSnapshot.GlobalEnvironment.ModGuid; PriorityString = rootSelectionBlock.Attributes.OfType().FirstOrDefault() is { } runAtStageAttribute ? runAtStageAttribute.Stage.Interpolate(environmentSnapshot) : environmentSnapshot.GlobalEnvironment.ModGuid; + RecursivelyFindRuleSet(rootSelectionBlock.Selector); + if (RuleSet == null) + { + throw new InterpreterException(rootSelectionBlock.Coordinate, + "Selection block does not have a ruleset for it"); + } + } + + private void RecursivelyFindRuleSet(Selector selector) + { + if (selector is IntersectionSelector intersectionSelector) + { + if (intersectionSelector.Selectors[0] is RulesetSelector rulesetSelector) + { + if (!Universe.RuleSets.TryGetValue(rulesetSelector.RulesetName, out var ruleSet)) + { + throw new InterpreterException(rulesetSelector.Coordinate, + $"Ruleset {rulesetSelector.RulesetName} does not exist!"); + } + RuleSet = ruleSet; + if (RuleSet.CanGetAssetNameFromSelectableName && intersectionSelector.Selectors[1] is NameSelector nameSelector) + { + if (nameSelector.NamePattern.Contains('*') || nameSelector.NamePattern.Contains('?')) + { + return; + } + + AssetName = RuleSet.SelectableNameToAssetName(nameSelector.NamePattern); + } + } + else + { + RecursivelyFindRuleSet(intersectionSelector.Selectors[0]); + } + } else if (selector is ChildSelector childSelector) + { + RecursivelyFindRuleSet(childSelector.Parent); + } } + + public string OriginalGuid { get; } public string PriorityString { get; } @@ -57,9 +82,29 @@ internal SassyTextPatcher(Environment environmentSnapshot, SelectionBlock rootSe public ulong Priority { get; set; } /// - public bool TryPatch(string patchType, string name, ref string patchData) + public bool TryPatch(string patchType, string name, ref ISelectable previousSelectable, out bool shouldStop) + { + previousSelectable.ClearModified(); + shouldStop = false; + if (RuleSet.CanIngestSelectable(previousSelectable)) + { + return _rootSelectionBlock.ExecuteFresh(_environmentSnapshot, previousSelectable); + } + var serialized = previousSelectable.Serialize(); + if (serialized.IsNullOrEmpty()) + { + shouldStop = true; + return false; + } + + previousSelectable = RuleSet.ConvertToSelectable(patchType, name, serialized); + return _rootSelectionBlock.ExecuteFresh(_environmentSnapshot, previousSelectable); + } + + public bool TryPatchBegin(string patchType, string name, string data, out ISelectable selectable) { - return _rootSelectionBlock.ExecuteFresh(_environmentSnapshot,patchType, name, ref patchData); + selectable = RuleSet.ConvertToSelectable(patchType, name, data); + return _rootSelectionBlock.ExecuteFresh(_environmentSnapshot, selectable); } } } \ No newline at end of file diff --git a/Runtime/SassyPatching/Execution/Universe.cs b/Runtime/SassyPatching/Execution/Universe.cs index 2c78292..52a6e9a 100644 --- a/Runtime/SassyPatching/Execution/Universe.cs +++ b/Runtime/SassyPatching/Execution/Universe.cs @@ -5,10 +5,11 @@ using PatchManager.SassyPatching.Attributes; using PatchManager.SassyPatching.Interfaces; using PatchManager.SassyPatching.Nodes; -using PatchManager.Shared.Interfaces; using SassyPatchGrammar; using System.Reflection; +using JetBrains.Annotations; using PatchManager.SassyPatching.Exceptions; +using PatchManager.SassyPatching.NewAssets; using PatchManager.SassyPatching.Nodes.Expressions; using PatchManager.SassyPatching.Utility; using PatchManager.Shared; @@ -103,17 +104,6 @@ public void AddConfigUpdater(long priority, string label, string? name, Expressi // Populated from Space Warps mod list come 1.3.0 public List AllMods; - /// - /// This is an action that is taken - /// - public readonly Action RegisterPatcher; - - - /// - /// Register a generator patch - /// - public readonly Action RegisterGenerator; - /// /// This logs errors in this universe /// @@ -138,12 +128,10 @@ public void AddConfigUpdater(long priority, string label, string? name, Expressi /// This action receives patchers and registers them for later execution /// The action to be taken to log an error /// The action to be taken to log a message - public Universe(Action registerPatcher, Action errorLogger, Action messageLogger, Action registerGenerator, List allMods) + public Universe(Action errorLogger, Action messageLogger, List allMods) { - RegisterPatcher = registerPatcher; ErrorLogger = errorLogger; MessageLogger = messageLogger; - RegisterGenerator = registerGenerator; LoadedLabels = new List(_preloadedLabels); AllMods = allMods; MessageLogger("Setup universe!"); @@ -461,6 +449,7 @@ public void RegisterAllPatches() } } + private void SortStages() { MessageLogger($"Sorting {UnsortedStages.Count} stages"); @@ -548,5 +537,235 @@ public static void RegisterRawLibrary(string modId, string name, string raw) { AllRawLibraries.Add($"{modId}:{name}", raw); } + + + #region Patch running + + + public int TotalPatchCount; + public List GenericPatches = new(); + public Dictionary> LabelPatches = new(); + public Dictionary> NamePatches = new(); + public Dictionary>> LabelNamePatches = new(); + + + private void RegisterPatcher(SassyTextPatcher patcher) + { + TotalPatchCount += 1; + if (patcher.RuleSet.Labels == null && patcher.AssetName == null) + { + AddSorted(GenericPatches, patcher); + } else if (patcher.RuleSet.Labels != null && patcher.AssetName == null) + { + foreach (var label in patcher.RuleSet.Labels) + { + if (LabelPatches.TryGetValue(label, out var patchers)) + { + AddSorted(patchers, patcher); + } + else + { + LabelPatches[label] = new List { patcher }; + } + } + } else if (patcher.RuleSet.Labels == null && patcher.AssetName != null) + { + if (NamePatches.TryGetValue(patcher.AssetName, out var patchers)) + { + AddSorted(patchers, patcher); + } + else + { + NamePatches[patcher.AssetName] = new List { patcher }; + } + } else if (patcher.RuleSet.Labels != null && patcher.AssetName != null) + { + foreach (var label in patcher.RuleSet.Labels) + { + if (LabelNamePatches.TryGetValue(label, out var namePatchers)) + { + if (namePatchers.TryGetValue(patcher.AssetName, out var patchers)) + { + AddSorted(patchers, patcher); + } + else + { + namePatchers[patcher.AssetName] = new List { patcher }; + } + } + else + { + LabelNamePatches[label] = new Dictionary> + { + [patcher.AssetName] = new() { patcher } + }; + } + } + } + } + + + private static void AddSorted(List patchers, SassyTextPatcher patcher) + { + var index = patchers.FindIndex(x => x.Priority > patcher.Priority); + if (index == -1) + { + patchers.Add(patcher); + } + else + { + patchers.Insert(index,patcher); + } + } + + public string RunAllPatchesFor(string label, string name, string data, out int patchCount, out int errorCount) + { + patchCount = 0; + errorCount = 0; + var enumerator = new PatchEnumerator(GenericPatches, LabelPatches.GetValueOrDefault(label), NamePatches.GetValueOrDefault(name), LabelNamePatches.GetValueOrDefault(label)?.GetValueOrDefault(name)); + ISelectable previous = null; + while (enumerator.Next is { } next) + { + try + { + + if (previous == null) + { + if (next.TryPatchBegin(label, name, data, out previous)) patchCount++; + } + else + { + if (next.TryPatch(label, name, ref previous, out var stop)) patchCount++; + if (stop) + { + return string.Empty; + } + } + } + catch (Exception e) + { + errorCount += 1; + ErrorLogger($"Patching {label}:{name} failed due to {e}"); + } + } + + return previous?.Serialize() ?? data; + } + + public string RunAllPatchesFor(string label, string name, ISelectable selectable, out int patchCount, out int errorCount) + { + patchCount = 0; + errorCount = 0; + var enumerator = new PatchEnumerator(GenericPatches, LabelPatches.GetValueOrDefault(label), NamePatches.GetValueOrDefault(name), LabelNamePatches.GetValueOrDefault(label)?.GetValueOrDefault(name)); + while (enumerator.Next is { } next) + { + try + { + if (next.TryPatch(label, name, ref selectable, out var stop)) patchCount++; + if (stop) + { + return string.Empty; + } + } + catch (Exception e) + { + errorCount += 1; + ErrorLogger($"Patching {label}:{name} failed due to {e}"); + } + } + return selectable.Serialize(); + } + + private class PatchEnumerator + { + private List _generic; + [CanBeNull] private List _label; + [CanBeNull] private List _name; + [CanBeNull] private List _labelName; + private int _genericIndex = 0; + private int _labelIndex = 0; + private int _nameIndex = 0; + private int _labelNameIndex = 0; + public PatchEnumerator(List generic, List label, + List name, List labelName) + { + _generic = generic; + _label = label; + _name = name; + _labelName = labelName; + } + + [CanBeNull] + public SassyTextPatcher Next + { + get + { + SassyTextPatcher generic = null; + SassyTextPatcher label = null; + SassyTextPatcher name = null; + SassyTextPatcher labelName = null; + var minPriority = ulong.MaxValue; + + if (_genericIndex < _generic.Count) + { + generic = _generic[_genericIndex]; + minPriority = Math.Min(minPriority, generic.Priority); + } + + if (_label != null && _labelIndex < _label.Count) + { + label = _label[_labelIndex]; + minPriority = Math.Min(minPriority, label.Priority); + } + + if (_name != null && _nameIndex < _name.Count) + { + name = _name[_nameIndex]; + minPriority = Math.Min(minPriority, name.Priority); + } + + if (_labelName != null && _labelNameIndex < _labelName.Count) + { + labelName = _labelName[_labelNameIndex]; + minPriority = Math.Min(minPriority, labelName.Priority); + } + + if (generic != null && minPriority == generic.Priority) + { + _genericIndex += 1; + return generic; + } + + if (label != null && minPriority == label.Priority) + { + _labelIndex += 1; + return label; + } + + if (name != null && minPriority == name.Priority) + { + _nameIndex += 1; + return name; + } + + if (labelName != null && minPriority == labelName.Priority) + { + _labelNameIndex += 1; + return labelName; + } + + return null; + } + } + } + + #endregion + + public List Generators = new(); + + public void RegisterGenerator(SassyGenerator generator) + { + Generators.Add(generator); + } } } \ No newline at end of file diff --git a/Runtime/SassyPatching/Interfaces/IPatcherRuleSet.cs b/Runtime/SassyPatching/Interfaces/IPatcherRuleSet.cs index 2d4dd54..8dd6e8a 100644 --- a/Runtime/SassyPatching/Interfaces/IPatcherRuleSet.cs +++ b/Runtime/SassyPatching/Interfaces/IPatcherRuleSet.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using JetBrains.Annotations; namespace PatchManager.SassyPatching.Interfaces { @@ -9,22 +10,32 @@ namespace PatchManager.SassyPatching.Interfaces public interface IPatcherRuleSet { /// - /// What type of patch type will this ruleset match + /// What type of labels will this ruleset match, used for optimization purposes /// - /// The label to match - /// True if the label matches the ruleset - public bool Matches(string label); + [CanBeNull] + public string[] Labels { get; } /// /// This converts json data to an ISelectable for the rest of the engine to use /// - /// The type of data to convert to an ISelectale + /// The type of data to convert to an ISelectable /// The name of the data /// The data to convert to an ISelectable /// The selectable representing the data public ISelectable ConvertToSelectable(string type, string name, string jsonData); + + + /// + /// + /// + /// + /// If the selectable from the previous patch can be ingested again + public bool CanIngestSelectable(ISelectable selectable); + + public bool CanGetAssetNameFromSelectableName { get; } - + public string SelectableNameToAssetName(string selectableName); + /// /// Creates a new asset for the patcher /// diff --git a/Runtime/SassyPatching/Interfaces/ISelectable.cs b/Runtime/SassyPatching/Interfaces/ISelectable.cs index 35e66dd..7927acb 100644 --- a/Runtime/SassyPatching/Interfaces/ISelectable.cs +++ b/Runtime/SassyPatching/Interfaces/ISelectable.cs @@ -69,5 +69,9 @@ public interface ISelectable /// /// The value representation of the selectable public DataValue GetValue(); + + public bool WasModified { get; } + + public void ClearModified(); } } \ No newline at end of file diff --git a/Runtime/SassyPatching/Nodes/Selectors/ChildSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/ChildSelector.cs index 36d1f06..d0e1198 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/ChildSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/ChildSelector.cs @@ -48,8 +48,8 @@ private List SelectChildren(List - public override List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) => - SelectChildren(Parent.SelectAllTopLevel(type, name, data, baseEnvironment, out rulesetMatchingObject)); + public override List SelectAllTopLevel(ISelectable inSelectable, Environment baseEnvironment) => + SelectChildren(Parent.SelectAllTopLevel(inSelectable, baseEnvironment)); public override List CreateNew(List rulesetArguments, Environment baseEnvironment, out INewAsset newAsset) => SelectChildren(Parent.CreateNew(rulesetArguments, baseEnvironment, out newAsset)); diff --git a/Runtime/SassyPatching/Nodes/Selectors/ClassCaptureSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/ClassCaptureSelector.cs index 7ef7e73..bd4d543 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/ClassCaptureSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/ClassCaptureSelector.cs @@ -49,9 +49,8 @@ public override List SelectAll(List - public override List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) + public override List SelectAllTopLevel(ISelectable inSelectable, Environment baseEnvironment) { - rulesetMatchingObject = null; return new(); } diff --git a/Runtime/SassyPatching/Nodes/Selectors/ClassSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/ClassSelector.cs index 1df1a7e..4732870 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/ClassSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/ClassSelector.cs @@ -28,9 +28,8 @@ public override List SelectAll(List - public override List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) + public override List SelectAllTopLevel(ISelectable inSelectable, Environment baseEnvironment) { - rulesetMatchingObject = null; return new(); } diff --git a/Runtime/SassyPatching/Nodes/Selectors/CombinationSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/CombinationSelector.cs index a4d7aeb..ca6fd54 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/CombinationSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/CombinationSelector.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using PatchManager.SassyPatching.Exceptions; using PatchManager.SassyPatching.Execution; using PatchManager.SassyPatching.Interfaces; using Environment = PatchManager.SassyPatching.Execution.Environment; @@ -49,20 +50,10 @@ public override List SelectAll(List - public override List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) + public override List SelectAllTopLevel(ISelectable inSelectable, Environment baseEnvironment) { - var start = new List(); - rulesetMatchingObject = null; - foreach (var selector in Selectors) - { - // ReSharper disable once IdentifierTypo - start = SelectionUtilities.CombineSelections(start,selector.SelectAllTopLevel(type, name, data,baseEnvironment, out var rsmo)); - if (rsmo != null && rulesetMatchingObject == null) - { - rulesetMatchingObject = rsmo; - } - } - return start; + throw new InterpreterException(Coordinate, + "Top level combination is not supported, use an intersection with the ruleset on the left and the combination in parentheses"); } public override List CreateNew(List rulesetArguments, Environment baseEnvironment, out INewAsset newAsset) diff --git a/Runtime/SassyPatching/Nodes/Selectors/ElementAdditionSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/ElementAdditionSelector.cs index 83d4643..6d95a2f 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/ElementAdditionSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/ElementAdditionSelector.cs @@ -56,9 +56,8 @@ public override List SelectAll(List - public override List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) + public override List SelectAllTopLevel(ISelectable inSelectable, Environment baseEnvironment) { - rulesetMatchingObject = null; return new(); } diff --git a/Runtime/SassyPatching/Nodes/Selectors/ElementSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/ElementSelector.cs index 77d8749..d273d15 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/ElementSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/ElementSelector.cs @@ -44,9 +44,8 @@ public override List SelectAll(List - public override List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) + public override List SelectAllTopLevel(ISelectable inSelectable, Environment baseEnvironment) { - rulesetMatchingObject = null; return new(); } diff --git a/Runtime/SassyPatching/Nodes/Selectors/EnsureSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/EnsureSelector.cs index 6b5cc01..9d669ea 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/EnsureSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/EnsureSelector.cs @@ -73,9 +73,8 @@ public override List SelectAll(List - public override List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) + public override List SelectAllTopLevel(ISelectable inSelectable, Environment baseEnvironment) { - rulesetMatchingObject = null; return new(); } diff --git a/Runtime/SassyPatching/Nodes/Selectors/IntersectionSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/IntersectionSelector.cs index 66c6899..4918c6f 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/IntersectionSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/IntersectionSelector.cs @@ -57,9 +57,9 @@ private List SelectAllSkippingFirst(List - public override List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) + public override List SelectAllTopLevel(ISelectable selectable, Environment baseEnvironment) { - var start = Selectors[0].SelectAllTopLevel(type, name, data, baseEnvironment, out rulesetMatchingObject); + var start = Selectors[0].SelectAllTopLevel(selectable, baseEnvironment); return SelectAllSkippingFirst(start); } diff --git a/Runtime/SassyPatching/Nodes/Selectors/NameSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/NameSelector.cs index d296ccc..f81e480 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/NameSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/NameSelector.cs @@ -28,9 +28,8 @@ public override List SelectAll(List - public override List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) + public override List SelectAllTopLevel(ISelectable inSelectable, Environment baseEnvironment) { - rulesetMatchingObject = null; return new(); } diff --git a/Runtime/SassyPatching/Nodes/Selectors/RulesetSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/RulesetSelector.cs index f99f7fa..56a44a2 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/RulesetSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/RulesetSelector.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using PatchManager.SassyPatching.Exceptions; using PatchManager.SassyPatching.Execution; using PatchManager.SassyPatching.Interfaces; @@ -27,33 +28,15 @@ public override List SelectAll(List - public override List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) + public override List SelectAllTopLevel(ISelectable selectable, Environment baseEnvironment) { - if (!Universe.RuleSets.TryGetValue(RulesetName, out var ruleSet)) - { - throw new InterpreterException(Coordinate, $"Ruleset: {RulesetName} does not exist!"); - } - - if (ruleSet.Matches(type)) + return new List { new () { - rulesetMatchingObject = ruleSet.ConvertToSelectable(type, name,data); - if (rulesetMatchingObject != null) - { - return - new List - { - new() - { - Selectable = rulesetMatchingObject, - Environment = new Environment(baseEnvironment.GlobalEnvironment, baseEnvironment) - } - }; - } - } - rulesetMatchingObject = null; - return new List { }; - + Selectable = selectable, + Environment = new Environment(baseEnvironment.GlobalEnvironment, baseEnvironment) + }}; } + public override List CreateNew(List rulesetArguments, Environment baseEnvironment, out INewAsset newAsset) { diff --git a/Runtime/SassyPatching/Nodes/Selectors/Selector.cs b/Runtime/SassyPatching/Nodes/Selectors/Selector.cs index 1396dbb..80c35c0 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/Selector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/Selector.cs @@ -24,13 +24,10 @@ internal Selector(Coordinate c) : base(c) /// /// Select all that match this selector from the type and data /// - /// The type, e.g. parts_data - /// The name of the data - /// The data, a textual representation of the data + /// The data a selectable representation of the data created by the ruleset /// The base environment to create the selectables in - /// The found object that matches the ruleset /// A list of all selections from the data - public abstract List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject); + public abstract List SelectAllTopLevel(ISelectable data, Environment baseEnvironment); /// diff --git a/Runtime/SassyPatching/Nodes/Selectors/WildcardSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/WildcardSelector.cs index 1ec64d4..9b505fa 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/WildcardSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/WildcardSelector.cs @@ -21,9 +21,8 @@ public override List SelectAll(List - public override List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) + public override List SelectAllTopLevel(ISelectable inSelectable, Environment baseEnvironment) { - rulesetMatchingObject = null; return new(); } diff --git a/Runtime/SassyPatching/Nodes/Selectors/WithoutClassSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/WithoutClassSelector.cs index 0246652..63d75cf 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/WithoutClassSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/WithoutClassSelector.cs @@ -28,9 +28,8 @@ public override List SelectAll(List - public override List SelectAllTopLevel(string type,string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) + public override List SelectAllTopLevel(ISelectable inSelectable, Environment baseEnvironment) { - rulesetMatchingObject = null; return new(); } diff --git a/Runtime/SassyPatching/Nodes/Selectors/WithoutNameSelector.cs b/Runtime/SassyPatching/Nodes/Selectors/WithoutNameSelector.cs index 13f37dd..bb58d09 100644 --- a/Runtime/SassyPatching/Nodes/Selectors/WithoutNameSelector.cs +++ b/Runtime/SassyPatching/Nodes/Selectors/WithoutNameSelector.cs @@ -28,9 +28,8 @@ public override List SelectAll(List - public override List SelectAllTopLevel(string type, string name, string data, Environment baseEnvironment, out ISelectable rulesetMatchingObject) + public override List SelectAllTopLevel(ISelectable inSelectable, Environment baseEnvironment) { - rulesetMatchingObject = null; return new(); } diff --git a/Runtime/SassyPatching/Nodes/Statements/SelectionBlock.cs b/Runtime/SassyPatching/Nodes/Statements/SelectionBlock.cs index 11df91e..363fdd3 100644 --- a/Runtime/SassyPatching/Nodes/Statements/SelectionBlock.cs +++ b/Runtime/SassyPatching/Nodes/Statements/SelectionBlock.cs @@ -40,17 +40,12 @@ internal SelectionBlock(Coordinate c, List attributes, Select /// Execute this selection block on a dataset /// /// The environment that contains this selection block - /// The type of dataset this is being executed (e.g. parts_data) - /// The dataset to execute this patch on - public bool ExecuteFresh(Environment snapshot, string datasetType, string name, ref string dataset) + /// The selectable to execute this patch on + public bool ExecuteFresh(Environment snapshot, ISelectable inSelectable) { // var subEnvironment = new Environment(snapshot.GlobalEnvironment, snapshot); - var selections = Selector.SelectAllTopLevel(datasetType, name, dataset, snapshot, out var rulesetMatchingObject); + var selections = Selector.SelectAllTopLevel(inSelectable, snapshot); - if (rulesetMatchingObject == null || selections.Count == 0) - { - return false; - } // Get the first matching selection if there are somehow more than one foreach (var selectable in selections) { var modifiable = selectable.Selectable.OpenModification(); @@ -74,11 +69,7 @@ public bool ExecuteFresh(Environment snapshot, string datasetType, string name, } - var newDataSet = rulesetMatchingObject.Serialize(); - if (newDataSet == dataset) return false; - dataset = newDataSet; - return true; - + return inSelectable.WasModified; } public INewAsset ExecuteCreation(Environment snapshot, List arguments) diff --git a/Runtime/SassyPatching/Selectables/JTokenSelectable.cs b/Runtime/SassyPatching/Selectables/JTokenSelectable.cs index 5db6aee..55ea94f 100644 --- a/Runtime/SassyPatching/Selectables/JTokenSelectable.cs +++ b/Runtime/SassyPatching/Selectables/JTokenSelectable.cs @@ -12,6 +12,7 @@ namespace PatchManager.SassyPatching.Selectables /// public class JTokenSelectable : BaseSelectable { + private bool _dirty = false; private readonly Action _markDirty; /// /// This is the token being modified @@ -27,7 +28,12 @@ public class JTokenSelectable : BaseSelectable /// The type of element this is, can be different from the name public JTokenSelectable(Action markDirty, JToken token, string name, string? elementType = null) { - _markDirty = markDirty; + _markDirty = () => + { + _dirty = true; + + markDirty(); + }; Token = token; ElementType = elementType ?? name; _getName = _ => name; @@ -129,5 +135,11 @@ public override ISelectable AddElement(string elementType) /// public override DataValue GetValue() => DataValue.FromJToken(Token); + + public override bool WasModified => _dirty; + public override void ClearModified() + { + _dirty = false; + } } } \ No newline at end of file diff --git a/Runtime/Science/Rulesets/DiscoverablesRuleset.cs b/Runtime/Science/Rulesets/DiscoverablesRuleset.cs index 05fdcd2..dda19a2 100644 --- a/Runtime/Science/Rulesets/DiscoverablesRuleset.cs +++ b/Runtime/Science/Rulesets/DiscoverablesRuleset.cs @@ -16,13 +16,25 @@ namespace PatchManager.Science.Rulesets [PatcherRuleset("discoverables","science_region_discoverables")] public class DiscoverablesRuleset : IPatcherRuleSet { - /// - public bool Matches(string label) => label == "science_region_discoverables"; + + public string[] Labels => new[] { "science_region_discoverables" }; /// public ISelectable ConvertToSelectable(string type, string name, string jsonData) => new DiscoverablesSelectable(JObject.Parse(jsonData)); + public bool CanIngestSelectable(ISelectable selectable) => selectable is DiscoverablesSelectable + { + Deleted: false + }; + + // TODO: Evaluate whether or not this is the case + public bool CanGetAssetNameFromSelectableName => false; + public string SelectableNameToAssetName(string selectableName) + { + throw new System.NotImplementedException(); + } + /// public INewAsset CreateNew(List dataValues) { diff --git a/Runtime/Science/Rulesets/ExperimentRuleset.cs b/Runtime/Science/Rulesets/ExperimentRuleset.cs index 2912394..6b995ca 100644 --- a/Runtime/Science/Rulesets/ExperimentRuleset.cs +++ b/Runtime/Science/Rulesets/ExperimentRuleset.cs @@ -16,13 +16,21 @@ namespace PatchManager.Science.Rulesets [PatcherRuleset("experiments","scienceExperiment"),UsedImplicitly] public class ExperimentRuleset : IPatcherRuleSet { - /// - public bool Matches(string label) => label == "scienceExperiment"; + public string[] Labels => new [] {"scienceExperiment"}; /// public ISelectable ConvertToSelectable(string type, string name, string jsonData) => new ExperimentSelectable(JObject.Parse(jsonData)); + public bool CanIngestSelectable(ISelectable selectable) => selectable is ExperimentSelectable + { + Deleted: false + }; + + // TODO: Evaluate whether or not this is the case + public bool CanGetAssetNameFromSelectableName => true; + public string SelectableNameToAssetName(string selectableName) => selectableName; + /// public INewAsset CreateNew(List dataValues) { diff --git a/Runtime/Science/Rulesets/RegionsRuleset.cs b/Runtime/Science/Rulesets/RegionsRuleset.cs index 29069d9..5dc7490 100644 --- a/Runtime/Science/Rulesets/RegionsRuleset.cs +++ b/Runtime/Science/Rulesets/RegionsRuleset.cs @@ -16,13 +16,24 @@ namespace PatchManager.Science.Rulesets [PatcherRuleset("regions","science_region")] public class RegionsRuleset : IPatcherRuleSet { - /// - public bool Matches(string label) => label == "science_region"; + public string[] Labels => new[] { "science_region" }; /// public ISelectable ConvertToSelectable(string type, string name, string jsonData) => new RegionsSelectable(JObject.Parse(jsonData)); + public bool CanIngestSelectable(ISelectable selectable) => selectable is RegionsSelectable + { + Deleted: false + }; + + // TODO: Evaluate whether or not this is the case + public bool CanGetAssetNameFromSelectableName => false; + public string SelectableNameToAssetName(string selectableName) + { + throw new System.NotImplementedException(); + } + /// public INewAsset CreateNew(List dataValues) { diff --git a/Runtime/Science/Rulesets/ScienceRuleset.cs b/Runtime/Science/Rulesets/ScienceRuleset.cs index 0d31efe..ba70933 100644 --- a/Runtime/Science/Rulesets/ScienceRuleset.cs +++ b/Runtime/Science/Rulesets/ScienceRuleset.cs @@ -14,13 +14,25 @@ namespace PatchManager.Science.Rulesets [PatcherRuleset("science","techNodeData")] public class ScienceRuleset : IPatcherRuleSet { - /// - public bool Matches(string label) => label == "techNodeData"; + + public string[] Labels => new[] { "techNodeData" }; /// public ISelectable ConvertToSelectable(string type, string name, string jsonData) => new ScienceSelectable(JObject.Parse(jsonData)); + public bool CanIngestSelectable(ISelectable selectable) => selectable is ScienceSelectable + { + Deleted: false + }; + + // TODO: Evaluate whether or not this is the case + public bool CanGetAssetNameFromSelectableName => false; + public string SelectableNameToAssetName(string selectableName) + { + throw new System.NotImplementedException(); + } + /// public INewAsset CreateNew(List dataValues) { diff --git a/Runtime/Science/Selectables/DiscoverablesSelectable.cs b/Runtime/Science/Selectables/DiscoverablesSelectable.cs index af2e8a4..0d0b9a2 100644 --- a/Runtime/Science/Selectables/DiscoverablesSelectable.cs +++ b/Runtime/Science/Selectables/DiscoverablesSelectable.cs @@ -13,10 +13,8 @@ namespace PatchManager.Science.Selectables /// public sealed class DiscoverablesSelectable : BaseSelectable { -#pragma warning disable CS0414 // Field is assigned but its value is never used private bool _modified; -#pragma warning restore CS0414 // Field is assigned but its value is never used - private bool _deleted; + public bool Deleted; /// /// Marks this part selectable as having been modified any level down @@ -32,7 +30,7 @@ public void SetModified() public void SetDeleted() { SetModified(); - _deleted = true; + Deleted = true; } @@ -116,11 +114,17 @@ public override ISelectable AddElement(string elementType) } /// - public override string Serialize() => _deleted ? "" : DiscoverablesObject.ToString(); + public override string Serialize() => Deleted ? "" : DiscoverablesObject.ToString(); /// public override DataValue GetValue() => DataValue.FromJToken(DiscoverablesObject); + public override bool WasModified => _modified; + public override void ClearModified() + { + _modified = false; + } + /// public override string ElementType { get; } } diff --git a/Runtime/Science/Selectables/ExperimentSelectable.cs b/Runtime/Science/Selectables/ExperimentSelectable.cs index e638af0..6abf937 100644 --- a/Runtime/Science/Selectables/ExperimentSelectable.cs +++ b/Runtime/Science/Selectables/ExperimentSelectable.cs @@ -12,10 +12,8 @@ namespace PatchManager.Science.Selectables /// public class ExperimentSelectable : BaseSelectable { -#pragma warning disable CS0414 // Field is assigned but its value is never used private bool _modified; -#pragma warning restore CS0414 // Field is assigned but its value is never used - private bool _deleted; + public bool Deleted; /// /// Marks this part selectable as having been modified any level down @@ -31,7 +29,7 @@ public void SetModified() public void SetDeleted() { SetModified(); - _deleted = true; + Deleted = true; } /// @@ -105,9 +103,15 @@ public override ISelectable AddElement(string elementType) } /// - public override string Serialize() => _deleted ? "" : ScienceObject.ToString(); + public override string Serialize() => Deleted ? "" : ScienceObject.ToString(); /// public override DataValue GetValue() => DataValue.FromJToken(DataObject); + + public override bool WasModified => _modified; + public override void ClearModified() + { + _modified = false; + } } } \ No newline at end of file diff --git a/Runtime/Science/Selectables/RegionsSelectable.cs b/Runtime/Science/Selectables/RegionsSelectable.cs index dc49a4e..a467395 100644 --- a/Runtime/Science/Selectables/RegionsSelectable.cs +++ b/Runtime/Science/Selectables/RegionsSelectable.cs @@ -13,10 +13,8 @@ namespace PatchManager.Science.Selectables /// public sealed class RegionsSelectable : BaseSelectable { -#pragma warning disable CS0414 // Field is assigned but its value is never used private bool _modified; -#pragma warning restore CS0414 // Field is assigned but its value is never used - private bool _deleted; + public bool Deleted; /// /// Marks this part selectable as having been modified any level down @@ -32,7 +30,7 @@ public void SetModified() public void SetDeleted() { SetModified(); - _deleted = true; + Deleted = true; } @@ -116,11 +114,17 @@ public override ISelectable AddElement(string elementType) } /// - public override string Serialize() => _deleted ? "" : RegionsObject.ToString(); + public override string Serialize() => Deleted ? "" : RegionsObject.ToString(); /// public override DataValue GetValue() => DataValue.FromJToken(RegionsObject); + public override bool WasModified => _modified; + public override void ClearModified() + { + _modified = false; + } + /// public override string ElementType { get; } } diff --git a/Runtime/Science/Selectables/ScienceSelectable.cs b/Runtime/Science/Selectables/ScienceSelectable.cs index 37b4954..8ec9c97 100644 --- a/Runtime/Science/Selectables/ScienceSelectable.cs +++ b/Runtime/Science/Selectables/ScienceSelectable.cs @@ -12,10 +12,8 @@ namespace PatchManager.Science.Selectables /// public sealed class ScienceSelectable : BaseSelectable { -#pragma warning disable CS0414 // Field is assigned but its value is never used private bool _modified; -#pragma warning restore CS0414 // Field is assigned but its value is never used - private bool _deleted; + public bool Deleted; /// /// Marks this part selectable as having been modified any level down @@ -31,7 +29,7 @@ public void SetModified() public void SetDeleted() { SetModified(); - _deleted = true; + Deleted = true; } /// @@ -99,9 +97,15 @@ public override ISelectable AddElement(string elementType) } /// - public override string Serialize() => _deleted ? "" : ScienceObject.ToString(); + public override string Serialize() => Deleted ? "" : ScienceObject.ToString(); /// public override DataValue GetValue() => DataValue.FromJToken(ScienceObject); + + public override bool WasModified => _modified; + public override void ClearModified() + { + _modified = false; + } } } \ No newline at end of file diff --git a/Runtime/Shared/Interfaces.meta b/Runtime/Shared/Interfaces.meta deleted file mode 100644 index b55e450..0000000 --- a/Runtime/Shared/Interfaces.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 7c3133a20b629d248a09e7b33d808636 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/Shared/Interfaces/ITextAssetGenerator.cs b/Runtime/Shared/Interfaces/ITextAssetGenerator.cs deleted file mode 100644 index b206105..0000000 --- a/Runtime/Shared/Interfaces/ITextAssetGenerator.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace PatchManager.Shared.Interfaces -{ - /// - /// Describes a patcher that can generate text assets - /// - public interface ITextAssetGenerator - { - /// - /// The priority of this patcher compared to other patchers, so it can order the way they get executed - /// - public ulong Priority { get; } - - /// - /// Creates a new text asset - /// - /// The label of the created asset - /// The name of the created asset - /// The contents of the created asset - public string Create(out string label, out string name); - } -} \ No newline at end of file diff --git a/Runtime/Shared/Interfaces/ITextAssetGenerator.cs.meta b/Runtime/Shared/Interfaces/ITextAssetGenerator.cs.meta deleted file mode 100644 index 8a48c68..0000000 --- a/Runtime/Shared/Interfaces/ITextAssetGenerator.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2edafcb72ca6c6148bd79c841ddce8b7 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/Shared/Interfaces/ITextPatcher.cs b/Runtime/Shared/Interfaces/ITextPatcher.cs deleted file mode 100644 index 5a87498..0000000 --- a/Runtime/Shared/Interfaces/ITextPatcher.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace PatchManager.Shared.Interfaces -{ - /// - /// This interfaces describes a patcher that can patch a simple text asset - /// - public interface ITextPatcher - { - /// - /// The priority of this patcher compared to other patchers, so it can order the way they get executed - /// - public ulong Priority { get; } - - /// - /// Execute this patcher on a blob of text - /// - /// The type of patch, "part" for parts patches, etc... - /// The name of the blob of text being patched - /// The string representation of the file being patched, if it gets set to an empty string, then we should just delete whatever is being patched - /// True if the patchData was modified - public bool TryPatch(string patchType, string name, ref string patchData); - } -} \ No newline at end of file diff --git a/Runtime/Shared/Interfaces/ITextPatcher.cs.meta b/Runtime/Shared/Interfaces/ITextPatcher.cs.meta deleted file mode 100644 index b40782b..0000000 --- a/Runtime/Shared/Interfaces/ITextPatcher.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2527ff8777b2be542ac2abac380cbc8b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From 4977a8477a952cbf5c37f677a4c4cc23d1105595 Mon Sep 17 00:00:00 2001 From: Lexi Date: Sat, 5 Jul 2025 21:30:45 -0400 Subject: [PATCH 2/2] Better logging, actually make the loading flow actions in a sane way --- Runtime/Core/Assets/PatchingManager.cs | 28 +++++++-------------- Runtime/SassyPatching/Execution/Universe.cs | 4 +-- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/Runtime/Core/Assets/PatchingManager.cs b/Runtime/Core/Assets/PatchingManager.cs index e36bf5b..720948c 100644 --- a/Runtime/Core/Assets/PatchingManager.cs +++ b/Runtime/Core/Assets/PatchingManager.cs @@ -232,7 +232,7 @@ private static AsyncOperationHandle> RebuildCache(string label) } catch (Exception e) { - Logging.LogError($"Unable to patch {asset.name} due to: {e.Message}"); + Logging.LogError($"Unable to patch {asset.name} due to: {e.Message}, {e.StackTrace}"); } }); @@ -318,29 +318,19 @@ GenericFlowAction CreateIndexedFlowAction(int idx) (resolve2, _) => { var handle = RebuildCache(distinctKeys[idx]); - var killTips = false; - if (idx + 1 < distinctKeys.Count) - { - GameManager.Instance.LoadingFlow.FlowActions.Insert( - GameManager.Instance.LoadingFlow.flowIndex + 1, - CreateIndexedFlowAction(idx + 1) - ); - } - else - { - killTips = true; - } - - CoroutineUtil.Instance.DoCoroutine(WaitForCacheRebuildSingleHandle(handle, resolve2, killTips)); + CoroutineUtil.Instance.DoCoroutine(WaitForCacheRebuildSingleHandle(handle, resolve2, idx + 1 == distinctKeys.Count)); }); } if (distinctKeys.Count > 0) { - GameManager.Instance.LoadingFlow.FlowActions.Insert( - GameManager.Instance.LoadingFlow.flowIndex + 1, - CreateIndexedFlowAction(0) - ); + for (var i = distinctKeys.Count - 1; i >= 0; i--) + { + GameManager.Instance.LoadingFlow.FlowActions.Insert( + GameManager.Instance.LoadingFlow.flowIndex + 1, + CreateIndexedFlowAction(i) + ); + } } resolve(); diff --git a/Runtime/SassyPatching/Execution/Universe.cs b/Runtime/SassyPatching/Execution/Universe.cs index 52a6e9a..933a954 100644 --- a/Runtime/SassyPatching/Execution/Universe.cs +++ b/Runtime/SassyPatching/Execution/Universe.cs @@ -645,7 +645,7 @@ public string RunAllPatchesFor(string label, string name, string data, out int p catch (Exception e) { errorCount += 1; - ErrorLogger($"Patching {label}:{name} failed due to {e}"); + ErrorLogger($"Patching {label}:{name} failed due to {e.Message}"); } } @@ -670,7 +670,7 @@ public string RunAllPatchesFor(string label, string name, ISelectable selectable catch (Exception e) { errorCount += 1; - ErrorLogger($"Patching {label}:{name} failed due to {e}"); + ErrorLogger($"Patching {label}:{name} failed due to {e.Message}"); } } return selectable.Serialize();