From 77741621c54e9e66cb5627bd6011d566ced13997 Mon Sep 17 00:00:00 2001 From: Squiggles Date: Sat, 3 May 2025 00:13:31 +0100 Subject: [PATCH 01/11] feat(ModdingAPI): introduce basic modding api --- .gitignore | 3 ++ Assets/Scripts/DLS.asmdef | 30 +++++------ .../Description/Helpers/ChipTypeHelper.cs | 8 +++ Assets/Scripts/Game/Main/Main.cs | 3 ++ .../Game/Project/BuiltinChipCreator.cs | 11 ++-- .../Scripts/Game/Project/ModdedChipCreator.cs | 25 +++++++++ .../Game/Project/ModdedChipCreator.cs.meta | 2 + Assets/Scripts/ModdingAPI.meta | 8 +++ Assets/Scripts/ModdingAPI/ChipCreator.cs | 20 +++++++ Assets/Scripts/ModdingAPI/ChipCreator.cs.meta | 2 + .../Scripts/ModdingAPI/DLS.ModdingAPI.asmdef | 19 +++++++ .../ModdingAPI/DLS.ModdingAPI.asmdef.meta | 7 +++ Assets/Scripts/ModdingAPI/Debug.cs | 10 ++++ Assets/Scripts/ModdingAPI/Debug.cs.meta | 2 + Assets/Scripts/ModdingAPI/IMod.cs | 9 ++++ Assets/Scripts/ModdingAPI/IMod.cs.meta | 2 + Assets/Scripts/Mods.meta | 8 +++ Assets/Scripts/Mods/ModLoader.cs | 51 ++++++++++++++++++ Assets/Scripts/Mods/ModLoader.cs.meta | 2 + Assets/Scripts/SaveSystem/SavePaths.cs | 1 + ProjectSettings/ProjectVersion.txt | 4 +- TestData/Mods/ExampleMod.dls | Bin 0 -> 4608 bytes .../Projects/MainTest/ProjectDescription.json | 8 +-- 23 files changed, 211 insertions(+), 24 deletions(-) create mode 100644 Assets/Scripts/Game/Project/ModdedChipCreator.cs create mode 100644 Assets/Scripts/Game/Project/ModdedChipCreator.cs.meta create mode 100644 Assets/Scripts/ModdingAPI.meta create mode 100644 Assets/Scripts/ModdingAPI/ChipCreator.cs create mode 100644 Assets/Scripts/ModdingAPI/ChipCreator.cs.meta create mode 100644 Assets/Scripts/ModdingAPI/DLS.ModdingAPI.asmdef create mode 100644 Assets/Scripts/ModdingAPI/DLS.ModdingAPI.asmdef.meta create mode 100644 Assets/Scripts/ModdingAPI/Debug.cs create mode 100644 Assets/Scripts/ModdingAPI/Debug.cs.meta create mode 100644 Assets/Scripts/ModdingAPI/IMod.cs create mode 100644 Assets/Scripts/ModdingAPI/IMod.cs.meta create mode 100644 Assets/Scripts/Mods.meta create mode 100644 Assets/Scripts/Mods/ModLoader.cs create mode 100644 Assets/Scripts/Mods/ModLoader.cs.meta create mode 100644 TestData/Mods/ExampleMod.dls diff --git a/.gitignore b/.gitignore index dc00d664..fb37a608 100644 --- a/.gitignore +++ b/.gitignore @@ -25,10 +25,13 @@ # Visual Studio cache directory .vs/ +.vscode/ +bin/ # Gradle cache directory .gradle/ + # Autogenerated VS/MD/Consulo solution and project files ExportedObj/ .consulo/ diff --git a/Assets/Scripts/DLS.asmdef b/Assets/Scripts/DLS.asmdef index 73295ad9..e6c9cb8d 100644 --- a/Assets/Scripts/DLS.asmdef +++ b/Assets/Scripts/DLS.asmdef @@ -1,17 +1,17 @@ { - "name": "DLS", - "rootNamespace": "", - "references": [ - "GUID:d4f8eab5cdd4a544c9923829818c11c0", - "GUID:2b65bcfb6aad3f1459e94b356ff58293" - ], - "includePlatforms": [], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": false, - "precompiledReferences": [], - "autoReferenced": true, - "defineConstraints": [], - "versionDefines": [], - "noEngineReferences": false + "name": "DLS", + "rootNamespace": "", + "references": [ + "Assembly-Seb", + "DLS.Description" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file diff --git a/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs b/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs index 4964d076..e742d16c 100644 --- a/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs +++ b/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using UnityEngine; namespace DLS.Description { @@ -60,6 +61,13 @@ public static class ChipTypeHelper public static bool IsRomType(ChipType type) => type == ChipType.Rom_256x16; + public static ChipType AddNewModded(string name) + { + ChipType type = (ChipType)(Names.Count + 30); // Hacky... + Names.Add(type, name); + return type; + } + public static ChipType GetCorrespondingBusTerminusType(ChipType type) { return type switch diff --git a/Assets/Scripts/Game/Main/Main.cs b/Assets/Scripts/Game/Main/Main.cs index 3ffc82c0..71436907 100644 --- a/Assets/Scripts/Game/Main/Main.cs +++ b/Assets/Scripts/Game/Main/Main.cs @@ -4,6 +4,7 @@ using System.Linq; using DLS.Description; using DLS.Graphics; +using DLS.Mods; using DLS.SaveSystem; using UnityEngine; @@ -23,6 +24,8 @@ public static class Main public static void Init() { SavePaths.EnsureDirectoryExists(SavePaths.ProjectsPath); + SavePaths.EnsureDirectoryExists(SavePaths.ModsPath); + ModLoader.LoadMods(SavePaths.ModsPath); SaveAndApplyAppSettings(Loader.LoadAppSettings()); } diff --git a/Assets/Scripts/Game/Project/BuiltinChipCreator.cs b/Assets/Scripts/Game/Project/BuiltinChipCreator.cs index 1bf17b7f..4d8775f8 100644 --- a/Assets/Scripts/Game/Project/BuiltinChipCreator.cs +++ b/Assets/Scripts/Game/Project/BuiltinChipCreator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using DLS.Description; using UnityEngine; using static DLS.Graphics.DrawSettings; @@ -9,6 +10,7 @@ namespace DLS.Game public static class BuiltinChipCreator { static readonly Color ChipCol_SplitMerge = new(0.1f, 0.1f, 0.1f); //new(0.8f, 0.8f, 0.8f); + public static List ModdedChips = new(); public static ChipDescription[] CreateAllBuiltinChipDescriptions() { @@ -49,10 +51,13 @@ public static ChipDescription[] CreateAllBuiltinChipDescriptions() CreateBus(PinBitCount.Bit4), CreateBusTerminus(PinBitCount.Bit4), CreateBus(PinBitCount.Bit8), - CreateBusTerminus(PinBitCount.Bit8) - }; + CreateBusTerminus(PinBitCount.Bit8), + // ---- Modded Chips ---- + }.Concat(ModdedChips.ToArray()).ToArray(); } + + static ChipDescription CreateNand() { Color col = new(0.73f, 0.26f, 0.26f); @@ -371,7 +376,7 @@ static ChipDescription CreateBusTerminus(PinBitCount bitCount) } - static ChipDescription CreateBuiltinChipDescription(ChipType type, Vector2 size, Color col, PinDescription[] inputs = null, PinDescription[] outputs = null, DisplayDescription[] displays = null, bool hideName = false) + public static ChipDescription CreateBuiltinChipDescription(ChipType type, Vector2 size, Color col, PinDescription[] inputs = null, PinDescription[] outputs = null, DisplayDescription[] displays = null, bool hideName = false) { string name = ChipTypeHelper.GetName(type); ValidatePinIDs(inputs, outputs, name); diff --git a/Assets/Scripts/Game/Project/ModdedChipCreator.cs b/Assets/Scripts/Game/Project/ModdedChipCreator.cs new file mode 100644 index 00000000..6d8e2d80 --- /dev/null +++ b/Assets/Scripts/Game/Project/ModdedChipCreator.cs @@ -0,0 +1,25 @@ +using DLS.Description; +using UnityEngine; + +namespace DLS.Game +{ + public static class ModdedChipCreator + { + public static void RegisterChip(ChipType type, Vector2 size, Color col, PinDescription[] inputs = null, PinDescription[] outputs = null, DisplayDescription[] displays = null, bool hideName = false) + { + ChipDescription chipDescription = BuiltinChipCreator.CreateBuiltinChipDescription(type, size, col, inputs, outputs, displays, hideName); + + BuiltinChipCreator.ModdedChips.Add(chipDescription); + } + + public static PinDescription CreatePinDescription(string name, int id, PinBitCount bitCount = PinBitCount.Bit1) => + new( + name, + id, + Vector2.zero, + bitCount, + PinColour.Red, + PinValueDisplayMode.Off + ); + } +} \ No newline at end of file diff --git a/Assets/Scripts/Game/Project/ModdedChipCreator.cs.meta b/Assets/Scripts/Game/Project/ModdedChipCreator.cs.meta new file mode 100644 index 00000000..0078f94e --- /dev/null +++ b/Assets/Scripts/Game/Project/ModdedChipCreator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b63348c6a526e7846ab863f2d9d473fb \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI.meta b/Assets/Scripts/ModdingAPI.meta new file mode 100644 index 00000000..27aa6fe2 --- /dev/null +++ b/Assets/Scripts/ModdingAPI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 43f23a4e7b724f94395dc5f05cde7b10 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/ModdingAPI/ChipCreator.cs b/Assets/Scripts/ModdingAPI/ChipCreator.cs new file mode 100644 index 00000000..5384f8ca --- /dev/null +++ b/Assets/Scripts/ModdingAPI/ChipCreator.cs @@ -0,0 +1,20 @@ +using DLS.Description; +using DLS.Game; +using UnityEngine; + +namespace DLS.ModdingAPI +{ + public static class ChipCreator + { + public static void RegisterChip(string name, Vector2 size, Color col, PinDescription[] inputs = null, PinDescription[] outputs = null, DisplayDescription[] displays = null, bool hideName = false) + { + ChipType type = ChipTypeHelper.AddNewModded(name); + ModdedChipCreator.RegisterChip(type, size, col, inputs, outputs, displays, hideName); + } + + public static PinDescription CreatePinDescription(string name, int id, PinBitCount bitCount = PinBitCount.Bit1) + { + return ModdedChipCreator.CreatePinDescription(name, id, bitCount); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/ChipCreator.cs.meta b/Assets/Scripts/ModdingAPI/ChipCreator.cs.meta new file mode 100644 index 00000000..aeb9ee2f --- /dev/null +++ b/Assets/Scripts/ModdingAPI/ChipCreator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e798728c9b0672a4c91de977d122a933 \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/DLS.ModdingAPI.asmdef b/Assets/Scripts/ModdingAPI/DLS.ModdingAPI.asmdef new file mode 100644 index 00000000..4d0035cc --- /dev/null +++ b/Assets/Scripts/ModdingAPI/DLS.ModdingAPI.asmdef @@ -0,0 +1,19 @@ +{ + "name": "DLSModdingAPI", + "references": [ + "UnityEngine.CoreModule", + "DLS.Description", + "DLS" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "UnityEngine.CoreModule.dll" + ], + "autoReferenced": false, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/DLS.ModdingAPI.asmdef.meta b/Assets/Scripts/ModdingAPI/DLS.ModdingAPI.asmdef.meta new file mode 100644 index 00000000..6173a3fe --- /dev/null +++ b/Assets/Scripts/ModdingAPI/DLS.ModdingAPI.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 61a9784a30ea9384088f2051bf313a3e +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/ModdingAPI/Debug.cs b/Assets/Scripts/ModdingAPI/Debug.cs new file mode 100644 index 00000000..796ca23d --- /dev/null +++ b/Assets/Scripts/ModdingAPI/Debug.cs @@ -0,0 +1,10 @@ +namespace DLS.ModdingAPI +{ + public static class Debug + { + public static void Log(object toLog) + { + UnityEngine.Debug.Log(toLog); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/Debug.cs.meta b/Assets/Scripts/ModdingAPI/Debug.cs.meta new file mode 100644 index 00000000..331b522b --- /dev/null +++ b/Assets/Scripts/ModdingAPI/Debug.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0bc8960d8fa5b004fbee2bc9e42d7fee \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/IMod.cs b/Assets/Scripts/ModdingAPI/IMod.cs new file mode 100644 index 00000000..3f65fa33 --- /dev/null +++ b/Assets/Scripts/ModdingAPI/IMod.cs @@ -0,0 +1,9 @@ +namespace DLS.ModdingAPI +{ + public interface IMod + { + string Name { get; } + string Version { get; } + void Initialize(); + } +} \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/IMod.cs.meta b/Assets/Scripts/ModdingAPI/IMod.cs.meta new file mode 100644 index 00000000..9495d021 --- /dev/null +++ b/Assets/Scripts/ModdingAPI/IMod.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7fe56ef1ac2353c45baa0cdea81b9c8b \ No newline at end of file diff --git a/Assets/Scripts/Mods.meta b/Assets/Scripts/Mods.meta new file mode 100644 index 00000000..168c7681 --- /dev/null +++ b/Assets/Scripts/Mods.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d2c495a8eb211094f8aa588da8bc03ec +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Mods/ModLoader.cs b/Assets/Scripts/Mods/ModLoader.cs new file mode 100644 index 00000000..fa932499 --- /dev/null +++ b/Assets/Scripts/Mods/ModLoader.cs @@ -0,0 +1,51 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +namespace DLS.Mods +{ + public static class ModLoader + { + static ModLoader() + { + AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly; + } + + private static Assembly ResolveAssembly(object sender, ResolveEventArgs args) + { + if (args.Name.StartsWith("DLSModdingAPI")) + { + return Assembly.GetExecutingAssembly(); + } + + return null; // Let the default resolver handle other assemblies + } + public static void LoadMods(string modsDirectory) + { + UnityEngine.Debug.Log("Loading mods..."); + foreach (string dllPath in Directory.GetFiles(modsDirectory, "*.dls")) + { + Assembly modAssembly = Assembly.LoadFrom(dllPath); + foreach (Type type in modAssembly.GetTypes().Where(t => !t.IsAbstract)) + { + // Check if the type has the same structure as IMod + var nameProperty = type.GetProperty("Name"); + var versionProperty = type.GetProperty("Version"); + var initializeMethod = type.GetMethod("Initialize"); + + if (nameProperty != null && versionProperty != null && initializeMethod != null) + { + // Ugly, but ModLoader.IMod would not be the same as ModdingAPI.IMod + object modInstance = Activator.CreateInstance(type); + + string name = (string)nameProperty.GetValue(modInstance); + string version = (string)versionProperty.GetValue(modInstance); + initializeMethod.Invoke(modInstance, null); + + UnityEngine.Debug.Log($"Loaded {name}, v{version}"); + } + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Mods/ModLoader.cs.meta b/Assets/Scripts/Mods/ModLoader.cs.meta new file mode 100644 index 00000000..c2bf5efb --- /dev/null +++ b/Assets/Scripts/Mods/ModLoader.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ab9befd72784fff46bbe84536e22cf9a \ No newline at end of file diff --git a/Assets/Scripts/SaveSystem/SavePaths.cs b/Assets/Scripts/SaveSystem/SavePaths.cs index 7fe94974..64e495c6 100644 --- a/Assets/Scripts/SaveSystem/SavePaths.cs +++ b/Assets/Scripts/SaveSystem/SavePaths.cs @@ -16,6 +16,7 @@ public static class SavePaths public static readonly string ProjectsPath = Path.Combine(AllData, "Projects"); public static readonly string DeletedProjectsPath = Path.Combine(AllData, "Deleted Projects"); public static readonly string AppSettingsPath = Path.Combine(AllData, "AppSettings.json"); + public static readonly string ModsPath = Path.Combine(AllData, "Mods"); public static void EnsureDirectoryExists(string directoryPath) => Directory.CreateDirectory(directoryPath); diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt index 8678d93f..7b22109e 100644 --- a/ProjectSettings/ProjectVersion.txt +++ b/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 6000.0.46f1 -m_EditorVersionWithRevision: 6000.0.46f1 (fb93bc360d3a) +m_EditorVersion: 6000.0.47f1 +m_EditorVersionWithRevision: 6000.0.47f1 (2ad1ed33fd3b) diff --git a/TestData/Mods/ExampleMod.dls b/TestData/Mods/ExampleMod.dls new file mode 100644 index 0000000000000000000000000000000000000000..e6f5e0ffa431deb3d90f1d4cb650619b017e6e3e GIT binary patch literal 4608 zcmeHKU2Ggz75?t*dgD0G#&%6eNgF0{+HOO;-C$ESEp6<0?RKf1INr4z2vst>d%d1A zJF}V@Cw3)>LC_}!k`s8ZM)(4eh}MrZHH^V?fQ24T-9y|OBeA(MQ7je5OXDX`$>e?Th&L@@ z>`M$H&3J;}STzn^zZHpw$NX!u9$Tv7ccdkxV=pdM%B*jZpP*l3XS|?JUJ8d&uN?fY zl4ZraY8(fXSRNuPODA!al$M$%n(>5YKl2GKdcb>@(4zen^$RP9n+AV_A6f_SXR7D$ z4&GBa{7OB?)|UbVTyDHk7C?9j|E%$ zzj5d-&aQA_i<7ZhhP%D@B5LIPlzYk&Zav}DKj^W-pSJo5m&FV%dW-QICg$)UaR84K zhj7y1DT7l6D+bRS{H#HTn8OC~BXAAYU8pHP}Dd5M%B7TZ%^zF+==P^d* zgeqbmK93^TQWFp3tHd#UgZK&jmN<^T5}(6u;xztAeA&pW#A#f_`@{ia!_bYCZXlzw z^bEL!3&e5jf{9^*90}ii{(n zrQ=Iyowi1GBZ_sqxVYxWF4-P+%(Uye+0@Ocm|L>G7Hdog?T+i|@I~$G(20ASwv?74 zr=_&~^pXJ=kI*i=Fzq7I8 zuLNP+iP`9Q_tcsW{1vygM&ox3vZz<8&WesV78#sqd+@|zqr9j+XWdXe?On?jLWZ>w zZ+GlA$N-MNvE7gc+Q?(K?s{CF?bZrc%XK{W_1z0oOg-YlBRL^47qpV-rfX={G*#PL zR=wgehv=(GowAM^q1!S1g6sFPWjE@0&PK0jQMX(sM>xIecDVFj5JJf5P&>>@6eTyi z6O~=3Czt zi?9`=BugS16V9ulQRp>3h=EZuig z(sGF&mpyOZaecI-2Hn@p7IS+1zPb@`7UQ?~se0yd5u0 zChsWWrQEb8h|8$*23zL+yhy6Tm-bom72<62>c+3L@4TPfWwMk~lKOPQ@U~0dMJa2l zGEs3hI z;=WCO6Wpnc0iI?eDc;)w+US@$Ht3NmxEk$izn1r&V5$rFWaB$n(bIs8R~t)(Ug% z+cG`H;|@jMETq>7n$~;{^g-asypXN*Ay1;Sd%v@t^;vQ?kEV_GK zOMLk#?CiHYI@_@Uo-uKpqL&eUYjagSb8g?ePZCeM`r#{f@;_tK{6F;JbMOBi-9G~V E2Ap+}od5s; literal 0 HcmV?d00001 diff --git a/TestData/Projects/MainTest/ProjectDescription.json b/TestData/Projects/MainTest/ProjectDescription.json index 4516ae1a..ab3b5f63 100644 --- a/TestData/Projects/MainTest/ProjectDescription.json +++ b/TestData/Projects/MainTest/ProjectDescription.json @@ -1,9 +1,9 @@ { "ProjectName": "MainTest", - "DLSVersion_LastSaved": "2.1.4", + "DLSVersion_LastSaved": "2.1.5", "DLSVersion_EarliestCompatible": "2.0.0", - "CreationTime": "2025-03-14T18:23:30.404+01:00", - "LastSaveTime": "2025-04-25T17:44:29.842+02:00", + "CreationTime": "2025-03-14T17:23:30.404+00:00", + "LastSaveTime": "2025-05-03T00:10:21.928+01:00", "Prefs_MainPinNamesDisplayMode": 2, "Prefs_ChipPinNamesDisplayMode": 1, "Prefs_GridDisplayMode": 1, @@ -135,7 +135,7 @@ "Name":"TEST" }, { - "Chips":["PULSE","TEST MergeSplit"], + "Chips":["PULSE","TEST MergeSplit","#","hi"], "IsToggledOpen":true, "Name":"OTHER" } From 5d33754c57ace7db4d9b3c766061ec15511f851b Mon Sep 17 00:00:00 2001 From: Squiggles Date: Sat, 3 May 2025 21:29:19 +0100 Subject: [PATCH 02/11] feat(ModdingAPI): Load new chips on project load --- .../Description/Helpers/ChipTypeHelper.cs | 2 +- Assets/Scripts/Game/Main/Main.cs | 3 +- .../Scripts/Game/Project/ModdedChipCreator.cs | 1 - Assets/Scripts/ModdingAPI/IMod.cs | 3 ++ Assets/Scripts/Mods/LoadedMod.cs | 44 +++++++++++++++ Assets/Scripts/Mods/LoadedMod.cs.meta | 2 + Assets/Scripts/Mods/ModLoader.cs | 50 +++++++++++++----- Assets/Scripts/SaveSystem/Loader.cs | 2 + TestData/Mods/ExampleMod.dls | Bin 4608 -> 5120 bytes .../Projects/MainTest/ProjectDescription.json | 4 +- 10 files changed, 92 insertions(+), 19 deletions(-) create mode 100644 Assets/Scripts/Mods/LoadedMod.cs create mode 100644 Assets/Scripts/Mods/LoadedMod.cs.meta diff --git a/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs b/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs index e742d16c..e832ad30 100644 --- a/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs +++ b/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs @@ -63,7 +63,7 @@ public static class ChipTypeHelper public static ChipType AddNewModded(string name) { - ChipType type = (ChipType)(Names.Count + 30); // Hacky... + ChipType type = (ChipType)(Names.Count + 1); Names.Add(type, name); return type; } diff --git a/Assets/Scripts/Game/Main/Main.cs b/Assets/Scripts/Game/Main/Main.cs index 71436907..3c52eda9 100644 --- a/Assets/Scripts/Game/Main/Main.cs +++ b/Assets/Scripts/Game/Main/Main.cs @@ -25,7 +25,7 @@ public static void Init() { SavePaths.EnsureDirectoryExists(SavePaths.ProjectsPath); SavePaths.EnsureDirectoryExists(SavePaths.ModsPath); - ModLoader.LoadMods(SavePaths.ModsPath); + ModLoader.InitializeMods(SavePaths.ModsPath); SaveAndApplyAppSettings(Loader.LoadAppSettings()); } @@ -69,6 +69,7 @@ public static void CreateOrLoadProject(string projectName, string startupChipNam else ActiveProject = CreateProject(projectName); ActiveProject.LoadDevChipOrCreateNewIfDoesntExist(startupChipName); + ActiveProject.StartSimulation(); UIDrawer.SetActiveMenu(UIDrawer.MenuType.None); } diff --git a/Assets/Scripts/Game/Project/ModdedChipCreator.cs b/Assets/Scripts/Game/Project/ModdedChipCreator.cs index 6d8e2d80..2116c4c1 100644 --- a/Assets/Scripts/Game/Project/ModdedChipCreator.cs +++ b/Assets/Scripts/Game/Project/ModdedChipCreator.cs @@ -8,7 +8,6 @@ public static class ModdedChipCreator public static void RegisterChip(ChipType type, Vector2 size, Color col, PinDescription[] inputs = null, PinDescription[] outputs = null, DisplayDescription[] displays = null, bool hideName = false) { ChipDescription chipDescription = BuiltinChipCreator.CreateBuiltinChipDescription(type, size, col, inputs, outputs, displays, hideName); - BuiltinChipCreator.ModdedChips.Add(chipDescription); } diff --git a/Assets/Scripts/ModdingAPI/IMod.cs b/Assets/Scripts/ModdingAPI/IMod.cs index 3f65fa33..77a010a9 100644 --- a/Assets/Scripts/ModdingAPI/IMod.cs +++ b/Assets/Scripts/ModdingAPI/IMod.cs @@ -1,3 +1,5 @@ +using DLS.Game; + namespace DLS.ModdingAPI { public interface IMod @@ -5,5 +7,6 @@ public interface IMod string Name { get; } string Version { get; } void Initialize(); + void OnProjectLoad(); } } \ No newline at end of file diff --git a/Assets/Scripts/Mods/LoadedMod.cs b/Assets/Scripts/Mods/LoadedMod.cs new file mode 100644 index 00000000..78955e0e --- /dev/null +++ b/Assets/Scripts/Mods/LoadedMod.cs @@ -0,0 +1,44 @@ +using System.Reflection; +using DLS.Game; + +public class LoadedMod +{ + readonly string name; + readonly string version; + readonly object instance; + readonly MethodInfo initializeMethod; + readonly MethodInfo onProjectLoadMethod; + + public LoadedMod(string name, string version, object instance, MethodInfo initMethod, MethodInfo onProjectMethod) + { + this.name = name; + this.version = version; + this.instance = instance; + initializeMethod = initMethod; + onProjectLoadMethod = onProjectMethod; + } + + public void Initialize() + { + try + { + initializeMethod.Invoke(instance, null); + } + catch (System.Exception e) + { + throw new System.Exception($"Mod {name} failed to initialize:\n{e}"); + } + } + + public void OnProjectLoad() + { + try + { + onProjectLoadMethod.Invoke(instance, null); + } + catch (System.Exception e) + { + throw new System.Exception($"Mod {name} failed on project load:\n{e}"); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Mods/LoadedMod.cs.meta b/Assets/Scripts/Mods/LoadedMod.cs.meta new file mode 100644 index 00000000..2ed95ee9 --- /dev/null +++ b/Assets/Scripts/Mods/LoadedMod.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ef608844b5e118a4cac387ff1a356ed8 \ No newline at end of file diff --git a/Assets/Scripts/Mods/ModLoader.cs b/Assets/Scripts/Mods/ModLoader.cs index fa932499..4ca0a7bd 100644 --- a/Assets/Scripts/Mods/ModLoader.cs +++ b/Assets/Scripts/Mods/ModLoader.cs @@ -1,11 +1,14 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using DLS.Game; namespace DLS.Mods { public static class ModLoader { + static readonly List loadedMods = new(); static ModLoader() { AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly; @@ -20,32 +23,51 @@ private static Assembly ResolveAssembly(object sender, ResolveEventArgs args) return null; // Let the default resolver handle other assemblies } - public static void LoadMods(string modsDirectory) + public static void InitializeMods(string modsDirectory) { UnityEngine.Debug.Log("Loading mods..."); foreach (string dllPath in Directory.GetFiles(modsDirectory, "*.dls")) { Assembly modAssembly = Assembly.LoadFrom(dllPath); - foreach (Type type in modAssembly.GetTypes().Where(t => !t.IsAbstract)) + try { - // Check if the type has the same structure as IMod - var nameProperty = type.GetProperty("Name"); - var versionProperty = type.GetProperty("Version"); - var initializeMethod = type.GetMethod("Initialize"); - - if (nameProperty != null && versionProperty != null && initializeMethod != null) + foreach (Type type in modAssembly.GetTypes().Where(t => !t.IsAbstract)) { - // Ugly, but ModLoader.IMod would not be the same as ModdingAPI.IMod - object modInstance = Activator.CreateInstance(type); + // Check if the type has the same structure as IMod + var nameProperty = type.GetProperty("Name"); + var versionProperty = type.GetProperty("Version"); + var initializeMethod = type.GetMethod("Initialize"); + var onProjectLoadMethod = type.GetMethod("OnProjectLoad"); - string name = (string)nameProperty.GetValue(modInstance); - string version = (string)versionProperty.GetValue(modInstance); - initializeMethod.Invoke(modInstance, null); + if (nameProperty != null && versionProperty != null && initializeMethod != null && onProjectLoadMethod != null) + { + // Ugly, but ModLoader.IMod would not be the same as ModdingAPI.IMod + object modInstance = Activator.CreateInstance(type); - UnityEngine.Debug.Log($"Loaded {name}, v{version}"); + string name = (string) nameProperty.GetValue(modInstance); + string version = (string) versionProperty.GetValue(modInstance); + LoadedMod mod = new(name, version, modInstance, initializeMethod, onProjectLoadMethod); + loadedMods.Add(mod); + mod.Initialize(); + } + } + } + catch (ReflectionTypeLoadException ex) + { + foreach(Exception inner in ex.LoaderExceptions) + { + UnityEngine.Debug.LogError(inner.Message); } } } } + + public static void InvokeModsOnProjectLoad() + { + foreach (LoadedMod mod in loadedMods) + { + mod.OnProjectLoad(); + } + } } } \ No newline at end of file diff --git a/Assets/Scripts/SaveSystem/Loader.cs b/Assets/Scripts/SaveSystem/Loader.cs index 590a3af1..7646be51 100644 --- a/Assets/Scripts/SaveSystem/Loader.cs +++ b/Assets/Scripts/SaveSystem/Loader.cs @@ -4,6 +4,7 @@ using System.Linq; using DLS.Description; using DLS.Game; +using DLS.Mods; namespace DLS.SaveSystem { @@ -23,6 +24,7 @@ public static AppSettings LoadAppSettings() public static Project LoadProject(string projectName) { ProjectDescription projectDescription = LoadProjectDescription(projectName); + ModLoader.InvokeModsOnProjectLoad(); ChipLibrary chipLibrary = LoadChipLibrary(projectDescription); return new Project(projectDescription, chipLibrary); } diff --git a/TestData/Mods/ExampleMod.dls b/TestData/Mods/ExampleMod.dls index e6f5e0ffa431deb3d90f1d4cb650619b017e6e3e..ca01f9e9f052fdf93ad0dd75500a12dd1909f1c2 100644 GIT binary patch delta 1162 zcmY+ET}YE*6vzM1vk%>7b8gEtTTEv;r`bv^`jA)_CPi7x%7jRlm|C-@f(12&iU7pViyNRNp8g$WBH(hjDP}EIWSQPXD5kW%H|9MqxhCDl*23>aBwFu=ynS$8JpWz6sQ7lwTlsUa5B&!+sX02s?+ z;J=E_jP<$}elk6VQ#baFB1=>(+9OXFxr$nf!wRr<= z9sRW`W~p^hK=An02}>aDvS_XMJtiJYJKh>T!+n{LcYKx|zu;p0iD)L<3=0iw4euJh zhrW9iy2B5#f}C&BdVgaqPZ+b;lwqDyy%ndw8v$;T4wd7 z&*QtSXBtNo9t?9d?MWUFwqhw_)D|p8OzuWYP`}2RqT76+z@C4euI@J8dq)rU(o1H& z2C2QF8INss-Nmd|$5BNZ3;5;CHnn0S9Jnn73P?MONhc~v50)9OGK?8E8*VpjGwdMk zI7ZGzkJ00(vXtNws$~dThcUr;4Q`TkNZ-SGxdx*qwSwD;iW<1ki5mFON0#6m8G<6K z@RY2@8*&Xkk&XCCZZ`2a*@$*nMI(1@6$zsgS)G7GSo!wAVeBJga-ZI;x??NxHPO%h z7^%R$r^ZWt>Wj@Uc2J0RHP7BKXIru*)!*Bd7~Im|aUc`7Pje;6-^AApB7uXnM^u+% zeLN7vHYCx46#CJNF5Vo(7U~Wh0BkxDfo?K|LoDs&wz_CnV}LoGw7`uVPw&S?r<*30 zoVwojD*0JGa#Y$N-EMd0gQG~O-%jtGm)U;+J@7XbaFvKDwaWE86ZGsC8F${aMOs&9 L3OrxMcr@n^?Z3O~ delta 1021 zcmYL|TS$~a6vzKF-}L>0*~{*V<1rE!HTBsLU>=3*{vgizv6$5DQnLUfM0X z5k=WN^%C6(iXJMVpy(m$ErKY!QG`#@TTl@s6#Zw`Y+!!pf6knlb7sEXteY)Ae=3@8 zo;ZieF@|WwD;5I|19mRBt77=pR^eAd#Pfc*z$`USR;f-AcX|t$5#e;yX|YiF)O`_W z;%5;JH&o;{Z={jP!x+;-`G8OX-!ipXMlxl<{z|R}7f(Y=E*a9bSavYZCFy#!Ttw*> zvVuE<80?}O1+uFQ(tgJNySZ3^u}~;9(=DQ?oFmiXN+5O^ zSk1CkoUzuS!M=f>v`*^h^;5L5_Ex;4W-Y`vv^O@;j?u4G$E`$xej|NmP(86`XMW4v z`8JM<2hzr<;WJ#9Gw_bJY<$IUVdIfl!PO?ie8c&MR}J4ouMR_>@I|NM~ry$TUCR1%`^ST_$P!Np=YRv2?R+QTUO;xLi8r?Pa6dlbn96 zMh(_bTUdaE+=9&!z4JurC6*WHnDX>lJ;oa^=t3JZ0t1t7jcnuM!JH?)@y-Hor#5QO zgLQ1_5??Gp+6a>lDo8(;7}goCF>Ey4V%TZ8leBS&EJmNv!x_xB%5V{LWDHjra&eQa z#$9B1yCWtk&UdL4)$n2)su9F)vJA(_7|xOl@tjQHBe??OY z=`|On?5A9#FG@Rs} zB3REm4I+&p>Lm6drJfelhx2+F)+2FZ&!-y)UN_&Vz4qds=T%m9I29u&of4rgd;Qaw fPyYFIW{Z04EfWpugZIryVc~=&Q*|Q&-%s%u46vj* diff --git a/TestData/Projects/MainTest/ProjectDescription.json b/TestData/Projects/MainTest/ProjectDescription.json index ab3b5f63..887abe5d 100644 --- a/TestData/Projects/MainTest/ProjectDescription.json +++ b/TestData/Projects/MainTest/ProjectDescription.json @@ -3,7 +3,7 @@ "DLSVersion_LastSaved": "2.1.5", "DLSVersion_EarliestCompatible": "2.0.0", "CreationTime": "2025-03-14T17:23:30.404+00:00", - "LastSaveTime": "2025-05-03T00:10:21.928+01:00", + "LastSaveTime": "2025-05-03T21:25:00.452+01:00", "Prefs_MainPinNamesDisplayMode": 2, "Prefs_ChipPinNamesDisplayMode": 1, "Prefs_GridDisplayMode": 1, @@ -135,7 +135,7 @@ "Name":"TEST" }, { - "Chips":["PULSE","TEST MergeSplit","#","hi"], + "Chips":["PULSE","TEST MergeSplit","#","hi","girl bye :sob:"], "IsToggledOpen":true, "Name":"OTHER" } From 8152c51b48b4a1da5e4d52c383dd01697b87bd8f Mon Sep 17 00:00:00 2001 From: Squiggles Date: Sat, 3 May 2025 22:53:29 +0100 Subject: [PATCH 03/11] fix: modded chips duplicated in library on reload --- .../Description/Helpers/ChipTypeHelper.cs | 2 +- Assets/Scripts/Game/Project/ChipLibrary.cs | 3 +- Assets/Scripts/Game/Project/Project.cs | 32 ++++++++++++++++++ Assets/Scripts/ModdingAPI/IMod.cs | 2 -- TestData/Mods/ExampleMod.dls | Bin 5120 -> 5120 bytes .../Projects/MainTest/ProjectDescription.json | 4 +-- 6 files changed, 37 insertions(+), 6 deletions(-) diff --git a/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs b/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs index e832ad30..7bb46a23 100644 --- a/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs +++ b/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs @@ -63,7 +63,7 @@ public static class ChipTypeHelper public static ChipType AddNewModded(string name) { - ChipType type = (ChipType)(Names.Count + 1); + ChipType type = (ChipType)(Names.Count + 2); Names.Add(type, name); return type; } diff --git a/Assets/Scripts/Game/Project/ChipLibrary.cs b/Assets/Scripts/Game/Project/ChipLibrary.cs index 602a6427..eed1c956 100644 --- a/Assets/Scripts/Game/Project/ChipLibrary.cs +++ b/Assets/Scripts/Game/Project/ChipLibrary.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using DLS.Description; +using UnityEngine; namespace DLS.Game { @@ -9,7 +10,7 @@ public class ChipLibrary public readonly List allChips = new(); readonly HashSet builtinChipNames = new(ChipDescription.NameComparer); - readonly Dictionary descriptionFromNameLookup = new(ChipDescription.NameComparer); + Dictionary descriptionFromNameLookup = new(ChipDescription.NameComparer); readonly List hiddenChips = new(); diff --git a/Assets/Scripts/Game/Project/Project.cs b/Assets/Scripts/Game/Project/Project.cs index 0438a70a..9770f82a 100644 --- a/Assets/Scripts/Game/Project/Project.cs +++ b/Assets/Scripts/Game/Project/Project.cs @@ -339,6 +339,31 @@ public void DeleteChip(string chipToDeleteName) } } + public void DeleteModdedChip(ChipDescription chipToDelete) + { + bool simReloadRequired = ChipContainsSubchipIndirectly(ViewedChip, chipToDelete.Name); + + if (ChipContainsSubChipDirectly(ViewedChip, chipToDelete.Name)) + { + ViewedChip.UndoController.Clear(); + } + + BuiltinChipCreator.ModdedChips.RemoveAll(chip => ChipDescription.NameMatch(chip.Name, chipToDelete.Name)); + + // Delete chip save file, remove from library, and update project description + UpdateAndSaveAffectedChips(chipToDelete, null, true); + chipLibrary.RemoveChip(chipToDelete.Name); + SetStarred(chipToDelete.Name, false, false, false); // ensure removed from starred list + UpdateAndSaveProjectDescription(); + + // Remove any instances of the deleted chip from the active chip + ViewedChip.DeleteSubchipsByName(chipToDelete.Name); + if (simReloadRequired) + { + ViewedChip.RebuildSimulation(); + } + } + // Test if chip's subchips (or any of their subchips, etc...) contain the target subchip bool ChipContainsSubchipIndirectly(DevChipInstance devChip, string targetSubchip) { @@ -478,6 +503,13 @@ public void ToggleGridDisplay() public void NotifyExit() { simThreadActive = false; + + List moddedChipsCopy = new(BuiltinChipCreator.ModdedChips); + + foreach (ChipDescription chip in moddedChipsCopy) + { + DeleteModdedChip(chip); + } } void SimThread() diff --git a/Assets/Scripts/ModdingAPI/IMod.cs b/Assets/Scripts/ModdingAPI/IMod.cs index 77a010a9..4a792125 100644 --- a/Assets/Scripts/ModdingAPI/IMod.cs +++ b/Assets/Scripts/ModdingAPI/IMod.cs @@ -1,5 +1,3 @@ -using DLS.Game; - namespace DLS.ModdingAPI { public interface IMod diff --git a/TestData/Mods/ExampleMod.dls b/TestData/Mods/ExampleMod.dls index ca01f9e9f052fdf93ad0dd75500a12dd1909f1c2..9a5e01303e4eacc04c0887e0e4fd9b935a63091b 100644 GIT binary patch delta 1152 zcma)*TS!zv9LB$yW5?W$b=O@}yB=N9<64`dcJorEmQqHh=0?7Vnn~W+RVs?spiHPB zIRu$K)Kf_X$rAJw1-(`D)Jqqr7hfZajDqN!F^!;y<}kncfB%{J&o_I{j@qO4Cs`^}$ZAkz4s*0>RV39@;Er4>k`+dk1=A zYU8L0yyX~dZAN=CKsN~PYHg_H*hgLJoHNRW-+5xJj+=&HmiNO2@mQL8r}+Z+WGd7d zCceRB{;8-XTQq%|Va+?5AE0KhK~?x9ipg|C@c|UqGj^d8X{=^R6&K1hcpz{p&f$tY z1sA56O2c7!fa3_MOr^>EvgueDka&yXuu9sGd7Ti3fY8xmfAU%d5gyj zL>85*)uYvHN*7uX7Z~_0H_H}Ij?63ZL#T~cf2$32E|9F>%|g*6)Y0HADc~bbWRWfu zkX{sPZqcmPY|w1dJgC`0nsJ=qkd9s@Fo8T{B_@$CgSg3zjcGEB8I1F;XY`tU{z@Sc zh8yh&!;fxqCC-vTTp|ncj4Z($vK+Hy4L*~*b-tOb!6E!4U1WBr7M)sj@_RN!5^o9| zL4>T35l5J7{W}=*qgrhB=%2{;J3GyQXk@_Gbgg&}!#2~r= zcM(0PUhKR~*%+^B;R(Hn!Q!G`4D(ToEjm#7AwDOljxjgDIg5Re>}TO9jxsw8RQ_L$ z7}s{NS~qQ2tUkyp%e(beGRh+*b9<-u&YZlyzqM*^&WX4R66akR!nxu0dcN=^bM28RqV|JMKO{ zy-Hsz7BW54B_1gu%01H+K(Q*7)v8;RiySo}Qp?^mrk|Z$5+0P50w;0+p=y_GRP8dF z%4eYRvUDxLNtV!&uB~N^(hUGUH;<@9IPS$6(~F%JviItg;NO(wak?vk@offUZ0uWf z7h)mCB9Tae8>UE}WLQjddna<^?z?l3PlkjpL2r_fwS?ZbA2*lm^6Ixa&9KwzSf z=BAv04|f;}VMO-Oo|8SQ&km<1@k7=#jU$R6&TusCNv0Rkab8s#-wb(gh?&u z>BL11{OCsw!WbcoaFvWfkyUs>uE$%l0bj@_{3Lgoc!F#~4{XuIv$n;6(F0jMfLvj7 z_rNjql8v%gpRD@qDGImH=Y9J3v>}-;4XbJ=BKA?(J-j4pzf*5_G$%STR~@g#?+AH2 zxx*+1aD?^*;IHKz+zpA6DE43!?MUJ{hH;pQr?8v44~GDcE<(v5nZ!}H>F1#x zrrnG&=JeA754^$QPpdAq++B0=UiX{Puj;(7!UGuy1Ty!1g+e{`heEHje*k*;?^;LG fr3LbRMP}Y#C{u diff --git a/TestData/Projects/MainTest/ProjectDescription.json b/TestData/Projects/MainTest/ProjectDescription.json index 887abe5d..6a2c8c1f 100644 --- a/TestData/Projects/MainTest/ProjectDescription.json +++ b/TestData/Projects/MainTest/ProjectDescription.json @@ -3,7 +3,7 @@ "DLSVersion_LastSaved": "2.1.5", "DLSVersion_EarliestCompatible": "2.0.0", "CreationTime": "2025-03-14T17:23:30.404+00:00", - "LastSaveTime": "2025-05-03T21:25:00.452+01:00", + "LastSaveTime": "2025-05-03T22:51:12.178+01:00", "Prefs_MainPinNamesDisplayMode": 2, "Prefs_ChipPinNamesDisplayMode": 1, "Prefs_GridDisplayMode": 1, @@ -135,7 +135,7 @@ "Name":"TEST" }, { - "Chips":["PULSE","TEST MergeSplit","#","hi","girl bye :sob:"], + "Chips":["PULSE","TEST MergeSplit","#","This little chippy went to market","This little chippy stayed home"], "IsToggledOpen":true, "Name":"OTHER" } From de4ff1282e2d3b67bafbc56029e39ff723917da7 Mon Sep 17 00:00:00 2001 From: Squiggles Date: Sun, 4 May 2025 21:00:49 +0100 Subject: [PATCH 04/11] feat(ModdingAPI): modded chip functions --- .../Description/Helpers/ChipTypeHelper.cs | 9 +- .../Description/Types/SubTypes/ChipTypes.cs | 1 + .../Game/Project/BuiltinChipCreator.cs | 6 +- Assets/Scripts/Game/Project/ChipLibrary.cs | 14 ++- .../Scripts/Game/Project/ModdedChipCreator.cs | 93 +++++++++++++++--- Assets/Scripts/Game/Project/Project.cs | 5 +- Assets/Scripts/ModdingAPI/ChipCreator.cs | 20 ---- Assets/Scripts/ModdingAPI/ChipCreator.cs.meta | 2 - Assets/Scripts/ModdingAPI/IMod.cs | 1 - Assets/Scripts/ModdingAPI/Registry.cs | 21 ++++ Assets/Scripts/ModdingAPI/Registry.cs.meta | 2 + Assets/Scripts/Mods/LoadedMod.cs | 19 +--- Assets/Scripts/Mods/ModLoader.cs | 12 +-- Assets/Scripts/SaveSystem/Loader.cs | 5 +- Assets/Scripts/Simulation/SimChip.cs | 3 + Assets/Scripts/Simulation/Simulator.cs | 14 +++ TestData/Mods/ExampleMod.dls | Bin 5120 -> 5632 bytes .../Projects/MainTest/ProjectDescription.json | 4 +- 18 files changed, 150 insertions(+), 81 deletions(-) delete mode 100644 Assets/Scripts/ModdingAPI/ChipCreator.cs delete mode 100644 Assets/Scripts/ModdingAPI/ChipCreator.cs.meta create mode 100644 Assets/Scripts/ModdingAPI/Registry.cs create mode 100644 Assets/Scripts/ModdingAPI/Registry.cs.meta diff --git a/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs b/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs index 7bb46a23..e6c9659b 100644 --- a/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs +++ b/Assets/Scripts/Description/Helpers/ChipTypeHelper.cs @@ -48,7 +48,7 @@ public static class ChipTypeHelper { ChipType.Bus_8Bit, "BUS-8" }, { ChipType.BusTerminus_1Bit, "BUS-TERMINUS-1" }, { ChipType.BusTerminus_4Bit, "BUS-TERMINUS-4" }, - { ChipType.BusTerminus_8Bit, "BUS-TERMINUS-8" } + { ChipType.BusTerminus_8Bit, "BUS-TERMINUS-8" }, }; public static string GetName(ChipType type) => Names[type]; @@ -61,13 +61,6 @@ public static class ChipTypeHelper public static bool IsRomType(ChipType type) => type == ChipType.Rom_256x16; - public static ChipType AddNewModded(string name) - { - ChipType type = (ChipType)(Names.Count + 2); - Names.Add(type, name); - return type; - } - public static ChipType GetCorrespondingBusTerminusType(ChipType type) { return type switch diff --git a/Assets/Scripts/Description/Types/SubTypes/ChipTypes.cs b/Assets/Scripts/Description/Types/SubTypes/ChipTypes.cs index 5b9d9ce7..75721d59 100644 --- a/Assets/Scripts/Description/Types/SubTypes/ChipTypes.cs +++ b/Assets/Scripts/Description/Types/SubTypes/ChipTypes.cs @@ -3,6 +3,7 @@ namespace DLS.Description public enum ChipType { Custom, + Modded, // ---- Basic Chips ---- Nand, diff --git a/Assets/Scripts/Game/Project/BuiltinChipCreator.cs b/Assets/Scripts/Game/Project/BuiltinChipCreator.cs index 4d8775f8..1b9defc1 100644 --- a/Assets/Scripts/Game/Project/BuiltinChipCreator.cs +++ b/Assets/Scripts/Game/Project/BuiltinChipCreator.cs @@ -10,7 +10,6 @@ namespace DLS.Game public static class BuiltinChipCreator { static readonly Color ChipCol_SplitMerge = new(0.1f, 0.1f, 0.1f); //new(0.8f, 0.8f, 0.8f); - public static List ModdedChips = new(); public static ChipDescription[] CreateAllBuiltinChipDescriptions() { @@ -52,8 +51,7 @@ public static ChipDescription[] CreateAllBuiltinChipDescriptions() CreateBusTerminus(PinBitCount.Bit4), CreateBus(PinBitCount.Bit8), CreateBusTerminus(PinBitCount.Bit8), - // ---- Modded Chips ---- - }.Concat(ModdedChips.ToArray()).ToArray(); + }; } @@ -376,7 +374,7 @@ static ChipDescription CreateBusTerminus(PinBitCount bitCount) } - public static ChipDescription CreateBuiltinChipDescription(ChipType type, Vector2 size, Color col, PinDescription[] inputs = null, PinDescription[] outputs = null, DisplayDescription[] displays = null, bool hideName = false) + static ChipDescription CreateBuiltinChipDescription(ChipType type, Vector2 size, Color col, PinDescription[] inputs = null, PinDescription[] outputs = null, DisplayDescription[] displays = null, bool hideName = false) { string name = ChipTypeHelper.GetName(type); ValidatePinIDs(inputs, outputs, name); diff --git a/Assets/Scripts/Game/Project/ChipLibrary.cs b/Assets/Scripts/Game/Project/ChipLibrary.cs index eed1c956..0a041f46 100644 --- a/Assets/Scripts/Game/Project/ChipLibrary.cs +++ b/Assets/Scripts/Game/Project/ChipLibrary.cs @@ -10,11 +10,12 @@ public class ChipLibrary public readonly List allChips = new(); readonly HashSet builtinChipNames = new(ChipDescription.NameComparer); - Dictionary descriptionFromNameLookup = new(ChipDescription.NameComparer); + readonly HashSet moddedChipNames = new(ChipDescription.NameComparer); + readonly Dictionary descriptionFromNameLookup = new(ChipDescription.NameComparer); readonly List hiddenChips = new(); - public ChipLibrary(ChipDescription[] customChips, ChipDescription[] builtinChips) + public ChipLibrary(ChipDescription[] customChips, ChipDescription[] builtinChips, ChipDescription[] moddedChips) { // Add built-in chips to list of all chips foreach (ChipDescription chip in builtinChips) @@ -26,6 +27,13 @@ public ChipLibrary(ChipDescription[] customChips, ChipDescription[] builtinChips builtinChipNames.Add(chip.Name); } + // Add modded chips to list of all chips + foreach (ChipDescription chip in moddedChips) + { + AddChipToLibrary(chip); + moddedChipNames.Add(chip.Name); + } + // Add custom chips to list of all chips foreach (ChipDescription chip in customChips) { @@ -106,7 +114,7 @@ public string[] GetAllCustomChipNames() foreach (ChipDescription chip in allChips) { - if (!IsBuiltinChip(chip.Name)) + if (!IsBuiltinChip(chip.Name) && !(chip.ChipType == ChipType.Modded)) { customChipNames.Add(chip.Name); } diff --git a/Assets/Scripts/Game/Project/ModdedChipCreator.cs b/Assets/Scripts/Game/Project/ModdedChipCreator.cs index 2116c4c1..3668be10 100644 --- a/Assets/Scripts/Game/Project/ModdedChipCreator.cs +++ b/Assets/Scripts/Game/Project/ModdedChipCreator.cs @@ -1,24 +1,95 @@ +using System; +using System.Collections.Generic; using DLS.Description; +using DLS.Simulation; using UnityEngine; namespace DLS.Game { public static class ModdedChipCreator { - public static void RegisterChip(ChipType type, Vector2 size, Color col, PinDescription[] inputs = null, PinDescription[] outputs = null, DisplayDescription[] displays = null, bool hideName = false) + public static List ModdedChips = new(); + private static readonly Dictionary> ModdedChipFunctions = new(); + + public static ChipDescription[] CreateAllModdedChipDescriptions() + { + return ModdedChips.ToArray(); + } + + public static void RegisterChip( + string name, + Vector2 size, + Color col, + PinDescription[] inputs = null, + PinDescription[] outputs = null, + DisplayDescription[] displays = null, + bool hideName = false, + Action simulationFunction = null) + { + // Register the chip description + ChipDescription chipDescription = CreateModdedChipDescription(name, ChipType.Modded, size, col, inputs, outputs, displays, hideName); + ModdedChips.Add(chipDescription); + + // Register the simulation function + if (simulationFunction != null) + { + ModdedChipFunctions[chipDescription] = simulationFunction; + } + } + + static ChipDescription CreateModdedChipDescription(string name, ChipType type, Vector2 size, Color col, PinDescription[] inputs = null, PinDescription[] outputs = null, DisplayDescription[] displays = null, bool hideName = false) { - ChipDescription chipDescription = BuiltinChipCreator.CreateBuiltinChipDescription(type, size, col, inputs, outputs, displays, hideName); - BuiltinChipCreator.ModdedChips.Add(chipDescription); + ValidatePinIDs(inputs, outputs, name); + + return new ChipDescription + { + Name = name, + NameLocation = hideName ? NameDisplayLocation.Hidden : NameDisplayLocation.Centre, + Colour = col, + Size = new Vector2(size.x, size.y), + InputPins = inputs ?? Array.Empty(), + OutputPins = outputs ?? Array.Empty(), + SubChips = Array.Empty(), + Wires = Array.Empty(), + Displays = displays, + ChipType = type + }; } + public static bool TryGetSimulationFunction(ChipDescription chipDescription, out Action simulationFunction) + { + return ModdedChipFunctions.TryGetValue(chipDescription, out simulationFunction); + } + public static PinDescription CreatePinDescription(string name, int id, PinBitCount bitCount = PinBitCount.Bit1) => - new( - name, - id, - Vector2.zero, - bitCount, - PinColour.Red, - PinValueDisplayMode.Off - ); + new( + name, + id, + Vector2.zero, + bitCount, + PinColour.Red, + PinValueDisplayMode.Off + ); + + static void ValidatePinIDs(PinDescription[] inputs, PinDescription[] outputs, string chipName) + { + HashSet pinIDs = new(); + + AddPins(inputs); + AddPins(outputs); + return; + + void AddPins(PinDescription[] pins) + { + if (pins == null) return; + foreach (PinDescription pin in pins) + { + if (!pinIDs.Add(pin.ID)) + { + throw new Exception($"Pin has duplicate ID ({pin.ID}) in modded chip: {chipName}"); + } + } + } + } } } \ No newline at end of file diff --git a/Assets/Scripts/Game/Project/Project.cs b/Assets/Scripts/Game/Project/Project.cs index 9770f82a..f47cdc6e 100644 --- a/Assets/Scripts/Game/Project/Project.cs +++ b/Assets/Scripts/Game/Project/Project.cs @@ -348,11 +348,12 @@ public void DeleteModdedChip(ChipDescription chipToDelete) ViewedChip.UndoController.Clear(); } - BuiltinChipCreator.ModdedChips.RemoveAll(chip => ChipDescription.NameMatch(chip.Name, chipToDelete.Name)); + ModdedChipCreator.ModdedChips.RemoveAll(chip => ChipDescription.NameMatch(chip.Name, chipToDelete.Name)); // Delete chip save file, remove from library, and update project description UpdateAndSaveAffectedChips(chipToDelete, null, true); chipLibrary.RemoveChip(chipToDelete.Name); + EnsureChipRemovedFromCollections(chipToDelete.Name); SetStarred(chipToDelete.Name, false, false, false); // ensure removed from starred list UpdateAndSaveProjectDescription(); @@ -504,7 +505,7 @@ public void NotifyExit() { simThreadActive = false; - List moddedChipsCopy = new(BuiltinChipCreator.ModdedChips); + List moddedChipsCopy = new(ModdedChipCreator.ModdedChips); foreach (ChipDescription chip in moddedChipsCopy) { diff --git a/Assets/Scripts/ModdingAPI/ChipCreator.cs b/Assets/Scripts/ModdingAPI/ChipCreator.cs deleted file mode 100644 index 5384f8ca..00000000 --- a/Assets/Scripts/ModdingAPI/ChipCreator.cs +++ /dev/null @@ -1,20 +0,0 @@ -using DLS.Description; -using DLS.Game; -using UnityEngine; - -namespace DLS.ModdingAPI -{ - public static class ChipCreator - { - public static void RegisterChip(string name, Vector2 size, Color col, PinDescription[] inputs = null, PinDescription[] outputs = null, DisplayDescription[] displays = null, bool hideName = false) - { - ChipType type = ChipTypeHelper.AddNewModded(name); - ModdedChipCreator.RegisterChip(type, size, col, inputs, outputs, displays, hideName); - } - - public static PinDescription CreatePinDescription(string name, int id, PinBitCount bitCount = PinBitCount.Bit1) - { - return ModdedChipCreator.CreatePinDescription(name, id, bitCount); - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/ChipCreator.cs.meta b/Assets/Scripts/ModdingAPI/ChipCreator.cs.meta deleted file mode 100644 index aeb9ee2f..00000000 --- a/Assets/Scripts/ModdingAPI/ChipCreator.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: e798728c9b0672a4c91de977d122a933 \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/IMod.cs b/Assets/Scripts/ModdingAPI/IMod.cs index 4a792125..3f65fa33 100644 --- a/Assets/Scripts/ModdingAPI/IMod.cs +++ b/Assets/Scripts/ModdingAPI/IMod.cs @@ -5,6 +5,5 @@ public interface IMod string Name { get; } string Version { get; } void Initialize(); - void OnProjectLoad(); } } \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/Registry.cs b/Assets/Scripts/ModdingAPI/Registry.cs new file mode 100644 index 00000000..8e85b7de --- /dev/null +++ b/Assets/Scripts/ModdingAPI/Registry.cs @@ -0,0 +1,21 @@ +using System; +using DLS.Description; +using DLS.Game; +using DLS.Simulation; +using UnityEngine; + +public static class Registry +{ + public static void RegisterChip( + string name, + Vector2 size, + Color col, + PinDescription[] inputs = null, + PinDescription[] outputs = null, + DisplayDescription[] displays = null, + bool hideName = false, + Action simulationFunction = null) + { + ModdedChipCreator.RegisterChip(name, size, col, inputs, outputs, displays, hideName, simulationFunction); + } +} \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/Registry.cs.meta b/Assets/Scripts/ModdingAPI/Registry.cs.meta new file mode 100644 index 00000000..dfbdc9e3 --- /dev/null +++ b/Assets/Scripts/ModdingAPI/Registry.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 608a3f0c921587f4d9eaa2b21f01ad20 \ No newline at end of file diff --git a/Assets/Scripts/Mods/LoadedMod.cs b/Assets/Scripts/Mods/LoadedMod.cs index 78955e0e..d4c3b645 100644 --- a/Assets/Scripts/Mods/LoadedMod.cs +++ b/Assets/Scripts/Mods/LoadedMod.cs @@ -1,5 +1,5 @@ +using UnityEngine; using System.Reflection; -using DLS.Game; public class LoadedMod { @@ -9,13 +9,12 @@ public class LoadedMod readonly MethodInfo initializeMethod; readonly MethodInfo onProjectLoadMethod; - public LoadedMod(string name, string version, object instance, MethodInfo initMethod, MethodInfo onProjectMethod) + public LoadedMod(string name, string version, object instance, MethodInfo initMethod) { this.name = name; this.version = version; this.instance = instance; initializeMethod = initMethod; - onProjectLoadMethod = onProjectMethod; } public void Initialize() @@ -26,19 +25,7 @@ public void Initialize() } catch (System.Exception e) { - throw new System.Exception($"Mod {name} failed to initialize:\n{e}"); - } - } - - public void OnProjectLoad() - { - try - { - onProjectLoadMethod.Invoke(instance, null); - } - catch (System.Exception e) - { - throw new System.Exception($"Mod {name} failed on project load:\n{e}"); + Debug.LogError($"Mod {name} failed to initialize:\n{e}"); } } } \ No newline at end of file diff --git a/Assets/Scripts/Mods/ModLoader.cs b/Assets/Scripts/Mods/ModLoader.cs index 4ca0a7bd..be4f0a7e 100644 --- a/Assets/Scripts/Mods/ModLoader.cs +++ b/Assets/Scripts/Mods/ModLoader.cs @@ -37,16 +37,15 @@ public static void InitializeMods(string modsDirectory) var nameProperty = type.GetProperty("Name"); var versionProperty = type.GetProperty("Version"); var initializeMethod = type.GetMethod("Initialize"); - var onProjectLoadMethod = type.GetMethod("OnProjectLoad"); - if (nameProperty != null && versionProperty != null && initializeMethod != null && onProjectLoadMethod != null) + if (nameProperty != null && versionProperty != null && initializeMethod != null) { // Ugly, but ModLoader.IMod would not be the same as ModdingAPI.IMod object modInstance = Activator.CreateInstance(type); string name = (string) nameProperty.GetValue(modInstance); string version = (string) versionProperty.GetValue(modInstance); - LoadedMod mod = new(name, version, modInstance, initializeMethod, onProjectLoadMethod); + LoadedMod mod = new(name, version, modInstance, initializeMethod); loadedMods.Add(mod); mod.Initialize(); } @@ -62,12 +61,5 @@ public static void InitializeMods(string modsDirectory) } } - public static void InvokeModsOnProjectLoad() - { - foreach (LoadedMod mod in loadedMods) - { - mod.OnProjectLoad(); - } - } } } \ No newline at end of file diff --git a/Assets/Scripts/SaveSystem/Loader.cs b/Assets/Scripts/SaveSystem/Loader.cs index 7646be51..70ddbfd6 100644 --- a/Assets/Scripts/SaveSystem/Loader.cs +++ b/Assets/Scripts/SaveSystem/Loader.cs @@ -24,7 +24,6 @@ public static AppSettings LoadAppSettings() public static Project LoadProject(string projectName) { ProjectDescription projectDescription = LoadProjectDescription(projectName); - ModLoader.InvokeModsOnProjectLoad(); ChipLibrary chipLibrary = LoadChipLibrary(projectDescription); return new Project(projectDescription, chipLibrary); } @@ -90,6 +89,8 @@ static ChipLibrary LoadChipLibrary(ProjectDescription projectDescription) ChipDescription[] builtinChips = BuiltinChipCreator.CreateAllBuiltinChipDescriptions(); HashSet customChipNameHashset = new(ChipDescription.NameComparer); + ChipDescription[] moddedChips = ModdedChipCreator.CreateAllModdedChipDescriptions(); + for (int i = 0; i < loadedChips.Length; i++) { string chipPath = Path.Combine(chipDirectoryPath, projectDescription.AllCustomChipNames[i] + ".json"); @@ -106,7 +107,7 @@ static ChipLibrary LoadChipLibrary(ProjectDescription projectDescription) builtinChips = builtinChips.Where(b => !customChipNameHashset.Contains(b.Name)).ToArray(); UpgradeHelper.ApplyVersionChanges(loadedChips, builtinChips); - return new ChipLibrary(loadedChips, builtinChips); + return new ChipLibrary(loadedChips, builtinChips, moddedChips); } } } \ No newline at end of file diff --git a/Assets/Scripts/Simulation/SimChip.cs b/Assets/Scripts/Simulation/SimChip.cs index 5efec7c5..ffc868ff 100644 --- a/Assets/Scripts/Simulation/SimChip.cs +++ b/Assets/Scripts/Simulation/SimChip.cs @@ -20,6 +20,8 @@ public class SimChip public SimPin[] OutputPins = Array.Empty(); public SimChip[] SubChips = Array.Empty(); + public ChipDescription Description {get; private set;} + public SimChip() { @@ -32,6 +34,7 @@ public SimChip(ChipDescription desc, int id, uint[] internalState, SimChip[] sub ID = id; ChipType = desc.ChipType; IsBuiltin = ChipType != ChipType.Custom; + Description = desc; // ---- Create pins (don't allocate unnecessarily as very many sim chips maybe created!) ---- if (desc.InputPins.Length > 0) diff --git a/Assets/Scripts/Simulation/Simulator.cs b/Assets/Scripts/Simulation/Simulator.cs index 207385b0..bfeaca14 100644 --- a/Assets/Scripts/Simulation/Simulator.cs +++ b/Assets/Scripts/Simulation/Simulator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using UnityEngine; using DLS.Description; using DLS.Game; using Random = System.Random; @@ -472,6 +473,19 @@ static void ProcessBuiltinChip(SimChip chip) chip.OutputPins[1].State = (ushort)(data & ByteMask); break; } + case ChipType.Modded: + { + if (ModdedChipCreator.TryGetSimulationFunction(chip.Description, out var simulationFunction)) + { + // Call the modded chip's simulation function + simulationFunction(chip.InputPins, chip.OutputPins); + } + else + { + Debug.LogWarning($"No simulation function registered for modded chip: {chip.Description.Name}"); + } + break; + } // ---- Bus types ---- default: { diff --git a/TestData/Mods/ExampleMod.dls b/TestData/Mods/ExampleMod.dls index 9a5e01303e4eacc04c0887e0e4fd9b935a63091b..eb8ee2e900da25e011545415597a7e339167fd11 100644 GIT binary patch delta 2060 zcmb7FZERCj7=F&Z-K8J1wB6{2W4I5u)oqLo1Ov{&C>sm}rX7q0=T_P}O18GPcZ3OB zhX#Wv!g6CGL=$mJqGAGBP>G2Forxxx5F#4?h=S2+OpGQNA;$Qe+qyygVLa`5?)$v& zd)|+8PH#tjqqRp4l$~k%^DwgG838&LIC~Abuct=!;eB*0THg}Saa;)W4kb|E~fdh6x7!A4i z8SP5NY#Q?F?YK)wH^A7Xl%$iLK%xv7UEyOr8l`hhDHmWjbu*B1Q&(`Pknt+K$siYb z3w$2xUX!Y6!vGBhw-|iPRpT8|0ZYY#g31wv!e1Jz(o*?cT#+7?Cf^*&$vyw05+4KA z{8v`X?O7euSx2SCOxjkxRPlM4z0C3Dcw<-fe8pElR$Z5}(R);uJFRQAc#o;xV|&=J zeON+uH8lkp5yot_;(?3orhEiwWP-an%L95c8tY5R?e4YmJoqf%9KaN1ZZgW(Y;If4 z=O5%pjK|gd`hr(%Hf|4Yip5}E@~Gx>=5R6|*n=rcJxhSwIIZTTC6gZV$&xufQl-om z@Rb-=HPk2)i^NWKHeP0t8d^n%qQRqFBfcsm_L#WW#2Y3qG;s=Y?F!`teJ-gIRUz>P zWjpqwGtF?za6)n7yz)5>F}m6@q;$fARhT5UsFUo*pv;ldX?6s(VNLaZoZ(wlJSIVx@Ejp{-Tb-aFiaIUx5~UdXlz>6({pc6JsR6$ zQ9T;$k3JD;+MZQqYz_amHJ~}9N8_>HMc5Jv>G9+u8OR80RkKz<7@!<7UetV2u3Hlv zFh0;KEdf2KPh8dB621Ole`v5js7KZ$qv1d-m`Fszozbwq1K~(2n*4_ZhEjTD0Mel& zE?ryW$xx(aS0si&bf7&NLtjMi*%C>nSO{b)s%2c`C2OVTPez!*_|UqrMy_2O=}$zG z@W=b(NoJu2QBx;-AQ~3i$jWFeF{q~yAJomKKOPGOb!^(clbwQjS4DMy zd@!b?IhhO&;o(SMG^Hnp&@zzFhq(X7VcXVbTRU%NfFC&85JLik(1ANPna?b9`1v=u zkv<(#!sFmCL%FG*XWx`R%uJF2+^is!>(7w!T398a54?AgF? zPKhGs70!cIINe?D?qYY){0=BHE^qy$zC)XFR7>EL zKS3K~W}>UcW&01t`wq9~z3l(3yj_>1TryVbRVHgEes^?;38(X>s(jZt;r9F@#_DJN E4Y#>^3IG5A delta 1361 zcma)+U1*zC7{~wTyfI0W^ld)6x-UmQwkAtw*4YhpUv^E!mCd!aA4_pEvoyBVG$~1n z6vt9wjG_#z2hpw~6EEfzVWTMGl^45H@7$FiOpqdYF@{2ALh*l2vg)Ph&H0`4{GaDM zdCqx1=J(7e-dc$EroOz5M!SP(*ef~#9|dP!+>u=TWYqq4H_)$zZlKdzjD&nI)}wCF zv0X*v9yKQ7o}C_sgxP)S4bdTN^@WIwp!!)vw;)NG?Kr!{L-0p{t8PH3d-mgMK<*Nk z)I}NIobAC~TU%S(5<$RIUjDD|XvI&Bp}@bW1gLE+ zNbZn{5QU`r-P%>VBZ1fj;G_lYXO9h4%TvXPiq0_4&HuMJ&+pdZxZQK`KXrsXCW_Aa=o^zQQS%J={lp|2nKAyCt{rbe;6kkgx zelq+HpUD>JDxLTXPU^QrFF9-&FiaVKYWNGZ_X>1|dtx8C$gVuZF)j#YqoBQzlC1-Uc=hU$^~2zusulX zz|$=7niL3-PHZ7P*iHJe&+tjZV}^Z(gN9?8HDozBF-LB~l$kH0-P(pF#AOU0Qc2)C znZivhaIdeMm^e=MoX;{7QtGnzLUX!Q%w?Jp0Q%`#ga Date: Sun, 4 May 2025 23:46:04 +0100 Subject: [PATCH 05/11] feat(ModdingAPI): Modded collections known issue: collections appear to duplicate on reload --- .../Types/SubTypes/PinDescription.cs | 10 +++ Assets/Scripts/Game/Main/Main.cs | 4 ++ .../Scripts/Game/Project/ModdedChipCreator.cs | 10 --- .../Game/Project/ModdedCollectionCreator.cs | 20 ++++++ .../Project/ModdedCollectionCreator.cs.meta | 2 + Assets/Scripts/Game/Project/Project.cs | 7 +- .../Graphics/UI/Menus/ChipLibraryMenu.cs | 16 +++++ Assets/Scripts/ModdingAPI/ChipBuilder.cs | 66 ++++++++++++++++++ Assets/Scripts/ModdingAPI/ChipBuilder.cs.meta | 2 + .../Scripts/ModdingAPI/CollectionBuilder.cs | 22 ++++++ .../ModdingAPI/CollectionBuilder.cs.meta | 2 + Assets/Scripts/ModdingAPI/Debug.cs | 10 --- Assets/Scripts/ModdingAPI/Debug.cs.meta | 2 - Assets/Scripts/ModdingAPI/Registry.cs | 33 +++++---- TestData/Mods/ExampleMod.dls | Bin 5632 -> 6144 bytes .../Projects/MainTest/ProjectDescription.json | 6 +- 16 files changed, 171 insertions(+), 41 deletions(-) create mode 100644 Assets/Scripts/Game/Project/ModdedCollectionCreator.cs create mode 100644 Assets/Scripts/Game/Project/ModdedCollectionCreator.cs.meta create mode 100644 Assets/Scripts/ModdingAPI/ChipBuilder.cs create mode 100644 Assets/Scripts/ModdingAPI/ChipBuilder.cs.meta create mode 100644 Assets/Scripts/ModdingAPI/CollectionBuilder.cs create mode 100644 Assets/Scripts/ModdingAPI/CollectionBuilder.cs.meta delete mode 100644 Assets/Scripts/ModdingAPI/Debug.cs delete mode 100644 Assets/Scripts/ModdingAPI/Debug.cs.meta diff --git a/Assets/Scripts/Description/Types/SubTypes/PinDescription.cs b/Assets/Scripts/Description/Types/SubTypes/PinDescription.cs index 62745260..8176f495 100644 --- a/Assets/Scripts/Description/Types/SubTypes/PinDescription.cs +++ b/Assets/Scripts/Description/Types/SubTypes/PinDescription.cs @@ -20,6 +20,16 @@ public PinDescription(string name, int id, Vector2 position, PinBitCount bitCoun Colour = colour; ValueDisplayMode = valueDisplayMode; } + + public PinDescription(string name, int id) + { + Name = name; + ID = id; + Position = Vector2.zero; + BitCount = PinBitCount.Bit1; + Colour = PinColour.Red; + ValueDisplayMode = PinValueDisplayMode.Off; + } } public enum PinBitCount diff --git a/Assets/Scripts/Game/Main/Main.cs b/Assets/Scripts/Game/Main/Main.cs index 3c52eda9..8237306c 100644 --- a/Assets/Scripts/Game/Main/Main.cs +++ b/Assets/Scripts/Game/Main/Main.cs @@ -68,6 +68,10 @@ public static void CreateOrLoadProject(string projectName, string startupChipNam if (Loader.ProjectExists(projectName)) ActiveProject = LoadProject(projectName); else ActiveProject = CreateProject(projectName); + foreach(var coll in ActiveProject.description.ChipCollections) + Debug.Log(coll.Name); + ActiveProject.description.ChipCollections.AddRange(ModdedCollectionCreator.CreateModdedChipCollections()); // Todo: This seems to be inconsistent + ActiveProject.LoadDevChipOrCreateNewIfDoesntExist(startupChipName); ActiveProject.StartSimulation(); diff --git a/Assets/Scripts/Game/Project/ModdedChipCreator.cs b/Assets/Scripts/Game/Project/ModdedChipCreator.cs index 3668be10..6907715e 100644 --- a/Assets/Scripts/Game/Project/ModdedChipCreator.cs +++ b/Assets/Scripts/Game/Project/ModdedChipCreator.cs @@ -60,16 +60,6 @@ public static bool TryGetSimulationFunction(ChipDescription chipDescription, out { return ModdedChipFunctions.TryGetValue(chipDescription, out simulationFunction); } - - public static PinDescription CreatePinDescription(string name, int id, PinBitCount bitCount = PinBitCount.Bit1) => - new( - name, - id, - Vector2.zero, - bitCount, - PinColour.Red, - PinValueDisplayMode.Off - ); static void ValidatePinIDs(PinDescription[] inputs, PinDescription[] outputs, string chipName) { diff --git a/Assets/Scripts/Game/Project/ModdedCollectionCreator.cs b/Assets/Scripts/Game/Project/ModdedCollectionCreator.cs new file mode 100644 index 00000000..34225f14 --- /dev/null +++ b/Assets/Scripts/Game/Project/ModdedCollectionCreator.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Linq; +using DLS.Description; + +namespace DLS.Game +{ + public static class ModdedCollectionCreator + { + public static List ModdedCollections = new(); + + public static ChipCollection[] CreateModdedChipCollections() + { + return ModdedCollections.ToArray(); + } + public static void RegisterCollection(string name, string[] chipNames) + { + ModdedCollections.Add(new ChipCollection(name, chipNames)); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Game/Project/ModdedCollectionCreator.cs.meta b/Assets/Scripts/Game/Project/ModdedCollectionCreator.cs.meta new file mode 100644 index 00000000..d577f1e5 --- /dev/null +++ b/Assets/Scripts/Game/Project/ModdedCollectionCreator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 893d399dbc5f0524fb19be71c34bb475 \ No newline at end of file diff --git a/Assets/Scripts/Game/Project/Project.cs b/Assets/Scripts/Game/Project/Project.cs index f47cdc6e..d7bf2852 100644 --- a/Assets/Scripts/Game/Project/Project.cs +++ b/Assets/Scripts/Game/Project/Project.cs @@ -506,11 +506,16 @@ public void NotifyExit() simThreadActive = false; List moddedChipsCopy = new(ModdedChipCreator.ModdedChips); - foreach (ChipDescription chip in moddedChipsCopy) { DeleteModdedChip(chip); } + + List moddedCollectionsCopy = new(ModdedCollectionCreator.ModdedCollections); + foreach (ChipCollection collection in moddedCollectionsCopy) + { + ChipLibraryMenu.DeleteCollection(collection); + } } void SimThread() diff --git a/Assets/Scripts/Graphics/UI/Menus/ChipLibraryMenu.cs b/Assets/Scripts/Graphics/UI/Menus/ChipLibraryMenu.cs index 62fb9a42..f21238c3 100644 --- a/Assets/Scripts/Graphics/UI/Menus/ChipLibraryMenu.cs +++ b/Assets/Scripts/Graphics/UI/Menus/ChipLibraryMenu.cs @@ -643,6 +643,22 @@ static void DeleteSelectedCollection() project.SaveCurrentProjectDescription(); } + public static void DeleteCollection(ChipCollection collectionToDelete) + { + Debug.Log("Deleting collection"); + ChipCollection defaultCollection = GetDefaultCollection(); + + foreach (string chipName in collectionToDelete.Chips) + { + defaultCollection.Chips.Add(chipName); + } + + project.SetStarred(collectionToDelete.Name, false, true, false); + collections.Remove(collectionToDelete); + + project.SaveCurrentProjectDescription(); + } + static ChipCollection GetDefaultCollection() => collections.FirstOrDefault(c => ChipDescription.NameMatch(c.Name, defaultOtherChipsCollectionName)); static bool ValidateCollectionNameInput(string name) diff --git a/Assets/Scripts/ModdingAPI/ChipBuilder.cs b/Assets/Scripts/ModdingAPI/ChipBuilder.cs new file mode 100644 index 00000000..65b604f2 --- /dev/null +++ b/Assets/Scripts/ModdingAPI/ChipBuilder.cs @@ -0,0 +1,66 @@ +using System; +using DLS.Description; +using DLS.Simulation; +using UnityEngine; + +namespace DLS.ModdingAPI +{ + public class ChipBuilder + { + public readonly string name; + public Vector2 size = Vector2.one; + public Color color = Color.white; + public PinDescription[] inputs = null; + public PinDescription[] outputs = null; + public DisplayDescription[] displays = null; + public bool hideName = false; + public Action simulationFunction = null; + + public ChipBuilder(string name) + { + this.name = name; + } + + public ChipBuilder SetSize(Vector2 size) + { + this.size = size; + return this; + } + + public ChipBuilder SetColor(Color color) + { + this.color = color; + return this; + } + + public ChipBuilder SetInputs(PinDescription[] inputs) + { + this.inputs = inputs; + return this; + } + + public ChipBuilder SetOutputs(PinDescription[] outputs) + { + this.outputs = outputs; + return this; + } + + public ChipBuilder SetDisplays(DisplayDescription[] displays) + { + this.displays = displays; + return this; + } + + public ChipBuilder HideName(bool hide = true) + { + this.hideName = hide; + return this; + } + + public ChipBuilder SetSimulationFunction(Action simulationFunction) + { + this.simulationFunction = simulationFunction; + return this; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/ChipBuilder.cs.meta b/Assets/Scripts/ModdingAPI/ChipBuilder.cs.meta new file mode 100644 index 00000000..09cb8775 --- /dev/null +++ b/Assets/Scripts/ModdingAPI/ChipBuilder.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d225081c06659cc44a5c898a31668903 \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/CollectionBuilder.cs b/Assets/Scripts/ModdingAPI/CollectionBuilder.cs new file mode 100644 index 00000000..396138ad --- /dev/null +++ b/Assets/Scripts/ModdingAPI/CollectionBuilder.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace DLS.ModdingAPI +{ + public class CollectionBuilder + { + public readonly string name; + public List chips; + + public CollectionBuilder(string name) + { + this.name = name; + chips = new(); + } + + public CollectionBuilder AddChip(ChipBuilder chip) + { + chips.Add(chip); + return this; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/CollectionBuilder.cs.meta b/Assets/Scripts/ModdingAPI/CollectionBuilder.cs.meta new file mode 100644 index 00000000..bccc5f29 --- /dev/null +++ b/Assets/Scripts/ModdingAPI/CollectionBuilder.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 04c2257c20452374dab4167bb79b9d9f \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/Debug.cs b/Assets/Scripts/ModdingAPI/Debug.cs deleted file mode 100644 index 796ca23d..00000000 --- a/Assets/Scripts/ModdingAPI/Debug.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace DLS.ModdingAPI -{ - public static class Debug - { - public static void Log(object toLog) - { - UnityEngine.Debug.Log(toLog); - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/Debug.cs.meta b/Assets/Scripts/ModdingAPI/Debug.cs.meta deleted file mode 100644 index 331b522b..00000000 --- a/Assets/Scripts/ModdingAPI/Debug.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 0bc8960d8fa5b004fbee2bc9e42d7fee \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/Registry.cs b/Assets/Scripts/ModdingAPI/Registry.cs index 8e85b7de..00e1bd54 100644 --- a/Assets/Scripts/ModdingAPI/Registry.cs +++ b/Assets/Scripts/ModdingAPI/Registry.cs @@ -1,21 +1,24 @@ -using System; -using DLS.Description; +using System.Linq; using DLS.Game; -using DLS.Simulation; -using UnityEngine; -public static class Registry +namespace DLS.ModdingAPI { - public static void RegisterChip( - string name, - Vector2 size, - Color col, - PinDescription[] inputs = null, - PinDescription[] outputs = null, - DisplayDescription[] displays = null, - bool hideName = false, - Action simulationFunction = null) + public static class Registry { - ModdedChipCreator.RegisterChip(name, size, col, inputs, outputs, displays, hideName, simulationFunction); + public static void RegisterChips(params ChipBuilder[] chips) + { + foreach (ChipBuilder chip in chips) + { + ModdedChipCreator.RegisterChip(chip.name, chip.size, chip.color, chip.inputs, chip.outputs, chip.displays, chip.hideName, chip.simulationFunction); + } + } + + public static void RegisterCollections(params CollectionBuilder[] collections) + { + foreach(CollectionBuilder collection in collections) + { + ModdedCollectionCreator.RegisterCollection(collection.name, collection.chips.Select(chip => chip.name).ToArray()); + } + } } } \ No newline at end of file diff --git a/TestData/Mods/ExampleMod.dls b/TestData/Mods/ExampleMod.dls index eb8ee2e900da25e011545415597a7e339167fd11..6c694d6d76f833c1476b2c096c31b4ed12a084bd 100644 GIT binary patch delta 2135 zcmah~eQXnD9Dd%r?%H)-Sqob?wn4XMTOF(nfyn|v_Q6L%WUSy=kc`olaiO&Hu0^Ln zXNYJ-66S@N5aPn%ACyGRqM|VY5=b;81QMf?7=s~#|0vNc{((w--rEK;1ax(EOpsW3N?z7U}vKlh-X&2Ma_aP((qS5dD$icb@THYydV&VI|P04p+_e>;OFE zXXvP~s+9Rwx2SWMdMO2Y7g?{1dBSgfBeH^h`qFXesk+N{sbP zDlunKIwO^I@pNYwoq=i25;_m6K+aLq5U4$<0FDQmn|;gxJvBpgInpfSb{mQRA50ko z|2~+1?dM?8Y3;ZUT5htRblLq1jOW;iY z%yRm)a0PKRTuD^IoPcIrp-&03wq{(FiF4?(OV>9k;khK$%L=|ey}Y}AjuH-%)!}0|R9Sb_`rJ4hg$@Ec*rktmD3&qwRKF>A{jv-A zgU`^G7%sA~JYbid>7C^AVq4J_7|qY8f%{?+xJwKH=f$(bG4bZ2 z*|yX<7!5oPJWj)oxjEnmRmxJmEk;xgOB9JU;th2MF31%@huEcPs8Ajd&k2dTjfZVi zZN1sHe}&AAD(4wf;*eMK&h;kWzv_j&bdYMyb zQfpN=x*alMgPkzMqF(H=(s{+cTezk!;Ve0DQG}~v#5$f=le?>&L=)Cwg!uxeiIv2T zpq$*{(i{gSu=OF6+5I-VlYb8Hk)#kBXS&A3E; zCG9%LYT{C&he|i?BDIH+7nH^L0xm}iH|#n$uu9ER8PBQvaTDtuhwwc<=Q6!aw}w-= zj$^3f##$o6gLnlI%)~WfIesFB@EdU+7Sp>3?Zjo+NNmHi#PzlwBer21`iX90+_vMB zb{t7@no3o_DCeT`OyGUa0=TPO;-aejQ>f?RX=}t8QZ0PQkFxcRb9sI(Pq%zA99E6% zrn5hh>3M#)KK8q7pU`57OiX{#O1pQtq#hsaO<7;Mqd8mOn@#p+^mJ-v=6#5ZF9J^@`z_d)|`qz z>W^}>MMYjv5gv~g6#l5Mn=g=W`-Ht+I=n8YbX59!-NpV5N~M1j|D~g%)GHlR;#~hG zzU89QAC<{EA2!pSaCqfq^%hplp33KtygWG~pLHfGT5lEJwT8S!-XAA_y5+f2BG%j9 g*~)ms*r(pj;>{V?$Gk=NRb|$SG2g6vVz|-y7s6hQt^fc4 delta 1871 zcmb7_ZERCj7{~wT+}lgnm2NGp9c+Vbbz9j+ouU|Uyo_zYL}WT}E;v}%HM3-Em)pC9 z1xAMkgBXI0F~1oG24qB6;xdX6R^^H05oSBpa$sa#gjE`pF0Rew)8W`=k@3?O zI$Fpq6+tt`MZr)cK-=5Obl=fWM<|q?^+Z@lP4EDV&?}aQDi25se;X{;$SmaQiV@|B z6i_tx{f|l_+^6P0S!a{3naRrY&dr)Tn^k$7tko-JB*^ULx!;V{cT_Ktk&rF)1xrG- zRd3XTC$-?oeLSLPSfRR_me6pa{jgr0xWdxI2Y`BR_5h!7n`I_by&2W`E;p})b9hG^ zrYN(c2yfca(!}{s>c`lY@P@v0H+4059MK+nGbe|g!9B48tV!MA5_>`qxtJP$`=-7$ z%Q%itHRa=Ja9s>*25O{Yk=Uc<;SE-0V58VB4Fu#Z@-3mb+u?qPcO5?Ha0+VeI(eEg zS2aaVDBh*)!G5%Jw^cYTeVCA6t*0Z!P!EP=I|A5%NwQg+WWBlC9R_XD##HV49T~)8 zS`2t~6(3-Od{}LN=qm7kjWgP#JRT1r@-ZQX?Q5=z>}B~HR^kbaa=pNDvV?3cRR?{! zu#-g!Ja=DHt9u>o$DA`bMJ+X>eJ+)@cUluItUzF_3>>4#%3*5f9Bq0}i3`)dY++n` zhxhQ=znot<+Dq`@UHWd!XqD7>9&U9)T^w$)=MuNcSy@Lja?r_}@{=` zv0U4U-|#-qd(c^X0$1=Ns(4$6L=?IB7*Q<1MY0Gt$Z!@@oN7=>_X?~eA4db(h^NRc zj=ha+#7=aO9y0F8@fkUeU1H3!=L@dvh|v{=?H_cja!*Ha*!EZAxU~W^%wvrc-D% z6EQ2%noP|#HYGCyeX*grre_kp$&8gS8(&BcV3V2b*_`MbNSJ6$_oYp2P51O9dX&%E zkCtfMa_CHLLtNS)ZA!-MsCU=428&<%?m;WDJ!bYMtPGN=fk95`LCcvN)2VpO!nW=` ziMR#UwjpUXrUz3N8cZ`bgy~w$AvE_7SVOSydMoXT`H$FZ=GSF0`TVPyquoa~zH+&% zGv!-82E9}Ww^j<_qTu%{)#r$-=0W$V!PbjnSE&?UuTiRzUN z-|s01w8)acR=!nmabYps!sqYscb52L&bwbW&&H@VF>dYkN+ZX8cHQo*|C(pDG~AjI zSeR?f2C_Dv`WobL)9LN)OTM4%5Ap)OPiFqL)Wu(|wZF_;EGKKnZ|7|nP5w|n M?hpPdM(gJN0|!x86#xJL diff --git a/TestData/Projects/MainTest/ProjectDescription.json b/TestData/Projects/MainTest/ProjectDescription.json index 27d6802e..1902ee20 100644 --- a/TestData/Projects/MainTest/ProjectDescription.json +++ b/TestData/Projects/MainTest/ProjectDescription.json @@ -3,14 +3,14 @@ "DLSVersion_LastSaved": "2.1.5", "DLSVersion_EarliestCompatible": "2.0.0", "CreationTime": "2025-03-14T17:23:30.404+00:00", - "LastSaveTime": "2025-05-04T20:59:53.868+01:00", + "LastSaveTime": "2025-05-04T23:44:36.627+01:00", "Prefs_MainPinNamesDisplayMode": 2, "Prefs_ChipPinNamesDisplayMode": 1, "Prefs_GridDisplayMode": 1, "Prefs_Snapping": 0, "Prefs_StraightWires": 0, "Prefs_SimPaused": false, - "Prefs_SimTargetStepsPerSecond": 50000, + "Prefs_SimTargetStepsPerSecond": 150, "Prefs_SimStepsPerClockTick": 6, "AllCustomChipNames":[ "AND", @@ -131,7 +131,7 @@ }, { "Chips":["WIP2","RAM-sync"], - "IsToggledOpen":false, + "IsToggledOpen":true, "Name":"TEST" }, { From 61f775890664ebd7dbd46e96f2cb495549334ce2 Mon Sep 17 00:00:00 2001 From: Squiggles Date: Mon, 5 May 2025 15:08:37 +0100 Subject: [PATCH 06/11] fix: rewrite underlying modloader to work on build (sadly IL2CPP no longer supported) --- Assets/Build/DLS.unity | 2 +- Assets/Scripts/DLS.asmdef | 3 +- Assets/Scripts/Game/Main/Main.cs | 4 -- .../Scripts/Game/Project/ModdedChipCreator.cs | 32 +++++++-- .../Game/Project/ModdedCollectionCreator.cs | 10 ++- Assets/Scripts/Game/Project/Project.cs | 8 +-- .../Scripts/Graphics/UI/Menus/BottomBarUI.cs | 8 +++ .../Graphics/UI/Menus/ChipLibraryMenu.cs | 43 +++++------- Assets/Scripts/ModdingAPI/ChipBuilder.cs | 10 ++- .../ModdingAPI/CollectionBuilder.cs.meta | 11 +++- .../Scripts/ModdingAPI/DLS.ModdingAPI.asmdef | 18 +----- Assets/Scripts/ModdingAPI/DisplayBuilder.cs | 7 ++ .../Scripts/ModdingAPI/DisplayBuilder.cs.meta | 2 + Assets/Scripts/ModdingAPI/PinBuilder.cs | 61 ++++++++++++++++++ Assets/Scripts/ModdingAPI/PinBuilder.cs.meta | 2 + Assets/Scripts/ModdingAPI/Registry.cs | 9 +-- Assets/Scripts/Mods/LoadedMod.cs | 1 - Assets/Scripts/Mods/ModLoader.cs | 23 ++----- Assets/Scripts/Simulation/Simulator.cs | 11 +++- ProjectSettings/ProjectSettings.asset | 6 +- TestData/Mods/ExampleMod.dls | Bin 6144 -> 5632 bytes .../Projects/MainTest/ProjectDescription.json | 4 +- 22 files changed, 178 insertions(+), 97 deletions(-) create mode 100644 Assets/Scripts/ModdingAPI/DisplayBuilder.cs create mode 100644 Assets/Scripts/ModdingAPI/DisplayBuilder.cs.meta create mode 100644 Assets/Scripts/ModdingAPI/PinBuilder.cs create mode 100644 Assets/Scripts/ModdingAPI/PinBuilder.cs.meta diff --git a/Assets/Build/DLS.unity b/Assets/Build/DLS.unity index 26a65f16..9a3e6e0e 100644 --- a/Assets/Build/DLS.unity +++ b/Assets/Build/DLS.unity @@ -241,7 +241,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: openSaveDirectory: 0 - openInMainMenu: 0 + openInMainMenu: 1 testProjectName: MainTest openA: 0 chipToOpenA: a diff --git a/Assets/Scripts/DLS.asmdef b/Assets/Scripts/DLS.asmdef index e6c9cb8d..a1cb2884 100644 --- a/Assets/Scripts/DLS.asmdef +++ b/Assets/Scripts/DLS.asmdef @@ -3,7 +3,8 @@ "rootNamespace": "", "references": [ "Assembly-Seb", - "DLS.Description" + "DLS.Description", + "DLSModdingAPI" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Assets/Scripts/Game/Main/Main.cs b/Assets/Scripts/Game/Main/Main.cs index 8237306c..3c52eda9 100644 --- a/Assets/Scripts/Game/Main/Main.cs +++ b/Assets/Scripts/Game/Main/Main.cs @@ -68,10 +68,6 @@ public static void CreateOrLoadProject(string projectName, string startupChipNam if (Loader.ProjectExists(projectName)) ActiveProject = LoadProject(projectName); else ActiveProject = CreateProject(projectName); - foreach(var coll in ActiveProject.description.ChipCollections) - Debug.Log(coll.Name); - ActiveProject.description.ChipCollections.AddRange(ModdedCollectionCreator.CreateModdedChipCollections()); // Todo: This seems to be inconsistent - ActiveProject.LoadDevChipOrCreateNewIfDoesntExist(startupChipName); ActiveProject.StartSimulation(); diff --git a/Assets/Scripts/Game/Project/ModdedChipCreator.cs b/Assets/Scripts/Game/Project/ModdedChipCreator.cs index 6907715e..02624b5c 100644 --- a/Assets/Scripts/Game/Project/ModdedChipCreator.cs +++ b/Assets/Scripts/Game/Project/ModdedChipCreator.cs @@ -1,22 +1,30 @@ using System; using System.Collections.Generic; +using System.Linq; using DLS.Description; -using DLS.Simulation; +using DLS.ModdingAPI; using UnityEngine; +using PinDescription = DLS.Description.PinDescription; namespace DLS.Game { public static class ModdedChipCreator { + static List unbuiltChips = new(); public static List ModdedChips = new(); - private static readonly Dictionary> ModdedChipFunctions = new(); + private static readonly Dictionary> ModdedChipFunctions = new(); public static ChipDescription[] CreateAllModdedChipDescriptions() { + unbuiltChips = Registry.moddedChips; + foreach (ChipBuilder chip in unbuiltChips) + { + RegisterChip(chip.name, chip.size, chip.color, ConvertToDescriptionPins(chip.inputs), ConvertToDescriptionPins(chip.outputs), null, chip.hideName, chip.simulationFunction); //TODO: IMPLEMENT DISPLAYS + } return ModdedChips.ToArray(); } - public static void RegisterChip( + static void RegisterChip( string name, Vector2 size, Color col, @@ -24,7 +32,7 @@ public static void RegisterChip( PinDescription[] outputs = null, DisplayDescription[] displays = null, bool hideName = false, - Action simulationFunction = null) + Action simulationFunction = null) { // Register the chip description ChipDescription chipDescription = CreateModdedChipDescription(name, ChipType.Modded, size, col, inputs, outputs, displays, hideName); @@ -56,7 +64,21 @@ static ChipDescription CreateModdedChipDescription(string name, ChipType type, V }; } - public static bool TryGetSimulationFunction(ChipDescription chipDescription, out Action simulationFunction) + static PinDescription[] ConvertToDescriptionPins(ModdingAPI.PinDescription[] moddingPins) + { + if (moddingPins == null) return null; + + return moddingPins.Select(pin => new PinDescription( + pin.Name, + pin.ID, + pin.Position, + (Description.PinBitCount) pin.BitCount, + (Description.PinColour) pin.Colour, + (Description.PinValueDisplayMode) pin.ValueDisplayMode + )).ToArray(); + } + + public static bool TryGetSimulationFunction(ChipDescription chipDescription, out Action simulationFunction) { return ModdedChipFunctions.TryGetValue(chipDescription, out simulationFunction); } diff --git a/Assets/Scripts/Game/Project/ModdedCollectionCreator.cs b/Assets/Scripts/Game/Project/ModdedCollectionCreator.cs index 34225f14..117bf468 100644 --- a/Assets/Scripts/Game/Project/ModdedCollectionCreator.cs +++ b/Assets/Scripts/Game/Project/ModdedCollectionCreator.cs @@ -1,16 +1,22 @@ using System.Collections.Generic; using System.Linq; using DLS.Description; +using DLS.ModdingAPI; namespace DLS.Game { public static class ModdedCollectionCreator { + static List unbuiltCollections = new(); public static List ModdedCollections = new(); - public static ChipCollection[] CreateModdedChipCollections() + static ModdedCollectionCreator() { - return ModdedCollections.ToArray(); + unbuiltCollections = Registry.moddedCollections; + foreach(CollectionBuilder collection in unbuiltCollections) + { + RegisterCollection(collection.name, collection.chips.Select(chip => chip.name).ToArray()); + } } public static void RegisterCollection(string name, string[] chipNames) { diff --git a/Assets/Scripts/Game/Project/Project.cs b/Assets/Scripts/Game/Project/Project.cs index d7bf2852..e695c479 100644 --- a/Assets/Scripts/Game/Project/Project.cs +++ b/Assets/Scripts/Game/Project/Project.cs @@ -509,13 +509,7 @@ public void NotifyExit() foreach (ChipDescription chip in moddedChipsCopy) { DeleteModdedChip(chip); - } - - List moddedCollectionsCopy = new(ModdedCollectionCreator.ModdedCollections); - foreach (ChipCollection collection in moddedCollectionsCopy) - { - ChipLibraryMenu.DeleteCollection(collection); - } + } } void SimThread() diff --git a/Assets/Scripts/Graphics/UI/Menus/BottomBarUI.cs b/Assets/Scripts/Graphics/UI/Menus/BottomBarUI.cs index 932230d8..2db96e07 100644 --- a/Assets/Scripts/Graphics/UI/Menus/BottomBarUI.cs +++ b/Assets/Scripts/Graphics/UI/Menus/BottomBarUI.cs @@ -365,6 +365,14 @@ static ChipCollection GetChipCollectionByName(string name) } } + foreach (ChipCollection c in ModdedCollectionCreator.ModdedCollections) + { + if (ChipDescription.NameMatch(c.Name, name)) + { + return c; + } + } + throw new Exception("Failed to find collection with name: " + name); } diff --git a/Assets/Scripts/Graphics/UI/Menus/ChipLibraryMenu.cs b/Assets/Scripts/Graphics/UI/Menus/ChipLibraryMenu.cs index f21238c3..6ca85d63 100644 --- a/Assets/Scripts/Graphics/UI/Menus/ChipLibraryMenu.cs +++ b/Assets/Scripts/Graphics/UI/Menus/ChipLibraryMenu.cs @@ -61,9 +61,15 @@ public static class ChipLibraryMenu static ChipCollection lastAutoOpenedCollection; static List collections => project.description.ChipCollections; + static List moddedCollections => ModdedCollectionCreator.ModdedCollections; static Project project => Project.ActiveProject; + static ChipCollection GetCollectionAtIndex(int index) + { + return index < collections.Count ? collections[index] : moddedCollections[index - collections.Count]; + } + public static void DrawMenu() { MenuHelper.DrawBackgroundOverlay(); @@ -160,13 +166,13 @@ static void DrawCollectionsPanel(Vector2 topLeft, Vector2 size) Bounds2D panelBoundsMinusHeader = Bounds2D.CreateFromTopLeftAndSize(UI.PrevBounds.BottomLeft, new Vector2(size.x, size.y - UI.PrevBounds.Height)); Bounds2D panelContentBounds = Bounds2D.Shrink(panelBoundsMinusHeader, PanelUIPadding); - UI.DrawScrollView(ID_CollectionsScrollbar, panelContentBounds.TopLeft, panelContentBounds.Size, UILayoutHelper.DefaultSpacing, Anchor.TopLeft, ActiveUITheme.ScrollTheme, drawCollectionEntry, collections.Count); + UI.DrawScrollView(ID_CollectionsScrollbar, panelContentBounds.TopLeft, panelContentBounds.Size, UILayoutHelper.DefaultSpacing, Anchor.TopLeft, ActiveUITheme.ScrollTheme, drawCollectionEntry, collections.Count + moddedCollections.Count); MenuHelper.DrawReservedMenuPanel(panelID, panelBounds, false); } static void DrawCollectionEntry(Vector2 topLeft, float width, int collectionIndex, bool isLayoutPass) { - ChipCollection collection = collections[collectionIndex]; + ChipCollection collection = GetCollectionAtIndex(collectionIndex); string label = collection.GetDisplayString(); bool collectionHighlighted = collectionIndex == selectedCollectionIndex; @@ -230,7 +236,7 @@ static void DrawSelectedItemPanel(Vector2 topLeft, Vector2 size) if (hasChipSelected) { // ---- Draw ---- - ChipCollection collection = collections[selectedCollectionIndex]; + ChipCollection collection = GetCollectionAtIndex(selectedCollectionIndex); string selectedChipName = collection.Chips[selectedChipInCollectionIndex]; bool canStepUpInCollection = selectedChipInCollectionIndex > 0; bool canStepDownInCollection = selectedChipInCollectionIndex < collection.Chips.Count - 1; @@ -297,18 +303,19 @@ static void DrawSelectedItemPanel(Vector2 topLeft, Vector2 size) else if (hasCollectionSelected) { // ---- Draw ---- - ChipCollection collection = collections[selectedCollectionIndex]; + ChipCollection collection = GetCollectionAtIndex(selectedCollectionIndex); ButtonTheme colSource = GetButtonTheme(true, true); DrawHeader(collection.Name, colSource.buttonCols.normal, colSource.textCols.normal, ref topLeft, panelContentBounds.Width); bool isStarred = project.description.IsStarred(collection.Name, true); - bool toggleStarred = DrawHorizontalButtonGroup(buttonName_starUnstar[isStarred ? 1 : 0], null, ref topLeft, panelContentBounds.Width) == 0; + bool[] canStar = new[] {!moddedCollections.Contains(collection)}; + bool toggleStarred = DrawHorizontalButtonGroup(buttonName_starUnstar[isStarred ? 1 : 0], canStar, ref topLeft, panelContentBounds.Width) == 0; - interactableStates_move[0] = selectedCollectionIndex > 0; - interactableStates_move[1] = selectedCollectionIndex < collections.Count - 1; + interactableStates_move[0] = selectedCollectionIndex > 0 && !moddedCollections.Contains(collection); + interactableStates_move[1] = selectedCollectionIndex < collections.Count - 1 && !moddedCollections.Contains(collection); int buttonIndexOrganize = DrawHorizontalButtonGroup(buttonNames_moveSingleStep, interactableStates_move, ref topLeft, panelContentBounds.Width); - bool canRenameOrDelete = !ChipDescription.NameMatch(collection.Name, defaultOtherChipsCollectionName); + bool canRenameOrDelete = !ChipDescription.NameMatch(collection.Name, defaultOtherChipsCollectionName) && !moddedCollections.Contains(collection); interactableStates_renameDelete[0] = canRenameOrDelete; interactableStates_renameDelete[1] = canRenameOrDelete; int buttonIndexEditCollection = DrawHorizontalButtonGroup(buttonNames_collectionRenameOrDelete, interactableStates_renameDelete, ref topLeft, panelContentBounds.Width); @@ -342,7 +349,7 @@ static void DrawSelectedItemPanel(Vector2 topLeft, Vector2 size) int indexEnd = selectedCollectionIndex - 1; (collections[indexStart], collections[indexEnd]) = (collections[indexEnd], collections[indexStart]); selectedCollectionIndex = indexEnd; - collection = collections[selectedCollectionIndex]; + collection = GetCollectionAtIndex(selectedCollectionIndex); } else if (buttonIndexOrganize == 1) // Move collection down { @@ -350,7 +357,7 @@ static void DrawSelectedItemPanel(Vector2 topLeft, Vector2 size) int indexEnd = selectedCollectionIndex + 1; (collections[indexStart], collections[indexEnd]) = (collections[indexEnd], collections[indexStart]); selectedCollectionIndex = indexEnd; - collection = collections[selectedCollectionIndex]; + collection = GetCollectionAtIndex(selectedCollectionIndex); } } else if (hasStarredItemSelected) @@ -643,22 +650,6 @@ static void DeleteSelectedCollection() project.SaveCurrentProjectDescription(); } - public static void DeleteCollection(ChipCollection collectionToDelete) - { - Debug.Log("Deleting collection"); - ChipCollection defaultCollection = GetDefaultCollection(); - - foreach (string chipName in collectionToDelete.Chips) - { - defaultCollection.Chips.Add(chipName); - } - - project.SetStarred(collectionToDelete.Name, false, true, false); - collections.Remove(collectionToDelete); - - project.SaveCurrentProjectDescription(); - } - static ChipCollection GetDefaultCollection() => collections.FirstOrDefault(c => ChipDescription.NameMatch(c.Name, defaultOtherChipsCollectionName)); static bool ValidateCollectionNameInput(string name) diff --git a/Assets/Scripts/ModdingAPI/ChipBuilder.cs b/Assets/Scripts/ModdingAPI/ChipBuilder.cs index 65b604f2..c52d0b13 100644 --- a/Assets/Scripts/ModdingAPI/ChipBuilder.cs +++ b/Assets/Scripts/ModdingAPI/ChipBuilder.cs @@ -1,6 +1,4 @@ using System; -using DLS.Description; -using DLS.Simulation; using UnityEngine; namespace DLS.ModdingAPI @@ -12,9 +10,9 @@ public class ChipBuilder public Color color = Color.white; public PinDescription[] inputs = null; public PinDescription[] outputs = null; - public DisplayDescription[] displays = null; + public DisplayBuilder[] displays = null; public bool hideName = false; - public Action simulationFunction = null; + public Action simulationFunction = null; public ChipBuilder(string name) { @@ -45,7 +43,7 @@ public ChipBuilder SetOutputs(PinDescription[] outputs) return this; } - public ChipBuilder SetDisplays(DisplayDescription[] displays) + public ChipBuilder SetDisplays(DisplayBuilder[] displays) { this.displays = displays; return this; @@ -57,7 +55,7 @@ public ChipBuilder HideName(bool hide = true) return this; } - public ChipBuilder SetSimulationFunction(Action simulationFunction) + public ChipBuilder SetSimulationFunction(Action simulationFunction) { this.simulationFunction = simulationFunction; return this; diff --git a/Assets/Scripts/ModdingAPI/CollectionBuilder.cs.meta b/Assets/Scripts/ModdingAPI/CollectionBuilder.cs.meta index bccc5f29..a2d3b256 100644 --- a/Assets/Scripts/ModdingAPI/CollectionBuilder.cs.meta +++ b/Assets/Scripts/ModdingAPI/CollectionBuilder.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 04c2257c20452374dab4167bb79b9d9f \ No newline at end of file +guid: 04c2257c20452374dab4167bb79b9d9f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/ModdingAPI/DLS.ModdingAPI.asmdef b/Assets/Scripts/ModdingAPI/DLS.ModdingAPI.asmdef index 4d0035cc..5f0922f9 100644 --- a/Assets/Scripts/ModdingAPI/DLS.ModdingAPI.asmdef +++ b/Assets/Scripts/ModdingAPI/DLS.ModdingAPI.asmdef @@ -1,19 +1,3 @@ { - "name": "DLSModdingAPI", - "references": [ - "UnityEngine.CoreModule", - "DLS.Description", - "DLS" - ], - "includePlatforms": [], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": true, - "precompiledReferences": [ - "UnityEngine.CoreModule.dll" - ], - "autoReferenced": false, - "defineConstraints": [], - "versionDefines": [], - "noEngineReferences": false + "name": "DLSModdingAPI" } \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/DisplayBuilder.cs b/Assets/Scripts/ModdingAPI/DisplayBuilder.cs new file mode 100644 index 00000000..730bc414 --- /dev/null +++ b/Assets/Scripts/ModdingAPI/DisplayBuilder.cs @@ -0,0 +1,7 @@ +namespace DLS.ModdingAPI +{ + public class DisplayBuilder + { + // Todo + } +} \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/DisplayBuilder.cs.meta b/Assets/Scripts/ModdingAPI/DisplayBuilder.cs.meta new file mode 100644 index 00000000..75631ec5 --- /dev/null +++ b/Assets/Scripts/ModdingAPI/DisplayBuilder.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: debca0986f38b304ebe01b3c988e32b4 \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/PinBuilder.cs b/Assets/Scripts/ModdingAPI/PinBuilder.cs new file mode 100644 index 00000000..96b408bd --- /dev/null +++ b/Assets/Scripts/ModdingAPI/PinBuilder.cs @@ -0,0 +1,61 @@ +using UnityEngine; + +namespace DLS.ModdingAPI +{ + public struct PinDescription + { + public string Name; + public int ID; + public Vector2 Position; + public PinBitCount BitCount; + public PinColour Colour; + public PinValueDisplayMode ValueDisplayMode; + + public PinDescription(string name, int id, Vector2 position, PinBitCount bitCount, PinColour colour, PinValueDisplayMode valueDisplayMode) + { + Name = name; + ID = id; + Position = position; + BitCount = bitCount; + Colour = colour; + ValueDisplayMode = valueDisplayMode; + } + + public PinDescription(string name, int id) + { + Name = name; + ID = id; + Position = Vector2.zero; + BitCount = PinBitCount.Bit1; + Colour = PinColour.Red; + ValueDisplayMode = PinValueDisplayMode.Off; + } + } + + public enum PinBitCount + { + Bit1 = 1, + Bit4 = 4, + Bit8 = 8 + } + + public enum PinColour + { + Red, + Orange, + Yellow, + Green, + Blue, + Violet, + Pink, + White + } + + public enum PinValueDisplayMode + { + Off, + UnsignedDecimal, + SignedDecimal, + HEX + } +} \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/PinBuilder.cs.meta b/Assets/Scripts/ModdingAPI/PinBuilder.cs.meta new file mode 100644 index 00000000..3d7b83a1 --- /dev/null +++ b/Assets/Scripts/ModdingAPI/PinBuilder.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1dea80cfc7501f14c97a02b257032127 \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/Registry.cs b/Assets/Scripts/ModdingAPI/Registry.cs index 00e1bd54..2c21b5b2 100644 --- a/Assets/Scripts/ModdingAPI/Registry.cs +++ b/Assets/Scripts/ModdingAPI/Registry.cs @@ -1,15 +1,16 @@ -using System.Linq; -using DLS.Game; +using System.Collections.Generic; namespace DLS.ModdingAPI { public static class Registry { + public static List moddedChips = new(); + public static List moddedCollections = new(); public static void RegisterChips(params ChipBuilder[] chips) { foreach (ChipBuilder chip in chips) { - ModdedChipCreator.RegisterChip(chip.name, chip.size, chip.color, chip.inputs, chip.outputs, chip.displays, chip.hideName, chip.simulationFunction); + moddedChips.Add(chip); } } @@ -17,7 +18,7 @@ public static void RegisterCollections(params CollectionBuilder[] collections) { foreach(CollectionBuilder collection in collections) { - ModdedCollectionCreator.RegisterCollection(collection.name, collection.chips.Select(chip => chip.name).ToArray()); + moddedCollections.Add(collection); } } } diff --git a/Assets/Scripts/Mods/LoadedMod.cs b/Assets/Scripts/Mods/LoadedMod.cs index d4c3b645..1ee0690c 100644 --- a/Assets/Scripts/Mods/LoadedMod.cs +++ b/Assets/Scripts/Mods/LoadedMod.cs @@ -7,7 +7,6 @@ public class LoadedMod readonly string version; readonly object instance; readonly MethodInfo initializeMethod; - readonly MethodInfo onProjectLoadMethod; public LoadedMod(string name, string version, object instance, MethodInfo initMethod) { diff --git a/Assets/Scripts/Mods/ModLoader.cs b/Assets/Scripts/Mods/ModLoader.cs index be4f0a7e..abbc5b59 100644 --- a/Assets/Scripts/Mods/ModLoader.cs +++ b/Assets/Scripts/Mods/ModLoader.cs @@ -8,29 +8,16 @@ namespace DLS.Mods { public static class ModLoader { - static readonly List loadedMods = new(); - static ModLoader() - { - AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly; - } - - private static Assembly ResolveAssembly(object sender, ResolveEventArgs args) - { - if (args.Name.StartsWith("DLSModdingAPI")) - { - return Assembly.GetExecutingAssembly(); - } - - return null; // Let the default resolver handle other assemblies - } + public static readonly List loadedMods = new(); public static void InitializeMods(string modsDirectory) { UnityEngine.Debug.Log("Loading mods..."); foreach (string dllPath in Directory.GetFiles(modsDirectory, "*.dls")) { - Assembly modAssembly = Assembly.LoadFrom(dllPath); try { + Assembly modAssembly = Assembly.LoadFrom(dllPath); + foreach (Type type in modAssembly.GetTypes().Where(t => !t.IsAbstract)) { // Check if the type has the same structure as IMod @@ -58,6 +45,10 @@ public static void InitializeMods(string modsDirectory) UnityEngine.Debug.LogError(inner.Message); } } + catch (FileLoadException ex) + { + UnityEngine.Debug.LogError(ex.Message); + } } } diff --git a/Assets/Scripts/Simulation/Simulator.cs b/Assets/Scripts/Simulation/Simulator.cs index bfeaca14..876b87a0 100644 --- a/Assets/Scripts/Simulation/Simulator.cs +++ b/Assets/Scripts/Simulation/Simulator.cs @@ -4,6 +4,7 @@ using DLS.Description; using DLS.Game; using Random = System.Random; +using System.Linq; namespace DLS.Simulation { @@ -477,8 +478,16 @@ static void ProcessBuiltinChip(SimChip chip) { if (ModdedChipCreator.TryGetSimulationFunction(chip.Description, out var simulationFunction)) { + uint[] inputStates = chip.InputPins.Select(pin => pin.State).ToArray(); + uint[] outputStates = chip.OutputPins.Select(pin => pin.State).ToArray(); + // Call the modded chip's simulation function - simulationFunction(chip.InputPins, chip.OutputPins); + simulationFunction(inputStates, outputStates); + + for (int i = 0; i < chip.OutputPins.Length; i++) + { + chip.OutputPins[i].State = outputStates[i]; + } } else { diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 1529686a..e728f98c 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -718,7 +718,7 @@ PlayerSettings: additionalCompilerArguments: {} platformArchitecture: {} scriptingBackend: - Standalone: 1 + Standalone: 0 il2cppCompilerConfiguration: Standalone: 1 il2cppCodeGeneration: @@ -733,7 +733,7 @@ PlayerSettings: PS4: 1 PS5: 1 Stadia: 1 - Standalone: 2 + Standalone: 4 WebGL: 1 Windows Store Apps: 1 XboxOne: 1 @@ -748,7 +748,7 @@ PlayerSettings: gcIncremental: 0 gcWBarrierValidation: 0 apiCompatibilityLevelPerPlatform: {} - editorAssembliesCompatibilityLevel: 1 + editorAssembliesCompatibilityLevel: 2 m_RenderingPath: 1 m_MobileRenderingPath: 1 metroPackageName: 2D_BuiltInRenderer diff --git a/TestData/Mods/ExampleMod.dls b/TestData/Mods/ExampleMod.dls index 6c694d6d76f833c1476b2c096c31b4ed12a084bd..18d58e714b75a411d69a5d100949d4738fd71389 100644 GIT binary patch delta 1749 zcmZXVZERCj7{~wT+`GHB>-xI28{4q!94pX{sT;W943o_{%wRYM3`L#GG8^`mc99p8 zbx2TLTw*zrXhHzN7$K8{R52KXnS8HVVw4zzF(mrI7zoCf|G8xd@ig~$p6B_W z^W5jWw4G|0iobmzdU$a8P26eM5RDXx2EfCBl@CS}*;9*!mA$yib2$!d(9tzOgORNc zx?cp`^gp3csj6jM^@%!HteBw)ACHfy<#)P z@I7KF#YqD&YZKumlaj{Y7T<2AIn`A=rIoQTO;@VJemhMTsOpU3#D6uEF?xRu|9g$< zH_D#R=QI~`7M0DgaF%QxuMHO-kCih`c!tUJD!AhD^dd&ASOs}JR!Pbj3$ju@)MSi# ztW+P(SQVw%Vs*o)_bxU=M(EIyWg#;ZKC%pIrMaYKo|9@Oc}ASkz^A5XB9$B+O6zSO zcK`?2*Yx(_7H#e+YC3v5+t@ptz%linStlygoEa7ob=7V zn&o1f;l+$D6m4RgwBVE9k;jDQcAI-_-n5ypc^!Igi=5?{pA1bQG$ZD$bYVZXz=edd z1y&=PF)KZI0_S-rJB;%bObRa4nVJ=P5GI(&gN!_n0Gf?ib*3Pgna5$HiKVk>;lr0K zS$mR?4_Qp3m9_z++K0Vdoc0`8f_ZreE3ppmBE*r0$ttotqBr36vMv@au=TE|vk%$c zb?2w}klyGW?{Y?JcURsO8}tfsr5bEm?3ZuQ`@#0|NhN;HYh^vgDVFm4ecGS4w~xy` zt&8T31opAm)5xFBQC?($e|QEGeulK2$vY<6;Y1gY&QJ2VNEeopK0IWz*=DQFPMaHT z_Sx(wE$k*sk+kis9eYFjX1;jYTAEfj8=A**~;ICF5?;6zsju~ znv*MV4&NAqn6nejA!rOU@|BUsMUpP+zMfe=`2;u@+ zfgi~l+#r{uiRvS0Cs(7JT#L+hc2CIu*>2OMK7xhz7IrO|*`-CQ+fgD8sf~_ubC7)cL|rG}F) zDX;T!SMO+YVt4!KP;xXk>~u)S(BQ-in+H>Ab+9O&yHu1A>MwUxwG=m~x#A|@!#m@* zcT^o+bF%b8YsLzkP!3NtQxK8D?Y1H!(COdED*U~9yH^VyN1+y~{C$qnK$om?RylRJ zvdpc+3n5-gQ5ERa^ZMD@UVkA>k8a!yQAxPnj?eZyx4FFW+MCjH7*?RfX)!^moz#fH z!+R$RnOwg_UGy}q>l9d2zrKCbld1lZ!JT8N9dW(1G9$I z-Rn6rpyx#HDiMxVk9jB5$KIgl7XM%tS|Fh=c^3&Y_lvhwnF+pbm)1fH#YXxJYgHVw%nLl3~W-~2B2CS z3(t1#09@o}DU_OFS!H&KN=Lw;E5zGvz9tq4ula$f6n^uLh?Gonpt_#oEinTtB0#?r z5N6G+ChZ3<@!g&njV*oRIy(5-bs&!HX{ zrGc;Ied)y!c?Djqfx;2(3jC<^3ObMmc>fTs{+fmgI~&c(xb?O{QQ z5{n&V-yc$2G{q{&)J6)t&38wjhD4%?IBna|f9=w{RSlJPD?V@!(!X z46|{QI2S(>%kT?v5tdPX9L>Zhv5DA(=ZI~VKTd4IHuMo4#9qtpow9q;FWSynuDM)P z9vWO=7vOog%thrvA=>!9Y=mu8E%2k)0g|cjw&gbQK0niD!0s~}?D|-RA&!#E3e8+& zf7!fYUpYTMlF23pVjaV&Y;quhEvaO7q$SmtOeGM{_GHI>g?2eFo*ZaTrqGwjc0Zd) zXOe>{G(R1W(LLPXlQkDOtDK5ACo;Y1 Date: Mon, 5 May 2025 21:41:44 +0100 Subject: [PATCH 07/11] feat(ModdingAPI): displays --- .../Scripts/Game/Elements/SubChipInstance.cs | 10 +++-- .../Scripts/Game/Project/ModdedChipCreator.cs | 3 +- .../Game/Project/ModdedDisplayCreator.cs | 37 ++++++++++++++++++ .../Game/Project/ModdedDisplayCreator.cs.meta | 2 + .../Scripts/Graphics/World/DevSceneDrawer.cs | 13 +++++- Assets/Scripts/ModdingAPI/DisplayBuilder.cs | 27 ++++++++++++- Assets/Scripts/Simulation/Simulator.cs | 8 +--- TestData/Mods/ExampleMod.dls | Bin 5632 -> 6656 bytes .../Projects/MainTest/ProjectDescription.json | 4 +- 9 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 Assets/Scripts/Game/Project/ModdedDisplayCreator.cs create mode 100644 Assets/Scripts/Game/Project/ModdedDisplayCreator.cs.meta diff --git a/Assets/Scripts/Game/Elements/SubChipInstance.cs b/Assets/Scripts/Game/Elements/SubChipInstance.cs index d85725ee..c639e660 100644 --- a/Assets/Scripts/Game/Elements/SubChipInstance.cs +++ b/Assets/Scripts/Game/Elements/SubChipInstance.cs @@ -233,11 +233,13 @@ static List CreateDisplayInstances(ChipDescription chipDesc) static DisplayInstance CreateDisplayInstance(DisplayDescription displayDesc, ChipDescription chipDesc) { - DisplayInstance instance = new(); - instance.Desc = displayDesc; - instance.DisplayType = chipDesc.ChipType; + DisplayInstance instance = new() + { + Desc = displayDesc, + DisplayType = chipDesc.ChipType + }; - if (chipDesc.ChipType == ChipType.Custom) + if (chipDesc.ChipType == ChipType.Custom) { ChipDescription childDesc = GetDescriptionOfDisplayedSubChip(chipDesc, displayDesc.SubChipID); instance.ChildDisplays = CreateDisplayInstances(childDesc); diff --git a/Assets/Scripts/Game/Project/ModdedChipCreator.cs b/Assets/Scripts/Game/Project/ModdedChipCreator.cs index 02624b5c..c2ef918a 100644 --- a/Assets/Scripts/Game/Project/ModdedChipCreator.cs +++ b/Assets/Scripts/Game/Project/ModdedChipCreator.cs @@ -19,7 +19,8 @@ public static ChipDescription[] CreateAllModdedChipDescriptions() unbuiltChips = Registry.moddedChips; foreach (ChipBuilder chip in unbuiltChips) { - RegisterChip(chip.name, chip.size, chip.color, ConvertToDescriptionPins(chip.inputs), ConvertToDescriptionPins(chip.outputs), null, chip.hideName, chip.simulationFunction); //TODO: IMPLEMENT DISPLAYS + Debug.Log(chip.displays); + RegisterChip(chip.name, chip.size, chip.color, ConvertToDescriptionPins(chip.inputs), ConvertToDescriptionPins(chip.outputs), chip.displays != null ? ModdedDisplayCreator.RegisterDisplays(chip.displays) : null, chip.hideName, chip.simulationFunction); } return ModdedChips.ToArray(); } diff --git a/Assets/Scripts/Game/Project/ModdedDisplayCreator.cs b/Assets/Scripts/Game/Project/ModdedDisplayCreator.cs new file mode 100644 index 00000000..87f5f56d --- /dev/null +++ b/Assets/Scripts/Game/Project/ModdedDisplayCreator.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using UnityEngine; +using DLS.Description; +using DLS.ModdingAPI; +using System; + +namespace DLS.Game +{ + public static class ModdedDisplayCreator + { + public static List ModdedDisplays = new(); + public static Dictionary> ModdedDrawFunctions = new(); + + public static DisplayDescription[] RegisterDisplays(DisplayBuilder[] displays) + { + List descriptions = new(); + foreach (DisplayBuilder display in displays) + { + descriptions.Add(RegisterDisplay(display.Position, display.Scale, display.DrawFunction)); + } + return descriptions.ToArray(); + } + + public static DisplayDescription RegisterDisplay(Vector2 position, float scale, Action drawFunction) + { + DisplayDescription displayDescription = new(-1, position, scale); + ModdedDisplays.Add(displayDescription); + ModdedDrawFunctions[displayDescription] = drawFunction; + return displayDescription; + } + + public static bool TryGetDrawFunction(DisplayDescription displayDescription, out Action drawFunction) + { + return ModdedDrawFunctions.TryGetValue(displayDescription, out drawFunction); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Game/Project/ModdedDisplayCreator.cs.meta b/Assets/Scripts/Game/Project/ModdedDisplayCreator.cs.meta new file mode 100644 index 00000000..def12e10 --- /dev/null +++ b/Assets/Scripts/Game/Project/ModdedDisplayCreator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 44039eececa77894b831a4c537b7cf62 \ No newline at end of file diff --git a/Assets/Scripts/Graphics/World/DevSceneDrawer.cs b/Assets/Scripts/Graphics/World/DevSceneDrawer.cs index 49dec254..c9ef02cf 100644 --- a/Assets/Scripts/Graphics/World/DevSceneDrawer.cs +++ b/Assets/Scripts/Graphics/World/DevSceneDrawer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using DLS.Description; using DLS.Game; using DLS.Simulation; @@ -429,7 +430,6 @@ public static Bounds2D DrawDisplay(DisplayInstance display, Vector2 posParent, f { bounds = DrawDisplay_Dot(posWorld, scaleWorld, sim); } - else if (display.DisplayType == ChipType.DisplayLED) { bool simActive = sim != null; @@ -443,6 +443,17 @@ public static Bounds2D DrawDisplay(DisplayInstance display, Vector2 posParent, f bounds = DrawDisplay_LED(posWorld, scaleWorld, col); } + else if (display.DisplayType == ChipType.Modded) + { + if (ModdedDisplayCreator.TryGetDrawFunction(display.Desc, out var DrawDisplay_Modded) && sim != null) + { + uint[] inputStates = sim.InputPins.Select(pin => (uint) PinState.GetBitStates(pin.State)).ToArray(); + uint[] outputStates = sim.OutputPins.Select(pin => (uint) PinState.GetBitStates(pin.State)).ToArray(); + + DrawDisplay_Modded(posWorld, scaleWorld, inputStates, outputStates); + } + bounds = Bounds2D.CreateFromCentreAndSize(posWorld, Vector2.one * scaleWorld); + } display.LastDrawBounds = bounds; return bounds; diff --git a/Assets/Scripts/ModdingAPI/DisplayBuilder.cs b/Assets/Scripts/ModdingAPI/DisplayBuilder.cs index 730bc414..c048298f 100644 --- a/Assets/Scripts/ModdingAPI/DisplayBuilder.cs +++ b/Assets/Scripts/ModdingAPI/DisplayBuilder.cs @@ -1,7 +1,32 @@ +using System; +using UnityEngine; + namespace DLS.ModdingAPI { public class DisplayBuilder { - // Todo + public Vector2 Position; + public float Scale; + public Action DrawFunction; + + public DisplayBuilder() { } + + public DisplayBuilder SetPosition(Vector2 position) + { + Position = position; + return this; + } + + public DisplayBuilder SetScale(float scale) + { + Scale = scale; + return this; + } + + public DisplayBuilder SetDrawFunction(Action drawFunction) + { + DrawFunction = drawFunction; + return this; + } } } \ No newline at end of file diff --git a/Assets/Scripts/Simulation/Simulator.cs b/Assets/Scripts/Simulation/Simulator.cs index 876b87a0..6a6b5b7c 100644 --- a/Assets/Scripts/Simulation/Simulator.cs +++ b/Assets/Scripts/Simulation/Simulator.cs @@ -478,8 +478,8 @@ static void ProcessBuiltinChip(SimChip chip) { if (ModdedChipCreator.TryGetSimulationFunction(chip.Description, out var simulationFunction)) { - uint[] inputStates = chip.InputPins.Select(pin => pin.State).ToArray(); - uint[] outputStates = chip.OutputPins.Select(pin => pin.State).ToArray(); + uint[] inputStates = chip.InputPins.Select(pin => (uint) PinState.GetBitStates(pin.State)).ToArray(); + uint[] outputStates = chip.OutputPins.Select(pin => (uint) PinState.GetBitStates(pin.State)).ToArray(); // Call the modded chip's simulation function simulationFunction(inputStates, outputStates); @@ -489,10 +489,6 @@ static void ProcessBuiltinChip(SimChip chip) chip.OutputPins[i].State = outputStates[i]; } } - else - { - Debug.LogWarning($"No simulation function registered for modded chip: {chip.Description.Name}"); - } break; } // ---- Bus types ---- diff --git a/TestData/Mods/ExampleMod.dls b/TestData/Mods/ExampleMod.dls index 18d58e714b75a411d69a5d100949d4738fd71389..3c582c335a2d741037659c4f678dba5209998ae4 100644 GIT binary patch delta 2838 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 C=qrZ+ diff --git a/TestData/Projects/MainTest/ProjectDescription.json b/TestData/Projects/MainTest/ProjectDescription.json index b2a2c95f..dc96967d 100644 --- a/TestData/Projects/MainTest/ProjectDescription.json +++ b/TestData/Projects/MainTest/ProjectDescription.json @@ -3,7 +3,7 @@ "DLSVersion_LastSaved": "2.1.5", "DLSVersion_EarliestCompatible": "2.0.0", "CreationTime": "2025-03-14T17:23:30.404+00:00", - "LastSaveTime": "2025-05-05T14:47:18.999+01:00", + "LastSaveTime": "2025-05-05T21:33:27.513+01:00", "Prefs_MainPinNamesDisplayMode": 2, "Prefs_ChipPinNamesDisplayMode": 1, "Prefs_GridDisplayMode": 1, @@ -135,7 +135,7 @@ "Name":"TEST" }, { - "Chips":["PULSE","TEST MergeSplit","#"], + "Chips":["PULSE","TEST MergeSplit","#","PASS","CONST","HUGE-LED"], "IsToggledOpen":true, "Name":"OTHER" } From 8eb464af037966a07968500c8bfeb539951c152f Mon Sep 17 00:00:00 2001 From: Squiggles Date: Tue, 6 May 2025 21:18:13 +0100 Subject: [PATCH 08/11] feat(ModdingAPI): runtime notifications --- .../Interaction/ChipInteractionController.cs | 57 ++++++++++++++++-- Assets/Scripts/Game/Project/Project.cs | 13 ++++ Assets/Scripts/ModdingAPI/IMod.cs | 51 ++++++++++++++-- Assets/Scripts/Mods/LoadedMod.cs | 30 --------- Assets/Scripts/Mods/LoadedMod.cs.meta | 2 - Assets/Scripts/Mods/ModLoader.cs | 51 +++++++++------- TestData/Mods/ExampleMod.dls | Bin 6656 -> 6656 bytes 7 files changed, 141 insertions(+), 63 deletions(-) delete mode 100644 Assets/Scripts/Mods/LoadedMod.cs delete mode 100644 Assets/Scripts/Mods/LoadedMod.cs.meta diff --git a/Assets/Scripts/Game/Interaction/ChipInteractionController.cs b/Assets/Scripts/Game/Interaction/ChipInteractionController.cs index 53707624..6e460e6c 100644 --- a/Assets/Scripts/Game/Interaction/ChipInteractionController.cs +++ b/Assets/Scripts/Game/Interaction/ChipInteractionController.cs @@ -2,9 +2,13 @@ using System.Linq; using DLS.Description; using DLS.Graphics; +using DLS.ModdingAPI; +using DLS.Mods; using DLS.SaveSystem; using Seb.Helpers; using UnityEngine; +using PinBitCount = DLS.Description.PinBitCount; +using PinDescription = DLS.Description.PinDescription; namespace DLS.Game { @@ -239,9 +243,25 @@ void HandleMouseInput() if (HasControl) UpdatePositionsToMouse(); // --- Mouse button input --- - if (InputHelper.IsMouseDownThisFrame(MouseButton.Left)) HandleLeftMouseDown(); + if (InputHelper.IsMouseDownThisFrame(MouseButton.Left)) + { + ModLoader.NotifyMods((mod, args) => mod.OnMouseClick(args), new IMod.InputEventArgs + { + Position = InputHelper.MousePosWorld, + Button = IMod.MouseButton.Left + }); + HandleLeftMouseDown(); + } if (InputHelper.IsMouseUpThisFrame(MouseButton.Left)) HandleLeftMouseUp(); - if (InputHelper.IsMouseDownThisFrame(MouseButton.Right)) HandleRightMouseDown(); + if (InputHelper.IsMouseDownThisFrame(MouseButton.Right)) + { + ModLoader.NotifyMods((mod, args) => mod.OnMouseClick(args), new IMod.InputEventArgs + { + Position = InputHelper.MousePosWorld, + Button = IMod.MouseButton.Right + }); + HandleRightMouseDown(); + } // Shift + scroll to increase vertical spacing between elements when placing multiple at a time // (disabled if elements were duplicated since then we want to preserve relative positions) @@ -565,7 +585,16 @@ void FinishMovingElements() return; } - hasMoved |= (element.MoveStartPosition != element.Position); + hasMoved |= element.MoveStartPosition != element.Position; + + if (hasMoved) + { + ModLoader.NotifyMods((mod, args) => mod.OnMoveChip(args, element.Position), new IMod.ChipEventArgs + { + ChipName = element is SubChipInstance subchip ? subchip.Description.Name : string.Empty, + Position = element.MoveStartPosition + }); + } } if (hasMoved) ActiveDevChip.UndoController.RecordMoveElements(SelectedElements); @@ -598,8 +627,16 @@ void FinishPlacingNewElements() if (elementToPlace is SubChipInstance subchip) { ActiveDevChip.AddNewSubChip(subchip, false); + ModLoader.NotifyMods((mod, args) => mod.OnPlaceChip(args), new IMod.ChipEventArgs + { + ChipName = subchip.Description.Name, + Position = subchip.Position + }); + } + else if (elementToPlace is DevPinInstance devPin) + { + ActiveDevChip.AddNewDevPin(devPin, false); } - else if (elementToPlace is DevPinInstance devPin) ActiveDevChip.AddNewDevPin(devPin, false); } foreach (WireInstance wire in DuplicatedWires) @@ -629,6 +666,12 @@ void ExitWireEditMode() if (wireToEdit != null && isMovingWireEditPoint) { wireToEdit.SetWirePoint(wireEditPointOld, wireEditPointSelectedIndex); + ModLoader.NotifyMods((mod, args) => mod.OnEditWire(args), new IMod.WireEventArgs + { + SourcePinName = wireToEdit.SourcePin.Name, + TargetPinName = wireToEdit.TargetPin.Name, + BitCount = (int) wireToEdit.bitCount + }); } wireToEdit = null; @@ -840,6 +883,12 @@ void CompleteConnection(WireInstance.ConnectionInfo info) WireToPlace.FinishPlacingWire(info); ActiveDevChip.AddWire(WireToPlace, false); ActiveDevChip.UndoController.RecordAddWire(WireToPlace); + ModLoader.NotifyMods((mod, args) => mod.OnPlaceWire(args), new IMod.WireEventArgs + { + SourcePinName = WireToPlace.SourcePin.Name, + TargetPinName = WireToPlace.TargetPin.Name, + BitCount = (int) WireToPlace.bitCount + }); } } diff --git a/Assets/Scripts/Game/Project/Project.cs b/Assets/Scripts/Game/Project/Project.cs index e695c479..b5263886 100644 --- a/Assets/Scripts/Game/Project/Project.cs +++ b/Assets/Scripts/Game/Project/Project.cs @@ -5,11 +5,14 @@ using System.Threading; using DLS.Description; using DLS.Graphics; +using DLS.ModdingAPI; +using DLS.Mods; using DLS.SaveSystem; using DLS.Simulation; using Seb.Helpers; using UnityEngine; using Debug = UnityEngine.Debug; +using PinDescription = DLS.Description.PinDescription; namespace DLS.Game { @@ -252,6 +255,11 @@ public void LoadDevChipOrCreateNewIfDoesntExist(string chipName) { CreateBlankDevChip(); } + + ModLoader.NotifyMods((mod, args) => mod.OnProjectLoad(args), new IMod.ProjectEventArgs + { + ProjectName = this.description.ProjectName + }); } void SetNewActiveDevChip(DevChipInstance devChip) @@ -503,6 +511,11 @@ public void ToggleGridDisplay() public void NotifyExit() { + ModLoader.NotifyMods((mod, args) => mod.OnProjectUnload(args), new IMod.ProjectEventArgs + { + ProjectName = description.ProjectName + }); + simThreadActive = false; List moddedChipsCopy = new(ModdedChipCreator.ModdedChips); diff --git a/Assets/Scripts/ModdingAPI/IMod.cs b/Assets/Scripts/ModdingAPI/IMod.cs index 3f65fa33..40f0d5c8 100644 --- a/Assets/Scripts/ModdingAPI/IMod.cs +++ b/Assets/Scripts/ModdingAPI/IMod.cs @@ -1,9 +1,52 @@ +using UnityEngine; + namespace DLS.ModdingAPI { - public interface IMod + public abstract class IMod { - string Name { get; } - string Version { get; } - void Initialize(); + public abstract string Name { get; } + public abstract string Version { get; } + public abstract void Initialize(); + + // Optional event methods with default implementations + public virtual void OnPlaceChip(ChipEventArgs chip) { } + public virtual void OnMoveChip(ChipEventArgs chip, Vector2 newPosition) { } + public virtual void OnPlaceWire(WireEventArgs wire) { } + public virtual void OnEditWire(WireEventArgs wire) { } + public virtual void OnProjectLoad(ProjectEventArgs project) { } + public virtual void OnProjectUnload(ProjectEventArgs project) { } + public virtual void OnMouseClick(InputEventArgs args) { } + + public enum MouseButton + { + Left = 0, + Right = 1, + Middle = 2 + } + + public struct ChipEventArgs + { + public string ChipName; + public Vector2 Position; + } + + public struct WireEventArgs + { + public string SourcePinName; + public string TargetPinName; + public int BitCount; + } + + public struct ProjectEventArgs + { + public string ProjectName; + public string FilePath; + } + + public struct InputEventArgs + { + public Vector2 Position; + public MouseButton Button; + } } } \ No newline at end of file diff --git a/Assets/Scripts/Mods/LoadedMod.cs b/Assets/Scripts/Mods/LoadedMod.cs deleted file mode 100644 index 1ee0690c..00000000 --- a/Assets/Scripts/Mods/LoadedMod.cs +++ /dev/null @@ -1,30 +0,0 @@ -using UnityEngine; -using System.Reflection; - -public class LoadedMod -{ - readonly string name; - readonly string version; - readonly object instance; - readonly MethodInfo initializeMethod; - - public LoadedMod(string name, string version, object instance, MethodInfo initMethod) - { - this.name = name; - this.version = version; - this.instance = instance; - initializeMethod = initMethod; - } - - public void Initialize() - { - try - { - initializeMethod.Invoke(instance, null); - } - catch (System.Exception e) - { - Debug.LogError($"Mod {name} failed to initialize:\n{e}"); - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Mods/LoadedMod.cs.meta b/Assets/Scripts/Mods/LoadedMod.cs.meta deleted file mode 100644 index 2ed95ee9..00000000 --- a/Assets/Scripts/Mods/LoadedMod.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: ef608844b5e118a4cac387ff1a356ed8 \ No newline at end of file diff --git a/Assets/Scripts/Mods/ModLoader.cs b/Assets/Scripts/Mods/ModLoader.cs index abbc5b59..87e1e467 100644 --- a/Assets/Scripts/Mods/ModLoader.cs +++ b/Assets/Scripts/Mods/ModLoader.cs @@ -3,12 +3,30 @@ using System.IO; using System.Linq; using System.Reflection; -using DLS.Game; +using DLS.ModdingAPI; + namespace DLS.Mods -{ +{ public static class ModLoader { - public static readonly List loadedMods = new(); + public static readonly List loadedMods = new(); + + public static void NotifyMods(Action action, T arg) + { + foreach (IMod mod in loadedMods) + { + action(mod, arg); + } + } + + public static void NotifyMods(Action action) + { + foreach (IMod mod in loadedMods) + { + action(mod); + } + } + public static void InitializeMods(string modsDirectory) { UnityEngine.Debug.Log("Loading mods..."); @@ -17,30 +35,18 @@ public static void InitializeMods(string modsDirectory) try { Assembly modAssembly = Assembly.LoadFrom(dllPath); - - foreach (Type type in modAssembly.GetTypes().Where(t => !t.IsAbstract)) - { - // Check if the type has the same structure as IMod - var nameProperty = type.GetProperty("Name"); - var versionProperty = type.GetProperty("Version"); - var initializeMethod = type.GetMethod("Initialize"); - - if (nameProperty != null && versionProperty != null && initializeMethod != null) - { - // Ugly, but ModLoader.IMod would not be the same as ModdingAPI.IMod - object modInstance = Activator.CreateInstance(type); - string name = (string) nameProperty.GetValue(modInstance); - string version = (string) versionProperty.GetValue(modInstance); - LoadedMod mod = new(name, version, modInstance, initializeMethod); - loadedMods.Add(mod); - mod.Initialize(); - } + foreach (Type type in modAssembly.GetTypes().Where(t => typeof(IMod).IsAssignableFrom(t) && !t.IsAbstract)) + { + IMod modInstance = (IMod) Activator.CreateInstance(type); + loadedMods.Add(modInstance); + modInstance.Initialize(); + UnityEngine.Debug.Log($"Loaded mod: {modInstance.Name} v{modInstance.Version}"); } } catch (ReflectionTypeLoadException ex) { - foreach(Exception inner in ex.LoaderExceptions) + foreach (Exception inner in ex.LoaderExceptions) { UnityEngine.Debug.LogError(inner.Message); } @@ -51,6 +57,5 @@ public static void InitializeMods(string modsDirectory) } } } - } } \ No newline at end of file diff --git a/TestData/Mods/ExampleMod.dls b/TestData/Mods/ExampleMod.dls index 3c582c335a2d741037659c4f678dba5209998ae4..dbd0ba00a909fd810747ca81ad2844d3dfb1a0d3 100644 GIT binary patch delta 2100 zcmaJ?e{54#6#nkJudiKOy1uq+J6UJt9Q%PdKp1ROivca8z{IViovI9!8O+^abSgFMdE;rt$IQm4QoGtujtXz8 z78vw~+<>*%>rH#8KPWuO0%E?GbAuMrAYa-$pYpA#y{r8E#%qb4Ph? z&Hsg|w};t{EHAg20j(4`*&H$(CwXW=Zi2*rCs*N1^^1)Dm9`O33+=ZgUu3kZE7&aK z6?I;qczU;t!qVlGy%wq z*sH|32EW9!Oz_JRlhQIK`FbX1IHrWj`?V@?gb8$;!rY2au#?uF z31RNQdPJFnDj8)~jL{$~YD@y%s=(7S_$O)akoM1sYxtEGbKt5HWiDLiHs(dtw1XxG zWO-<#v_0m;3YJk9%%}$1q)-Ynj7s(mPN9Va2QP8rFa-;hhf2iwRn#MiYw*(W8-xLg z3t?fML9xtpevS6b@X(ABOKz6xLY{{eFouDg>fu5SPb>BXRh4C6qgM%BVDF)joPMdY zxUQU|6e^{fUVcsFS!*VvEEMiK`{XB*qEp zRuC4TPw)(~(lJh0PW)q~8pny(5x>r(7{>>^nXZnewv*+&x<>M~#D61hCwm|9G*4oR zxJKhkP0fT&bVtU~L3d;d`}nhjzu-l9z9*7;dx?&c+79 zGHfNBi$jF-aGdaNd`8%Y(=5Cg%cY}>uniC6JfW4aSMuH*@5K~5AT6_@(lG($te(0U zuQ#~0%yWt?8nv6>B~&q^(OSu_7;)t$;@c1g!J=*C)#8;k)m?mAImj1uQ9jAcTVF<3#xOu9ue z5K$~g{un?LBnrY0T?iQW1Huo`kVueZ5+O`umP|>A5rZOL-g7967~@I4=RD7QIp=*( z&-b;jzOVkR?ecy+dp@(Xgx`v14wo@2^Lc~&!#J=MH0A(tktz4PRsk;3JIPU^%6YA7 zV-p!vKT8)~CYv4)(Zk`udlkSn3TA4fzdMkvkP?ocX3t0o-SR{zsgpnvdE<~v&9#ydGZnFQoCebu=Gy1 zjM7q-RJ|3cBFs@&W$mhIJW@?!ymRQlthSmm9;qQ!6HDb$QGL3(FPfC& zlhXMf8#qC8MWPjFiF-7EFwwk#=C5t#wrYyXiBgt%^evzYK`kMwTCD1Hr_`K^#$+Cp z7WxftWPgZ!G|+|?_Oeg7g|&RHu<$F>c+B7hgG-Hlo8hyWUSSV+Q_MS3V;j@>9dR3j z=!K2rqLXKB#zb0+Py;U0&!A2dsxqix(6;vwe4 z1!1uO{*sHRV89#jE%ffn_*Fq!I1H)ruN^iNwsFA#U=3@-&vudi)>cu;XeL&~Q;Gj1R zX=HGP50Uh1nO&5l*r-hCng}qCiTC&>8}$mE#5nRbWYq*x5_7U@9>z0zKWN>XbQu|r z(aCU}A-tET_2uFirBGplBs@}LA>EP@!QdsdP()~>n$UwO1{)2|HP~#h#bBGkc0vnV z2n&%kJY(#K2rH@KcXD)vL}hRif&vY zZc+TGY$B9&9}c0K?!zT)5w8*s<8>66_)AENeU$a3Jc@6zQy!xxbpxZ=CkDx~NuI_} zNPi>0z;F19UjSo;VhH`&4Jug^)hNYfaFuVhH*oY2%&kh(;^}uasOsJXp2h>P@oeFtQ zc#eKnvt0bLck%Ano4Xo14)&^HPduHl!h*S6E-TD@&E7WJ!kf4+Y%zDPH{osfc6yUJ z+EeO%$`R&d%`Nmj%1iS~^Rz4pxwP}HDF>XA5@xL~Z^A*oE|;^=*CNTNbMm$2A37(T z)@%E^=w)D~zGhv-W}1xJoCE3BgN}oh5V%~ry0$zBrnm97 Date: Wed, 7 May 2025 21:08:11 +0100 Subject: [PATCH 09/11] feat(ModdingAPI): hiding chips on mod unload Chips containing modded subchips will now be "soft" hidden from the library if the mod they depend on is removed. They return when the mod is loaded again. Todo: warning message when this occurs Untested: modded supchips several chips deep --- .../Description/Types/ChipDescription.cs | 2 + .../Description/Types/ProjectDescription.cs | 1 + .../Types/SubTypes/DisplayDescription.cs | 3 + Assets/Scripts/Game/Project/ChipLibrary.cs | 20 ++++- .../Scripts/Game/Project/ModdedChipCreator.cs | 4 +- Assets/Scripts/Game/Project/Project.cs | 11 +-- .../Scripts/Graphics/UI/Menus/BottomBarUI.cs | 21 +++++ .../Graphics/UI/Menus/ChipLibraryMenu.cs | 25 +++++- .../Scripts/Graphics/UI/Menus/SearchPopup.cs | 20 ++++- Assets/Scripts/ModdingAPI/ChipBuilder.cs | 4 +- .../Scripts/ModdingAPI/CollectionBuilder.cs | 4 +- Assets/Scripts/ModdingAPI/DisplayBuilder.cs | 6 +- Assets/Scripts/ModdingAPI/IMod.cs | 1 + Assets/Scripts/Mods/ModLoader.cs | 12 +++ .../Scripts/SaveSystem/DescriptionCreator.cs | 16 +++- Assets/Scripts/SaveSystem/Loader.cs | 9 ++- TestData/Mods/ExampleMod.dls | Bin 6656 -> 0 bytes TestData/Mods/ExampleMod.dls.gone | Bin 0 -> 6656 bytes .../Projects/MainTest/Chips/display test.json | 73 ++++++++++++++++++ 19 files changed, 214 insertions(+), 18 deletions(-) delete mode 100644 TestData/Mods/ExampleMod.dls create mode 100644 TestData/Mods/ExampleMod.dls.gone create mode 100644 TestData/Projects/MainTest/Chips/display test.json diff --git a/Assets/Scripts/Description/Types/ChipDescription.cs b/Assets/Scripts/Description/Types/ChipDescription.cs index b5b0e67f..69d14336 100644 --- a/Assets/Scripts/Description/Types/ChipDescription.cs +++ b/Assets/Scripts/Description/Types/ChipDescription.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using UnityEngine; namespace DLS.Description @@ -11,6 +12,7 @@ public class ChipDescription // ---- Data ---- public string DLSVersion; + public List DependsOnModIDs = new(); public string Name; public NameDisplayLocation NameLocation; public ChipType ChipType; diff --git a/Assets/Scripts/Description/Types/ProjectDescription.cs b/Assets/Scripts/Description/Types/ProjectDescription.cs index a8dc0f33..5010fc98 100644 --- a/Assets/Scripts/Description/Types/ProjectDescription.cs +++ b/Assets/Scripts/Description/Types/ProjectDescription.cs @@ -81,6 +81,7 @@ public class ChipCollection [JsonIgnore] string displayName_open; public bool IsToggledOpen; public string Name; + public List DependsOnModIDs = new(); public ChipCollection(string name, params string[] chips) { diff --git a/Assets/Scripts/Description/Types/SubTypes/DisplayDescription.cs b/Assets/Scripts/Description/Types/SubTypes/DisplayDescription.cs index 258c5e23..93ac544f 100644 --- a/Assets/Scripts/Description/Types/SubTypes/DisplayDescription.cs +++ b/Assets/Scripts/Description/Types/SubTypes/DisplayDescription.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using UnityEngine; namespace DLS.Description @@ -9,12 +10,14 @@ public struct DisplayDescription public int SubChipID; public Vector2 Position; public float Scale; + public List DependsOnModIDs; public DisplayDescription(int subChipID, Vector2 position, float scale) { SubChipID = subChipID; Position = position; Scale = scale; + DependsOnModIDs = new(); } } } \ No newline at end of file diff --git a/Assets/Scripts/Game/Project/ChipLibrary.cs b/Assets/Scripts/Game/Project/ChipLibrary.cs index 0a041f46..9fdf34b9 100644 --- a/Assets/Scripts/Game/Project/ChipLibrary.cs +++ b/Assets/Scripts/Game/Project/ChipLibrary.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using DLS.Description; +using DLS.Mods; using UnityEngine; namespace DLS.Game @@ -48,7 +49,13 @@ void RebuildChipDescriptionLookup() descriptionFromNameLookup.Clear(); foreach (ChipDescription desc in allChips) { - descriptionFromNameLookup.Add(desc.Name, desc); + if (desc.DependsOnModIDs == null || desc.DependsOnModIDs.All(ModLoader.IsModLoaded)) + { + if (!descriptionFromNameLookup.ContainsKey(desc.Name)) + { + descriptionFromNameLookup.Add(desc.Name, desc); + } + } } foreach (ChipDescription desc in hiddenChips) @@ -62,7 +69,16 @@ void RebuildChipDescriptionLookup() public bool HasChip(string name) => TryGetChipDescription(name, out _); - public ChipDescription GetChipDescription(string name) => descriptionFromNameLookup[name]; + public ChipDescription GetChipDescription(string name) + { + try { + return descriptionFromNameLookup[name]; + } + catch (KeyNotFoundException) + { + return null; + } + } public bool TryGetChipDescription(string name, out ChipDescription description) => descriptionFromNameLookup.TryGetValue(name, out description); diff --git a/Assets/Scripts/Game/Project/ModdedChipCreator.cs b/Assets/Scripts/Game/Project/ModdedChipCreator.cs index c2ef918a..8c000583 100644 --- a/Assets/Scripts/Game/Project/ModdedChipCreator.cs +++ b/Assets/Scripts/Game/Project/ModdedChipCreator.cs @@ -20,12 +20,13 @@ public static ChipDescription[] CreateAllModdedChipDescriptions() foreach (ChipBuilder chip in unbuiltChips) { Debug.Log(chip.displays); - RegisterChip(chip.name, chip.size, chip.color, ConvertToDescriptionPins(chip.inputs), ConvertToDescriptionPins(chip.outputs), chip.displays != null ? ModdedDisplayCreator.RegisterDisplays(chip.displays) : null, chip.hideName, chip.simulationFunction); + RegisterChip(chip.modID, chip.name, chip.size, chip.color, ConvertToDescriptionPins(chip.inputs), ConvertToDescriptionPins(chip.outputs), chip.displays != null ? ModdedDisplayCreator.RegisterDisplays(chip.displays) : null, chip.hideName, chip.simulationFunction); } return ModdedChips.ToArray(); } static void RegisterChip( + string modID, string name, Vector2 size, Color col, @@ -37,6 +38,7 @@ static void RegisterChip( { // Register the chip description ChipDescription chipDescription = CreateModdedChipDescription(name, ChipType.Modded, size, col, inputs, outputs, displays, hideName); + chipDescription.DependsOnModIDs.Add(modID); ModdedChips.Add(chipDescription); // Register the simulation function diff --git a/Assets/Scripts/Game/Project/Project.cs b/Assets/Scripts/Game/Project/Project.cs index b5263886..c14c6791 100644 --- a/Assets/Scripts/Game/Project/Project.cs +++ b/Assets/Scripts/Game/Project/Project.cs @@ -518,11 +518,12 @@ public void NotifyExit() simThreadActive = false; - List moddedChipsCopy = new(ModdedChipCreator.ModdedChips); - foreach (ChipDescription chip in moddedChipsCopy) - { - DeleteModdedChip(chip); - } + // List moddedChipsCopy = new(ModdedChipCreator.ModdedChips); + // foreach (ChipDescription chip in moddedChipsCopy) + // { + + // DeleteModdedChip(chip); + // } } void SimThread() diff --git a/Assets/Scripts/Graphics/UI/Menus/BottomBarUI.cs b/Assets/Scripts/Graphics/UI/Menus/BottomBarUI.cs index 2db96e07..ceec4ddd 100644 --- a/Assets/Scripts/Graphics/UI/Menus/BottomBarUI.cs +++ b/Assets/Scripts/Graphics/UI/Menus/BottomBarUI.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Generic; +using System.Linq; using DLS.Description; using DLS.Game; +using DLS.Mods; using Seb.Helpers; using Seb.Types; using Seb.Vis; @@ -188,6 +191,24 @@ static void DrawBottomBar(Project project) for (int i = 0; i < project.description.StarredList.Count; i++) { StarredItem starred = project.description.StarredList[i]; + object desc = starred.IsCollection + ? GetChipCollectionByName(starred.Name) + : project.chipLibrary.GetChipDescription(starred.Name); + + List dependsOnModIDs = starred.IsCollection + ? ((ChipCollection) desc)?.DependsOnModIDs + : ((ChipDescription) desc)?.DependsOnModIDs; + + if (desc == null) + { + continue; + } + + if (dependsOnModIDs != null && !dependsOnModIDs.All(ModLoader.IsModLoaded)) + { + continue; + } + bool isToggledOpenCollection = activeCollection != null && ChipDescription.NameMatch(starred.Name, activeCollection.Name); string buttonName = starred.GetDisplayStringForBottomBar(isToggledOpenCollection); diff --git a/Assets/Scripts/Graphics/UI/Menus/ChipLibraryMenu.cs b/Assets/Scripts/Graphics/UI/Menus/ChipLibraryMenu.cs index 6ca85d63..cd2f36a3 100644 --- a/Assets/Scripts/Graphics/UI/Menus/ChipLibraryMenu.cs +++ b/Assets/Scripts/Graphics/UI/Menus/ChipLibraryMenu.cs @@ -2,6 +2,7 @@ using System.Linq; using DLS.Description; using DLS.Game; +using DLS.Mods; using Seb.Helpers; using Seb.Types; using Seb.Vis; @@ -144,6 +145,23 @@ static void DrawStarredPanel(Vector2 topLeft, Vector2 size) static void DrawStarredEntry(Vector2 topLeft, float width, int index, bool isLayoutPass) { StarredItem starredItem = project.description.StarredList[index]; + object desc = starredItem.IsCollection + ? GetCollectionAtIndex(index) + : project.chipLibrary.GetChipDescription(starredItem.Name); + + List dependsOnModIDs = starredItem.IsCollection + ? ((ChipCollection) desc)?.DependsOnModIDs + : ((ChipDescription) desc)?.DependsOnModIDs; + + if (desc == null) + { + return; + } + + if (dependsOnModIDs != null && !dependsOnModIDs.All(ModLoader.IsModLoaded)) + { + return; + } ButtonTheme theme = GetButtonTheme(starredItem.IsCollection, index == selectedStarredItemIndex); interactableStates_starredList[0] = index < project.description.StarredList.Count - 1; // can move down @@ -195,7 +213,12 @@ static void DrawCollectionEntry(Vector2 topLeft, float width, int collectionInde { for (int chipIndex = 0; chipIndex < collection.Chips.Count; chipIndex++) { - string chipName = collection.Chips[chipIndex]; + ChipDescription chip = project.chipLibrary.GetChipDescription(collection.Chips[chipIndex]); + if (chip == null || !chip.DependsOnModIDs.All(ModLoader.IsModLoaded)) + { + continue; + } + string chipName = chip.Name; ButtonTheme activeChipTheme = collectionIndex == selectedCollectionIndex && chipIndex == selectedChipInCollectionIndex ? ActiveUITheme.ChipLibraryChipToggleOn : ActiveUITheme.ChipLibraryChipToggleOff; Vector2 chipLabelPos = new(topLeft.x + nestedInset, UI.PrevBounds.Bottom - UILayoutHelper.DefaultSpacing); bool chipPressed = UI.Button(chipName, activeChipTheme, chipLabelPos, new Vector2(width - nestedInset, 2), true, false, false, Anchor.TopLeft, true, 1, isScrolling); diff --git a/Assets/Scripts/Graphics/UI/Menus/SearchPopup.cs b/Assets/Scripts/Graphics/UI/Menus/SearchPopup.cs index 203fdf1f..b6cc7a1f 100644 --- a/Assets/Scripts/Graphics/UI/Menus/SearchPopup.cs +++ b/Assets/Scripts/Graphics/UI/Menus/SearchPopup.cs @@ -3,6 +3,7 @@ using System.Linq; using DLS.Description; using DLS.Game; +using DLS.Mods; using Seb.Helpers; using Seb.Types; using Seb.Vis; @@ -131,6 +132,13 @@ static void CreateFilteredChipsList(string searchString) HashSet remainingChipNames = new(allChipNames); remainingChipNames.ExceptWith(recentChipNames); + // Filter out chips with null descriptions or unloaded mod dependencies + remainingChipNames.RemoveWhere(chipName => + { + ChipDescription desc = Project.ActiveProject.chipLibrary.GetChipDescription(chipName); + return desc == null || (desc.DependsOnModIDs != null && !desc.DependsOnModIDs.All(ModLoader.IsModLoaded)); + }); + List sortedList = remainingChipNames.ToList(); sortedList.Sort(); sortedList.Reverse(); @@ -154,9 +162,15 @@ static void CreateFilteredChipsList(string searchString) contains.ExceptWith(startsWith); contains.ExceptWith(startsWith_Lenient); - List all = ToSortedList(startsWith); - all.AddRange(ToSortedList(startsWith_Lenient)); - all.AddRange(ToSortedList(contains)); + // Filter out chips with null descriptions or unloaded mod dependencies + HashSet allFiltered = new(startsWith.Concat(startsWith_Lenient).Concat(contains)); + allFiltered.RemoveWhere(chipName => + { + ChipDescription desc = Project.ActiveProject.chipLibrary.GetChipDescription(chipName); + return desc == null || (desc.DependsOnModIDs != null && !desc.DependsOnModIDs.All(ModLoader.IsModLoaded)); + }); + + List all = ToSortedList(allFiltered); filteredChipNames = all.ToArray(); static string LenientString(string s) diff --git a/Assets/Scripts/ModdingAPI/ChipBuilder.cs b/Assets/Scripts/ModdingAPI/ChipBuilder.cs index c52d0b13..3edf16c8 100644 --- a/Assets/Scripts/ModdingAPI/ChipBuilder.cs +++ b/Assets/Scripts/ModdingAPI/ChipBuilder.cs @@ -5,6 +5,7 @@ namespace DLS.ModdingAPI { public class ChipBuilder { + public readonly string modID; public readonly string name; public Vector2 size = Vector2.one; public Color color = Color.white; @@ -14,8 +15,9 @@ public class ChipBuilder public bool hideName = false; public Action simulationFunction = null; - public ChipBuilder(string name) + public ChipBuilder(string modID, string name) { + this.modID = modID; this.name = name; } diff --git a/Assets/Scripts/ModdingAPI/CollectionBuilder.cs b/Assets/Scripts/ModdingAPI/CollectionBuilder.cs index 396138ad..6a33d2c3 100644 --- a/Assets/Scripts/ModdingAPI/CollectionBuilder.cs +++ b/Assets/Scripts/ModdingAPI/CollectionBuilder.cs @@ -4,11 +4,13 @@ namespace DLS.ModdingAPI { public class CollectionBuilder { + public string modID; public readonly string name; public List chips; - public CollectionBuilder(string name) + public CollectionBuilder(string modID, string name) { + this.modID = modID; this.name = name; chips = new(); } diff --git a/Assets/Scripts/ModdingAPI/DisplayBuilder.cs b/Assets/Scripts/ModdingAPI/DisplayBuilder.cs index c048298f..fb4deb9a 100644 --- a/Assets/Scripts/ModdingAPI/DisplayBuilder.cs +++ b/Assets/Scripts/ModdingAPI/DisplayBuilder.cs @@ -5,11 +5,15 @@ namespace DLS.ModdingAPI { public class DisplayBuilder { + public string modID; public Vector2 Position; public float Scale; public Action DrawFunction; - public DisplayBuilder() { } + public DisplayBuilder(string modID) + { + this.modID = modID; + } public DisplayBuilder SetPosition(Vector2 position) { diff --git a/Assets/Scripts/ModdingAPI/IMod.cs b/Assets/Scripts/ModdingAPI/IMod.cs index 40f0d5c8..2f2c924e 100644 --- a/Assets/Scripts/ModdingAPI/IMod.cs +++ b/Assets/Scripts/ModdingAPI/IMod.cs @@ -4,6 +4,7 @@ namespace DLS.ModdingAPI { public abstract class IMod { + public abstract string ModID { get; } public abstract string Name { get; } public abstract string Version { get; } public abstract void Initialize(); diff --git a/Assets/Scripts/Mods/ModLoader.cs b/Assets/Scripts/Mods/ModLoader.cs index 87e1e467..6ca81274 100644 --- a/Assets/Scripts/Mods/ModLoader.cs +++ b/Assets/Scripts/Mods/ModLoader.cs @@ -57,5 +57,17 @@ public static void InitializeMods(string modsDirectory) } } } + + public static bool IsModLoaded(string modId) + { + foreach (IMod mod in loadedMods) + { + if (mod.ModID == modId) + { + return true; + } + } + return false; + } } } \ No newline at end of file diff --git a/Assets/Scripts/SaveSystem/DescriptionCreator.cs b/Assets/Scripts/SaveSystem/DescriptionCreator.cs index 196e0ea1..e72e6ba8 100644 --- a/Assets/Scripts/SaveSystem/DescriptionCreator.cs +++ b/Assets/Scripts/SaveSystem/DescriptionCreator.cs @@ -27,6 +27,19 @@ public static ChipDescription CreateChipDescription(DevChipInstance chip) Vector2 minChipsSize = SubChipInstance.CalculateMinChipSize(inputPins, outputPins, name); size = Vector2.Max(minChipsSize, size); + // Update DependsOnModIDs if any subchip is a modded chip + List dependsOnModIDs = new(); + foreach (var subchip in chip.GetSubchips()) + { + if (subchip.Description.ChipType == ChipType.Modded && subchip.Description.DependsOnModIDs != null) + { + dependsOnModIDs.AddRange(subchip.Description.DependsOnModIDs); + } + } + + // Remove duplicates + dependsOnModIDs = dependsOnModIDs.Distinct().ToList(); + UpdateWireIndicesForDescriptionCreation(chip); // Create and return the chip description @@ -43,7 +56,8 @@ public static ChipDescription CreateChipDescription(DevChipInstance chip) OutputPins = outputPins, Wires = chip.Wires.Select(CreateWireDescription).ToArray(), Displays = displays, - ChipType = ChipType.Custom + ChipType = ChipType.Custom, + DependsOnModIDs = dependsOnModIDs }; } diff --git a/Assets/Scripts/SaveSystem/Loader.cs b/Assets/Scripts/SaveSystem/Loader.cs index 70ddbfd6..4590cd1b 100644 --- a/Assets/Scripts/SaveSystem/Loader.cs +++ b/Assets/Scripts/SaveSystem/Loader.cs @@ -5,6 +5,7 @@ using DLS.Description; using DLS.Game; using DLS.Mods; +using UnityEngine; namespace DLS.SaveSystem { @@ -81,6 +82,8 @@ public static ProjectDescription[] LoadAllProjectDescriptions() static ChipLibrary LoadChipLibrary(ProjectDescription projectDescription) { + ChipDescription[] moddedChips = ModdedChipCreator.CreateAllModdedChipDescriptions(); + string chipDirectoryPath = SavePaths.GetChipsPath(projectDescription.ProjectName); ChipDescription[] loadedChips = new ChipDescription[projectDescription.AllCustomChipNames.Length]; @@ -89,14 +92,16 @@ static ChipLibrary LoadChipLibrary(ProjectDescription projectDescription) ChipDescription[] builtinChips = BuiltinChipCreator.CreateAllBuiltinChipDescriptions(); HashSet customChipNameHashset = new(ChipDescription.NameComparer); - ChipDescription[] moddedChips = ModdedChipCreator.CreateAllModdedChipDescriptions(); - for (int i = 0; i < loadedChips.Length; i++) { string chipPath = Path.Combine(chipDirectoryPath, projectDescription.AllCustomChipNames[i] + ".json"); string chipSaveString = File.ReadAllText(chipPath); ChipDescription chipDesc = Serializer.DeserializeChipDescription(chipSaveString); + foreach (string modId in chipDesc.DependsOnModIDs) + { + Debug.Log($"Chip '{chipDesc.Name}' depends on mod ID: {modId}"); + } loadedChips[i] = chipDesc; customChipNameHashset.Add(chipDesc.Name); } diff --git a/TestData/Mods/ExampleMod.dls b/TestData/Mods/ExampleMod.dls deleted file mode 100644 index dbd0ba00a909fd810747ca81ad2844d3dfb1a0d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6656 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 Date: Thu, 8 May 2025 21:52:53 +0100 Subject: [PATCH 10/11] feat(ModdingAPI): show warning when chips disabled --- Assets/Scripts/Game/Project/ChipLibrary.cs | 7 +- .../Scripts/Game/Project/ModdedChipCreator.cs | 1 - Assets/Scripts/Graphics/UI/Menus/MainMenu.cs | 1 + .../Graphics/UI/Menus/ModWarningPopup.cs | 86 +++++++++++++++++++ .../Graphics/UI/Menus/ModWarningPopup.cs.meta | 2 + Assets/Scripts/Graphics/UI/UIDrawer.cs | 15 +++- Assets/Scripts/Mods/ModLoader.cs | 9 +- .../Scripts/SaveSystem/DescriptionCreator.cs | 2 +- Assets/Scripts/SaveSystem/Loader.cs | 6 +- 9 files changed, 112 insertions(+), 17 deletions(-) create mode 100644 Assets/Scripts/Graphics/UI/Menus/ModWarningPopup.cs create mode 100644 Assets/Scripts/Graphics/UI/Menus/ModWarningPopup.cs.meta diff --git a/Assets/Scripts/Game/Project/ChipLibrary.cs b/Assets/Scripts/Game/Project/ChipLibrary.cs index 9fdf34b9..ce509701 100644 --- a/Assets/Scripts/Game/Project/ChipLibrary.cs +++ b/Assets/Scripts/Game/Project/ChipLibrary.cs @@ -49,12 +49,9 @@ void RebuildChipDescriptionLookup() descriptionFromNameLookup.Clear(); foreach (ChipDescription desc in allChips) { - if (desc.DependsOnModIDs == null || desc.DependsOnModIDs.All(ModLoader.IsModLoaded)) + if (!descriptionFromNameLookup.ContainsKey(desc.Name)) { - if (!descriptionFromNameLookup.ContainsKey(desc.Name)) - { - descriptionFromNameLookup.Add(desc.Name, desc); - } + descriptionFromNameLookup.Add(desc.Name, desc); } } diff --git a/Assets/Scripts/Game/Project/ModdedChipCreator.cs b/Assets/Scripts/Game/Project/ModdedChipCreator.cs index 8c000583..d389d4f7 100644 --- a/Assets/Scripts/Game/Project/ModdedChipCreator.cs +++ b/Assets/Scripts/Game/Project/ModdedChipCreator.cs @@ -19,7 +19,6 @@ public static ChipDescription[] CreateAllModdedChipDescriptions() unbuiltChips = Registry.moddedChips; foreach (ChipBuilder chip in unbuiltChips) { - Debug.Log(chip.displays); RegisterChip(chip.modID, chip.name, chip.size, chip.color, ConvertToDescriptionPins(chip.inputs), ConvertToDescriptionPins(chip.outputs), chip.displays != null ? ModdedDisplayCreator.RegisterDisplays(chip.displays) : null, chip.hideName, chip.simulationFunction); } return ModdedChips.ToArray(); diff --git a/Assets/Scripts/Graphics/UI/Menus/MainMenu.cs b/Assets/Scripts/Graphics/UI/Menus/MainMenu.cs index a0f0cd47..d57eff75 100644 --- a/Assets/Scripts/Graphics/UI/Menus/MainMenu.cs +++ b/Assets/Scripts/Graphics/UI/Menus/MainMenu.cs @@ -133,6 +133,7 @@ public static void OnMenuOpened() activeMenuScreen = MenuScreen.Main; activePopup = PopupKind.None; selectedProjectIndex = -1; + ModWarningPopup.MenuShown = false; } static void DrawMainScreen() diff --git a/Assets/Scripts/Graphics/UI/Menus/ModWarningPopup.cs b/Assets/Scripts/Graphics/UI/Menus/ModWarningPopup.cs new file mode 100644 index 00000000..d073fffb --- /dev/null +++ b/Assets/Scripts/Graphics/UI/Menus/ModWarningPopup.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using DLS.Game; +using DLS.Mods; +using Seb.Vis; +using Seb.Vis.UI; +using UnityEngine; +using UnityEngine.TextCore.Text; + +namespace DLS.Graphics +{ + public static class ModWarningPopup + { + public static bool MenuShown = false; + public static void DrawMenu() + { + MenuHelper.DrawBackgroundOverlay(); + Draw.ID panelID = UI.ReservePanel(); + DrawSettings.UIThemeDLS theme = DrawSettings.ActiveUITheme; + + Vector2 pos = UI.Centre + Vector2.up * (UI.HalfHeight * 0.25f); + + // Collect chip names hidden due to missing mods + string hiddenChipNames = string.Join("\n", Project.ActiveProject.chipLibrary.allChips + .Where(chip => chip.DependsOnModIDs != null && !chip.DependsOnModIDs.All(ModLoader.IsModLoaded)) + .Select(chip => chip.Name)); + + // Format chip names and their dependencies + string hiddenChipsDependencies = string.Join("\n", Project.ActiveProject.chipLibrary.allChips + .Where(chip => chip.DependsOnModIDs != null && !chip.DependsOnModIDs.All(ModLoader.IsModLoaded)) + .Select(chip => $"{chip.Name,-30}{string.Join(", ", chip.DependsOnModIDs.Where(id => !ModLoader.IsModLoaded(id))),30}")); + + using (UI.BeginBoundsScope(true)) + { + // Draw warning text + UI.DrawText( + "Some chips contain subchips from mods that are not loaded.\nThe following chips have been disabled until their\nassociated mods have been loaded:", + theme.FontBold, + theme.FontSizeRegular, + pos, + Anchor.TextCentre, + Color.white + ); + + UI.DrawText( + $"{"Chip Name", -30}{"Mod ID", 30}", + theme.FontBold, + theme.FontSizeRegular, + UI.GetCurrentBoundsScope().BottomLeft + Vector2.down * 3f, + Anchor.TextCentreLeft, + Color.white + ); + + // Draw list of missing mod IDs + UI.DrawText( + hiddenChipsDependencies, + theme.FontRegular, + theme.FontSizeRegular, + UI.GetCurrentBoundsScope().BottomLeft + Vector2.down * 1.5f, + Anchor.TextCentreLeft, + Color.white + ); + + bool result = UI.Button( + "OK", + theme.ButtonTheme, + UI.GetCurrentBoundsScope().CentreBottom + Vector2.down * 3f, + size: (UI.GetCurrentBoundsScope().Width - DrawSettings.DefaultButtonSpacing * 6) * Vector2.right + ); + + MenuHelper.DrawReservedMenuPanel(panelID, UI.GetCurrentBoundsScope()); + + if (result || KeyboardShortcuts.CancelShortcutTriggered) + { + UIDrawer.SetActiveMenu(UIDrawer.MenuType.None); + } + } + } + + public static void OnMenuOpened() + { + MenuShown = true; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Graphics/UI/Menus/ModWarningPopup.cs.meta b/Assets/Scripts/Graphics/UI/Menus/ModWarningPopup.cs.meta new file mode 100644 index 00000000..77e389f3 --- /dev/null +++ b/Assets/Scripts/Graphics/UI/Menus/ModWarningPopup.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d51e67903e3b19c49b7ed29f39cafe25 \ No newline at end of file diff --git a/Assets/Scripts/Graphics/UI/UIDrawer.cs b/Assets/Scripts/Graphics/UI/UIDrawer.cs index 091248e4..b8c20c88 100644 --- a/Assets/Scripts/Graphics/UI/UIDrawer.cs +++ b/Assets/Scripts/Graphics/UI/UIDrawer.cs @@ -1,5 +1,8 @@ +using System.Linq; using DLS.Game; +using DLS.Mods; using Seb.Vis.UI; +using UnityEngine; namespace DLS.Graphics { @@ -20,7 +23,8 @@ public enum MenuType PulseEdit, UnsavedChanges, Search, - ChipLabelPopup + ChipLabelPopup, + ModWarning } static MenuType activeMenuOld; @@ -68,6 +72,7 @@ static void DrawProjectMenus(Project project) else if (menuToDraw == MenuType.Search) SearchPopup.DrawMenu(); else if (menuToDraw == MenuType.ChipLabelPopup) ChipLabelMenu.DrawMenu(); else if (menuToDraw == MenuType.PulseEdit) PulseEditMenu.DrawMenu(); + else if (menuToDraw == MenuType.ModWarning) ModWarningPopup.DrawMenu(); else { bool showSimPausedBanner = project.simPaused; @@ -98,12 +103,20 @@ static void NotifyIfActiveMenuChanged() else if (ActiveMenu == MenuType.Search) SearchPopup.OnMenuOpened(); else if (ActiveMenu == MenuType.ChipLabelPopup) ChipLabelMenu.OnMenuOpened(); else if (ActiveMenu == MenuType.PulseEdit) PulseEditMenu.OnMenuOpened(); + else if (ActiveMenu == MenuType.ModWarning) ModWarningPopup.OnMenuOpened(); if (InInputBlockingMenu() && Project.ActiveProject != null && Project.ActiveProject.controller != null) { Project.ActiveProject.controller.CancelEverything(); } + // Trigger ModWarningPopup if there are chips dependent on unloaded mods + if (Project.ActiveProject != null && Project.ActiveProject.controller != null && Project.ActiveProject.chipLibrary.allChips.Any(chip => chip.DependsOnModIDs != null && !chip.DependsOnModIDs.All(ModLoader.IsModLoaded)) && ModWarningPopup.MenuShown == false) + { + SetActiveMenu(MenuType.ModWarning); + ModWarningPopup.OnMenuOpened(); + } + activeMenuOld = ActiveMenu; } } diff --git a/Assets/Scripts/Mods/ModLoader.cs b/Assets/Scripts/Mods/ModLoader.cs index 6ca81274..9694c8f8 100644 --- a/Assets/Scripts/Mods/ModLoader.cs +++ b/Assets/Scripts/Mods/ModLoader.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using DLS.ModdingAPI; +using UnityEngine; namespace DLS.Mods { @@ -29,7 +30,7 @@ public static void NotifyMods(Action action) public static void InitializeMods(string modsDirectory) { - UnityEngine.Debug.Log("Loading mods..."); + Debug.Log("Loading mods..."); foreach (string dllPath in Directory.GetFiles(modsDirectory, "*.dls")) { try @@ -41,19 +42,19 @@ public static void InitializeMods(string modsDirectory) IMod modInstance = (IMod) Activator.CreateInstance(type); loadedMods.Add(modInstance); modInstance.Initialize(); - UnityEngine.Debug.Log($"Loaded mod: {modInstance.Name} v{modInstance.Version}"); + Debug.Log($"Loaded mod: {modInstance.Name} v{modInstance.Version}"); } } catch (ReflectionTypeLoadException ex) { foreach (Exception inner in ex.LoaderExceptions) { - UnityEngine.Debug.LogError(inner.Message); + Debug.LogError(inner.Message); } } catch (FileLoadException ex) { - UnityEngine.Debug.LogError(ex.Message); + Debug.LogError(ex.Message); } } } diff --git a/Assets/Scripts/SaveSystem/DescriptionCreator.cs b/Assets/Scripts/SaveSystem/DescriptionCreator.cs index e72e6ba8..ba4a1af9 100644 --- a/Assets/Scripts/SaveSystem/DescriptionCreator.cs +++ b/Assets/Scripts/SaveSystem/DescriptionCreator.cs @@ -31,7 +31,7 @@ public static ChipDescription CreateChipDescription(DevChipInstance chip) List dependsOnModIDs = new(); foreach (var subchip in chip.GetSubchips()) { - if (subchip.Description.ChipType == ChipType.Modded && subchip.Description.DependsOnModIDs != null) + if (subchip.Description.DependsOnModIDs != null && subchip.Description.DependsOnModIDs.Count != 0) { dependsOnModIDs.AddRange(subchip.Description.DependsOnModIDs); } diff --git a/Assets/Scripts/SaveSystem/Loader.cs b/Assets/Scripts/SaveSystem/Loader.cs index 4590cd1b..537ca64c 100644 --- a/Assets/Scripts/SaveSystem/Loader.cs +++ b/Assets/Scripts/SaveSystem/Loader.cs @@ -4,6 +4,7 @@ using System.Linq; using DLS.Description; using DLS.Game; +using DLS.Graphics; using DLS.Mods; using UnityEngine; @@ -98,15 +99,10 @@ static ChipLibrary LoadChipLibrary(ProjectDescription projectDescription) string chipSaveString = File.ReadAllText(chipPath); ChipDescription chipDesc = Serializer.DeserializeChipDescription(chipSaveString); - foreach (string modId in chipDesc.DependsOnModIDs) - { - Debug.Log($"Chip '{chipDesc.Name}' depends on mod ID: {modId}"); - } loadedChips[i] = chipDesc; customChipNameHashset.Add(chipDesc.Name); } - // If built-in chip name conflicts with a custom chip, the built-in chip must have been added in a newer version. // In that case, simply exclude the built-in chip. TODO: warn player that they should rename their chip if they want access to new builtin version builtinChips = builtinChips.Where(b => !customChipNameHashset.Contains(b.Name)).ToArray(); From 3e6c4e97521a8c01f5ad71f385561a62a9595a22 Mon Sep 17 00:00:00 2001 From: Squiggles Date: Fri, 9 May 2025 17:34:04 +0100 Subject: [PATCH 11/11] feat(ModdingAPI): keyboard shortcut builder --- .../Game/Interaction/KeyboardShortcuts.cs | 17 ++++++++++ Assets/Scripts/ModdingAPI/Registry.cs | 19 +++++++++++- Assets/Scripts/ModdingAPI/ShortcutBuilder.cs | 31 +++++++++++++++++++ .../ModdingAPI/ShortcutBuilder.cs.meta | 2 ++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 Assets/Scripts/ModdingAPI/ShortcutBuilder.cs create mode 100644 Assets/Scripts/ModdingAPI/ShortcutBuilder.cs.meta diff --git a/Assets/Scripts/Game/Interaction/KeyboardShortcuts.cs b/Assets/Scripts/Game/Interaction/KeyboardShortcuts.cs index 21f9dbd0..ca0cfe87 100644 --- a/Assets/Scripts/Game/Interaction/KeyboardShortcuts.cs +++ b/Assets/Scripts/Game/Interaction/KeyboardShortcuts.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; +using DLS.ModdingAPI; using Seb.Helpers; using UnityEngine; @@ -51,5 +53,20 @@ public static class KeyboardShortcuts static bool CtrlShortcutTriggered(KeyCode key) => InputHelper.IsKeyDownThisFrame(key) && InputHelper.CtrlIsHeld && !(InputHelper.AltIsHeld || InputHelper.ShiftIsHeld); static bool CtrlShiftShortcutTriggered(KeyCode key) => InputHelper.IsKeyDownThisFrame(key) && InputHelper.CtrlIsHeld && InputHelper.ShiftIsHeld && !(InputHelper.AltIsHeld); static bool ShiftShortcutTriggered(KeyCode key) => InputHelper.IsKeyDownThisFrame(key) && InputHelper.ShiftIsHeld && !(InputHelper.AltIsHeld || InputHelper.CtrlIsHeld); + + // ---- Modded shortcuts ---- + public static bool GetModdedShortcut(string shortcutName) + { + if (Registry.ModdedShortcuts.TryGetValue(shortcutName, out var shortcut)) + { + bool keyTriggered = InputHelper.IsKeyDownThisFrame(shortcut.Key); + bool modifierConditionMet = shortcut.ModifierCondition?.Invoke() ?? true; + + return keyTriggered && modifierConditionMet; + } + + Debug.LogWarning($"Shortcut '{shortcutName}' is not registered."); + return false; + } } } \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/Registry.cs b/Assets/Scripts/ModdingAPI/Registry.cs index 2c21b5b2..4e1ff786 100644 --- a/Assets/Scripts/ModdingAPI/Registry.cs +++ b/Assets/Scripts/ModdingAPI/Registry.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using UnityEngine; namespace DLS.ModdingAPI { @@ -6,6 +7,7 @@ public static class Registry { public static List moddedChips = new(); public static List moddedCollections = new(); + public static readonly Dictionary ModdedShortcuts = new(); public static void RegisterChips(params ChipBuilder[] chips) { foreach (ChipBuilder chip in chips) @@ -16,10 +18,25 @@ public static void RegisterChips(params ChipBuilder[] chips) public static void RegisterCollections(params CollectionBuilder[] collections) { - foreach(CollectionBuilder collection in collections) + foreach (CollectionBuilder collection in collections) { moddedCollections.Add(collection); } } + + public static void RegisterShortcuts(params ShortcutBuilder[] shortcuts) + { + foreach (ShortcutBuilder shortcut in shortcuts) + { + if (!ModdedShortcuts.ContainsKey(shortcut.Name)) + { + ModdedShortcuts[shortcut.Name] = shortcut; + } + else + { + Debug.LogWarning($"Shortcut with name '{shortcut.Name}' is already registered."); + } + } + } } } \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/ShortcutBuilder.cs b/Assets/Scripts/ModdingAPI/ShortcutBuilder.cs new file mode 100644 index 00000000..a63a1dc8 --- /dev/null +++ b/Assets/Scripts/ModdingAPI/ShortcutBuilder.cs @@ -0,0 +1,31 @@ +using System; +using UnityEngine; + +namespace DLS.ModdingAPI +{ + public class ShortcutBuilder + { + public readonly string modID; + public readonly string Name; + public KeyCode Key; + public Func ModifierCondition; + + public ShortcutBuilder(string modID, string name) + { + this.modID = modID; + Name = name; + } + + public ShortcutBuilder SetKey(KeyCode key) + { + Key = key; + return this; + } + + public ShortcutBuilder SetModifierCondition(Func modifierCondition) + { + ModifierCondition = modifierCondition; + return this; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ModdingAPI/ShortcutBuilder.cs.meta b/Assets/Scripts/ModdingAPI/ShortcutBuilder.cs.meta new file mode 100644 index 00000000..7f55217c --- /dev/null +++ b/Assets/Scripts/ModdingAPI/ShortcutBuilder.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c8251fa4c880009479062b4ee6d192f7 \ No newline at end of file