diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 525e474..1858c3c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,10 +66,6 @@ jobs: - name: Move NuGet Packages run: mv (Get-ChildItem -Recurse ./ -Include *.nupkg) ./ - - # Removes the version number from the package name - - name: Rename NuGet Packages - run: Get-ChildItem -Include *.nupkg -Path ./* | Rename-Item -NewName { $_.Name -Replace '\.\d+\.\d+\.\d+.*$','.nupkg' } # Publish the NuGet package(s) as an artifact, so they can be used in the following jobs - name: Upload NuGet Packages Artifact @@ -78,7 +74,7 @@ jobs: name: NuGet Packages if-no-files-found: error retention-days: 7 - path: ./*.nupkg + path: ./MonkeyLoader.GamePacks.ResoniteModLoader.nupkg # Only when it's not from a PR to avoid any funny packages in the cache - name: Save NuGet Package Cache diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c61aa44..3e65cc7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -70,9 +70,6 @@ jobs: with: dotnet-version: 10.x source-url: https://nuget.pkg.github.com/ResoniteModdingGroup/index.json - - - name: Add MonkeyLoader NuGet Source - run: dotnet nuget add source https://pkg.munally.com/MonkeyModdingTroop/index.json # Publish all NuGet packages to the GitHub feed # Use --skip-duplicate to prevent errors if a package with the same version already exists. diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader.sln b/MonkeyLoader.GamePacks.ResoniteModLoader.sln deleted file mode 100644 index 246ab67..0000000 --- a/MonkeyLoader.GamePacks.ResoniteModLoader.sln +++ /dev/null @@ -1,40 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.33516.290 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CEAAAF5F-3B06-40E0-B0D2-3BBD3513038B}" - ProjectSection(SolutionItems) = preProject - Directory.Build.props = Directory.Build.props - README.md = README.md - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonkeyLoader.GamePacks.ResoniteModLoader", "MonkeyLoader.GamePacks.ResoniteModLoader\MonkeyLoader.GamePacks.ResoniteModLoader.csproj", "{41F303F4-2BE1-4462-B5DC-144ECA630242}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build.yml = .github\workflows\build.yml - .github\workflows\publish.yml = .github\workflows\publish.yml - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {41F303F4-2BE1-4462-B5DC-144ECA630242}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {41F303F4-2BE1-4462-B5DC-144ECA630242}.Debug|Any CPU.Build.0 = Debug|Any CPU - {41F303F4-2BE1-4462-B5DC-144ECA630242}.Release|Any CPU.ActiveCfg = Release|Any CPU - {41F303F4-2BE1-4462-B5DC-144ECA630242}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {CEAAAF5F-3B06-40E0-B0D2-3BBD3513038B} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {7D505B2E-FBA2-4F50-BC1C-838BC8DF0D5C} - EndGlobalSection -EndGlobal diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader.slnx b/MonkeyLoader.GamePacks.ResoniteModLoader.slnx new file mode 100644 index 0000000..f72a922 --- /dev/null +++ b/MonkeyLoader.GamePacks.ResoniteModLoader.slnx @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/ExecutionHook.cs b/MonkeyLoader.GamePacks.ResoniteModLoader/ExecutionHook.cs index 7c73a2b..af396f5 100644 --- a/MonkeyLoader.GamePacks.ResoniteModLoader/ExecutionHook.cs +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/ExecutionHook.cs @@ -41,7 +41,7 @@ public void NotifyOfScreenshot(World world, string file, ScreenshotType type, Da public Task Initialize(PlatformInterface platformInterface) { - ModLoader.Logger.Debug(() => "Initialize() from platformInterface"); + ModLoaderHook.Logger.Debug(() => "Initialize() from platformInterface"); Platform = platformInterface; return Task.FromResult(true); } diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/de.json b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/de.json index 7d19eba..a8c3e75 100644 --- a/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/de.json +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/de.json @@ -2,6 +2,34 @@ "localeCode": "de", "authors": [ "Banane9" ], "messages": { - "MonkeyLoader.GamePacks.ResoniteModLoader.ModLoader.Description": "Läd Resonite Mod Loader (RML) Bibliotheken und Mods von ihren üblichen Orten." + "MonkeyLoader.GamePacks.ResoniteModLoader.ModLoader.Description": "Läd ResoniteModLoader (RML) Bibliotheken und Mods von ihren üblichen Orten.", + + "Settings.Category.ResoniteModLoader": "ResoniteModLoader", + "Settings.ModLoaderSettings": "ResoniteModLoader-Einstellungen", + + "Settings.ModLoaderSettings.DebugMode": "Debugmodus", + "Settings.ModLoaderSettings.DebugMode.Description": "Aktiviert Debug-Logging.", + "Settings.ModLoaderSettings.HideVisuals": "Visuelle Elemente verstecken", + "Settings.ModLoaderSettings.HideVisuals.Description": "Versteckt die Fortschrittsanzeige des Modloaders während des Startvorgangs.", + "Settings.ModLoaderSettings.LoadedMods": "Geladene Mods", + "Settings.ModLoaderSettings.ModLoaderVersion": "Version", + "Settings.ModLoaderSettings.ProjectLink": "GitHub Repo", + + "Settings.ModLoaderDebugSettings": "Debug-Einstellungen", + "Settings.ModLoaderDebugSettings.RefreshLocale": "Lokalisierung neu laden", + "Settings.ModLoaderDebugSettings.LocaleCount": "Einträge in der Lokalisierung zählen", + "Settings.ModLoaderDebugSettings.LocaleKeyCount": "Anzahl der Lokalisierungseinträge", + "Settings.ModLoaderDebugSettings.ForceAssetUpdate": "Asset-Update erzwingen", + + "Settings.ModSettings": "Mod-Einstellungen", + "Settings.ModSettings.ModList": "Mod-Einstellungen editieren", + "Settings.ModSettings.ModList.Breadcrumb": "Mod-Einstellungen", + "Settings.ModSettings.NoMods": "Keine Mods geladen", + "Settings.ModSettings.NoModsWithConfig": "Keine Mods mit Einstellungen zum anzeigen vorhanden", + "Settings.ModSettings.NoConfigs": "Keine Einstellungen für die ausgewählte Mod verfügbar.", + "Settings.ModSettings.ShowInternal": "Zeige Einstellungen für interne Nutzung", + "Settings.ModSettings.ShowInternal.Description": "Interne Einstellungen sind üblicherweise nicht für die direkte Änderung durch den Nutzer gedacht. Dies kann unbeabsichtige Auswirkungen haben.", + "Settings.ModSettings.ShowAll": "Alle Mods anzeigen", + "Settings.ModSettings.ShowAll.Description": "Zeigt Einträge für alle Mods, inklusive der ohne Einstellungsmöglichkeiten." } } \ No newline at end of file diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/en.json b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/en.json index d4d55f6..289aace 100644 --- a/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/en.json +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/en.json @@ -1,7 +1,36 @@ { "localeCode": "en", - "authors": [ "Banane9" ], + "authors": [ "Banane9", "Delta" ], "messages": { - "MonkeyLoader.GamePacks.ResoniteModLoader.ModLoader.Description": "Loads Resonite Mod Loader (RML) libraries and mods from their usual locations." + "MonkeyLoader.GamePacks.ResoniteModLoader.ModLoader.Name": "ResoniteModLoader", + "MonkeyLoader.GamePacks.ResoniteModLoader.ModLoader.Description": "Loads ResoniteModLoader (RML) libraries and mods from their usual locations.", + + "Settings.Category.ResoniteModLoader": "ResoniteModLoader", + "Settings.ModLoaderSettings": "ResoniteModLoader Settings", + + "Settings.ModLoaderSettings.DebugMode": "Enable Debug", + "Settings.ModLoaderSettings.DebugMode.Description": "Enables Debug Logging", + "Settings.ModLoaderSettings.HideVisuals": "Hide Visuals", + "Settings.ModLoaderSettings.HideVisuals.Description": "Hides Modloader progress indicator when launching", + "Settings.ModLoaderSettings.LoadedMods": "Loaded Mods", + "Settings.ModLoaderSettings.ModLoaderVersion": "Version", + "Settings.ModLoaderSettings.ProjectLink": "Github Repo", + + "Settings.ModLoaderDebugSettings": "Debugging Settings", + "Settings.ModLoaderDebugSettings.RefreshLocale": "Refresh Locale", + "Settings.ModLoaderDebugSettings.LocaleCount": "Count Locale Keys", + "Settings.ModLoaderDebugSettings.LocaleKeyCount": "Locale Key Count", + "Settings.ModLoaderDebugSettings.ForceAssetUpdate": "Force Asset Update", + + "Settings.ModSettings": "Mod Settings", + "Settings.ModSettings.ModList": "Edit Mod Settings", + "Settings.ModSettings.ModList.Breadcrumb": "Mod Settings", + "Settings.ModSettings.NoMods": "No mods loaded", + "Settings.ModSettings.NoModsWithConfig": "No mods with configs to show", + "Settings.ModSettings.NoConfigs": "No settings are available for the selected mod.", + "Settings.ModSettings.ShowInternal": "Show internal use configs", + "Settings.ModSettings.ShowInternal.Description": "Internal configs aren't generally intended to be changed directly by the user. Can cause unintended behaviour.", + "Settings.ModSettings.ShowAll": "Show all mods", + "Settings.ModSettings.ShowAll.Description": "Show all entries for mods, including ones with no settings." } } \ No newline at end of file diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/eo.json b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/eo.json new file mode 100644 index 0000000..13abe74 --- /dev/null +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/eo.json @@ -0,0 +1,22 @@ +{ + "localeCode": "eo", + "authors": [ "Delta" ], + "messages": { + "Settings.Category.ResoniteModLoader": "ResoniteModLoader", + "Settings.ModLoaderSettings": "Agordoj de ResoniteModLoader", + + "Settings.ModLoaderSettings.DebugMode": "Ebligi Sencimigon", + "Settings.ModLoaderSettings.DebugMode.Description": "Ebligu Sencimigan Protokolon", + "Settings.ModLoaderSettings.HideVisuals": "Kaŝi Vidaĵojn", + "Settings.ModLoaderSettings.HideVisuals.Description": "Kaŝas la progresa indikilo de la modŝargilo dum lanĉo", + "Settings.ModLoaderSettings.LoadedMods": "Ŝargitaj Modoj", + "Settings.ModLoaderSettings.ModLoaderVersion": "Versio", + "Settings.ModLoaderSettings.ProjectLink": "Github-Deponejo", + + "Settings.ModLoaderDebugSettings": "Sencimigaj Agordoj", + "Settings.ModLoaderDebugSettings.RefreshLocale": "Refreŝigi Lokalon", + "Settings.ModLoaderDebugSettings.LocaleCount": "Nombri Lokajn Ŝlosilojn", + "Settings.ModLoaderDebugSettings.LocaleKeyCount": "Nombri Lokajn Ŝlosilojn", + "Settings.ModLoaderDebugSettings.ForceAssetUpdate": "Devigi Asetan Ĝisdatigon" + } +} \ No newline at end of file diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/ja.json b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/ja.json index 362086f..43db164 100644 --- a/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/ja.json +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/ja.json @@ -2,6 +2,22 @@ "localeCode": "ja", "authors": [ "kazu" ], "messages": { - "MonkeyLoader.GamePacks.ResoniteModLoader.ModLoader.Description": "Resonite Mod Loader (RML) ライブラリ 及び モッド を通常の場所から読み込めるようにします。" + "MonkeyLoader.GamePacks.ResoniteModLoader.ModLoader.Description": "ResoniteModLoader (RML) ライブラリ 及び モッド を通常の場所から読み込めるようにします。", + + "Settings.Category.ResoniteModLoader": "ResoniteModLoader", + "Settings.ModLoaderSettings": "ResoniteModLoader 設定", + + "Settings.ModLoaderSettings.DebugMode": "デバッグログを有効化", + "Settings.ModLoaderSettings.DebugMode.Description": "デバッグログを記録するようにします。", + "Settings.ModLoaderSettings.HideVisuals": "表示を隠す", + "Settings.ModLoaderSettings.HideVisuals.Description": "起動時に表示される Modloader の読み込み表示を隠します。", + "Settings.ModLoaderSettings.LoadedMods": "読み込まれた Mod", + "Settings.ModLoaderSettings.ModLoaderVersion": "バージョン", + + "Settings.ModLoaderDebugSettings": "デバック設定", + "Settings.ModLoaderDebugSettings.RefreshLocale": "ロケールを更新", + "Settings.ModLoaderDebugSettings.LocaleCount": "ロケールキーを数える", + "Settings.ModLoaderDebugSettings.LocaleKeyCount": "ロケールの個数", + "Settings.ModLoaderDebugSettings.ForceAssetUpdate": "アセットを強制的に更新" } } \ No newline at end of file diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/pl.json b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/pl.json new file mode 100644 index 0000000..a551a17 --- /dev/null +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/pl.json @@ -0,0 +1,31 @@ +{ + "localeCode": "pl", + "authors": [ "Alex_2Pi" ], + "messages": { + "Settings.ModLoaderSettings": "ResoniteModLoader Ustawienia", + + "Settings.ModLoaderSettings.DebugMode": "Włącz Debugowanie", + "Settings.ModLoaderSettings.DebugMode.Description": "Włącza dziennik debugowania", + "Settings.ModLoaderSettings.HideVisuals": "Ukryj podgląd", + "Settings.ModLoaderSettings.HideVisuals.Description": "Ukrywa pasek postępu Modloadera podczas uruchamiania", + "Settings.ModLoaderSettings.LoadedMods": "Załadowane Modyfikacje", + "Settings.ModLoaderSettings.ModLoaderVersion": "Wersja", + + "Settings.ModLoaderDebugSettings": "Ustawienia debugowania", + "Settings.ModLoaderDebugSettings.RefreshLocale": "Odśwież ustawienia językowe", + "Settings.ModLoaderDebugSettings.LocaleCount": "Zlicz klucze językowe", + "Settings.ModLoaderDebugSettings.LocaleKeyCount": "Ilość kluczy lokalnych", + "Settings.ModLoaderDebugSettings.ForceAssetUpdate": "Wymuszona aktualizacja zasobów", + + "Settings.ModSettings": "Ustawienia modyfikacji", + "Settings.ModSettings.ModList": "Edytuj ustawienia modyfikacji", + "Settings.ModSettings.ModList.Breadcrumb": "Ustawienia modyfikacji", + "Settings.ModSettings.NoMods": "Brak załadowanych modów", + "Settings.ModSettings.NoModsWithConfig": "Brak modów z konfiguracjami do pokazania", + "Settings.ModSettings.NoConfigs": "Dla wybranego modu nie są dostępne żadne ustawienia.", + "Settings.ModSettings.ShowInternal": "Pokaż konfiguracje do użytku wewnętrznego", + "Settings.ModSettings.ShowInternal.Description": "Konfiguracje wewnętrzne nie są zazwyczaj przeznaczone do bezpośredniej zmiany przez użytkownika. Zmiana ich spowodować niepożądane zachowania.", + "Settings.ModSettings.ShowAll": "Pokaż wszystkie modyfikacje", + "Settings.ModSettings.ShowAll.Description": "Pokaż wszystkie wpisy dotyczące modów, w tym te bez ustawień." + } +} \ No newline at end of file diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/zh-cn.json b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/zh-cn.json new file mode 100644 index 0000000..f01f90a --- /dev/null +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/zh-cn.json @@ -0,0 +1,33 @@ +{ + "localeCode": "zh-cn", + "authors": [ "modimobeikete" ], + "messages": { + "Settings.Category.ResoniteModLoader": "Resonite模组加载器", + "Settings.ModLoaderSettings": "Resonite模组加载器设置", + + "Settings.ModLoaderSettings.DebugMode": "开启调试模式", + "Settings.ModLoaderSettings.DebugMode.Description": "开启调试日志", + "Settings.ModLoaderSettings.HideVisuals": "隐藏模组加载器进度条", + "Settings.ModLoaderSettings.HideVisuals.Description": "启动时隐藏模组加载器进度条", + "Settings.ModLoaderSettings.LoadedMods": "已加载的模组", + "Settings.ModLoaderSettings.ModLoaderVersion": "版本", + "Settings.ModLoaderSettings.ProjectLink": "Github仓库", + + "Settings.ModLoaderDebugSettings": "调试设置", + "Settings.ModLoaderDebugSettings.RefreshLocale": "刷新语言", + "Settings.ModLoaderDebugSettings.LocaleCount": "计算语言文本", + "Settings.ModLoaderDebugSettings.LocaleKeyCount": "语言文本数", + "Settings.ModLoaderDebugSettings.ForceAssetUpdate": "资产强制更新", + + "Settings.ModSettings": "模组设置", + "Settings.ModSettings.ModList": "编辑模组设置", + "Settings.ModSettings.ModList.Breadcrumb": "模组设置", + "Settings.ModSettings.NoMods": "没有加载的模组", + "Settings.ModSettings.NoModsWithConfig": "模组内没有可显示的配置", + "Settings.ModSettings.NoConfigs": "该模组没有可用的设置", + "Settings.ModSettings.ShowInternal": "显示内部可用配置", + "Settings.ModSettings.ShowInternal.Description": "内部配置通常不打算由用户直接更改。可能会导致意外行为。", + "Settings.ModSettings.ShowAll": "展示所有模组", + "Settings.ModSettings.ShowAll.Description": "显示模组的所有内容,包括没有设置的。" + } +} \ No newline at end of file diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/zh-tw.json b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/zh-tw.json new file mode 100644 index 0000000..04e0e89 --- /dev/null +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/Locale/zh-tw.json @@ -0,0 +1,32 @@ +{ + "localeCode": "zh-tw", + "authors": [ "Meow Wei 魏喵" ], + "messages": { + "Settings.Category.ResoniteModLoader": "RML", + "Settings.ModLoaderSettings": "ResoniteModLoader 設定", + + "Settings.ModLoaderSettings.DebugMode": "啟用除錯模式", + "Settings.ModLoaderSettings.DebugMode.Description": "本選項會啟用除錯記錄檔。", + "Settings.ModLoaderSettings.HideVisuals": "隱藏 Mod 載入顯示", + "Settings.ModLoaderSettings.HideVisuals.Description": "本選項啟動時,會隱藏 Mod 載入狀態的顯示。", + "Settings.ModLoaderSettings.LoadedMods": "已載入的 Mod 數量", + "Settings.ModLoaderSettings.ModLoaderVersion": "版本", + "Settings.ModLoaderSettings.ProjectLink": "GitHub 專案網址", + + "Settings.ModLoaderDebugSettings": "除錯設定", + "Settings.ModLoaderDebugSettings.RefreshLocale": "更新語系檔", + "Settings.ModLoaderDebugSettings.LocaleCount": "統計語系鍵", + "Settings.ModLoaderDebugSettings.LocaleKeyCount": "語系鍵數量", + "Settings.ModLoaderDebugSettings.ForceAssetUpdate": "強制更新資產", + + "Settings.ModSettings": "Mod 設定", + "Settings.ModSettings.ModList": "變更 Mod 設定", + "Settings.ModSettings.ModList.Breadcrumb": "Mod 設定", + "Settings.ModSettings.NoMods": "未載入任何 Mod", + "Settings.ModSettings.NoConfigs": "目前選擇的 Mod 沒有提供任何設定。", + "Settings.ModSettings.ShowInternal": "顯示內部用設定", + "Settings.ModSettings.ShowInternal.Description": "內部用設定通常不是設計給使用者調整的選項,調整可能會導致非預期的效果。", + "Settings.ModSettings.ShowAll": "顯示所有 Mod", + "Settings.ModSettings.ShowAll.Description": "在列表中顯示所有 Mod,包含沒有提供設定在內的 Mod。" + } +} \ No newline at end of file diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/ModConfigurationKey.cs b/MonkeyLoader.GamePacks.ResoniteModLoader/ModConfigurationKey.cs index 269ce2b..de0eef6 100644 --- a/MonkeyLoader.GamePacks.ResoniteModLoader/ModConfigurationKey.cs +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/ModConfigurationKey.cs @@ -158,7 +158,7 @@ public ModConfigurationKey(string name, string? description = null, Func? com { if (string.IsNullOrWhiteSpace(name)) { - ModLoader.Logger.Warn(() => $"ModConfigurationKey with description [{description}] has null or whitespace name - using Spacer name!"); + ModLoaderHook.Logger.Warn(() => $"ModConfigurationKey with description [{description}] has null or whitespace name - using Spacer name!"); Key = new($"Spacer-{name?.GetHashCode() ?? description?.GetHashCode() ?? _replacementCounter++}", description, computeDefault, internalAccessOnly, valueValidator); diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/ModLoader.cs b/MonkeyLoader.GamePacks.ResoniteModLoader/ModLoader.cs index 0f2156b..b50a47b 100644 --- a/MonkeyLoader.GamePacks.ResoniteModLoader/ModLoader.cs +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/ModLoader.cs @@ -1,14 +1,4 @@ using Elements.Core; -using FrooxEngine; -using HarmonyLib; -using MonkeyLoader; -using MonkeyLoader.Meta; -using MonkeyLoader.NuGet; -using MonkeyLoader.Patching; -using MonkeyLoader.Resonite; -using MonkeyLoader.Resonite.Features.FrooxEngine; -using NuGet.Packaging.Core; -using NuGet.Versioning; using System.Reflection; namespace ResoniteModLoader @@ -16,20 +6,29 @@ namespace ResoniteModLoader /// /// Contains the actual mod loader. /// - [HarmonyPatchCategory(nameof(ModLoader))] - [HarmonyPatch(typeof(EngineInitializer), nameof(EngineInitializer.InitializeFrooxEngine))] - public sealed class ModLoader : ResoniteMonkey + public sealed class ModLoader { /// /// ResoniteModLoader's version /// public static readonly string VERSION = VERSION_CONSTANT; - internal const string VERSION_CONSTANT = "4.2.0"; + internal const string VERSION_CONSTANT = "5.0.1"; private static readonly Lazy _isHeadless = new(() => AppDomain.CurrentDomain.GetAssemblies() - .Any(assembly => assembly.GetName().Name?.StartsWith("Resonite") ?? false)); + .SelectMany(static assembly => + { + try + { + return assembly.GetTypes(); + } + catch (ReflectionTypeLoadException typeLoadException) + { + return typeLoadException.Types ?? []; + } + }) + .Any(static type => type?.Namespace is "FrooxEngine.Headless")); /// /// Gets whether this is running on a headless client. @@ -37,137 +36,11 @@ public sealed class ModLoader : ResoniteMonkey /// true if ResoniteModLoader was loaded by a headless; otherwise, false. public static bool IsHeadless => _isHeadless.Value; - /// - public override string Name { get; } = "ModLoader"; - /// - /// Allows reading metadata for all loaded mods + /// Allows reading metadata for all loaded mods. /// - /// A new list containing each loaded mod + /// A sequence of all loaded resonite mods. public static IEnumerable Mods() - { - return Mod.Loader.RegularMods - .OfType() - .SelectMany(rmlMod => rmlMod.Monkeys) - .Cast(); - } - - /// - protected override IEnumerable GetFeaturePatches() - { - yield return new FeaturePatch(PatchCompatibility.HookOnly); - } - - /// - protected override bool OnEngineInit() - { - LoadProgressReporter.AddFixedPhases(4); - - return base.OnEngineInit(); - } - - /// - protected override bool OnEngineReady() => true; - - /// - protected override bool OnLoaded() => base.OnEngineReady(); - - private static IEnumerable GetAssemblyPaths(string root) - { - if (!Directory.Exists(root)) - yield break; - - foreach (var file in Directory.EnumerateFiles(root, "*.dll")) - { - if (Path.GetExtension(file).Equals(".dll", StringComparison.OrdinalIgnoreCase)) - yield return file; - } - } - - [HarmonyPostfix] - private static async Task InitializeFrooxEnginePostfixAsync(Task __result) - { - await __result; - - LoadProgressReporter.AdvanceFixedPhase("Loading RML Libraries..."); - - try - { - foreach (var file in GetAssemblyPaths("rml_libs")) - { - LoadProgressReporter.SetSubphase($"{Environment.NewLine}  {Path.GetFileNameWithoutExtension(file)}"); - - try - { - var assembly = Mod.Loader.AssemblyLoadStrategy.LoadFile(Path.GetFullPath(file)); - var name = assembly.GetName(); - - Mod.Loader.NuGet.Add(new LoadedNuGetPackage(new PackageIdentity(name.Name, new NuGetVersion(name.Version!)), NuGetHelper.Framework)); - - Logger.Info(() => $"Loaded library {name.Name}.{name.Version} from rml_libs: {file}"); - } - catch (Exception ex) - { - Logger.Warn(() => ex.Format($"Failed to load library from rml_libs: {file}")); - } - } - - LoadProgressReporter.AdvanceFixedPhase("Collecting RML Mods..."); - - var rmlMods = await LoadModsAsync().ToArrayAsync(); - - LoadProgressReporter.AdvanceFixedPhase("Running RML Mods..."); - await Task.Run(() => Mod.Loader.RunMods(rmlMods)); - LoadProgressReporter.AdvanceFixedPhase("Done with RML"); - } - catch (Exception ex) - { - Logger.Error(() => ex.Format("Exception in execution hook!")); - LoadProgressReporter.AdvanceFixedPhase("Error running RML Mods!"); - } - } - - private static async IAsyncEnumerable LoadModsAsync() - { - var modAssemblies = new List(); - - foreach (var file in GetAssemblyPaths("rml_mods")) - { - try - { - var modAssembly = await Task.Run(() => Mod.Loader.AssemblyLoadStrategy.LoadFile(Path.GetFullPath(file!))); - modAssemblies.Add(modAssembly); - } - catch (Exception ex) - { - Logger.Warn(() => ex.Format($"Failed to load assembly from rml_mods: {file}")); - } - } - - foreach (var modAssembly in modAssemblies) - { - var fileName = Path.GetFileName(modAssembly.Location); - LoadProgressReporter.SetSubphase($"{Environment.NewLine}  {modAssembly.GetName().Name!}"); - - RmlMod? rmlMod = null; - var success = true; - - try - { - rmlMod = new RmlMod(Mod.Loader, modAssembly); - Logger.Info(() => $"Loaded mod from rml_mods: {fileName}"); - - Mod.Loader.AddMod(rmlMod); - } - catch (Exception ex) - { - success = false; - Logger.Warn(() => ex.Format($"Failed to load mod from rml_mods: {fileName}")); - } - - if (success) - yield return rmlMod!; - } - } + => ModLoaderHook.ResoniteMods; } } \ No newline at end of file diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/ModLoaderHook.cs b/MonkeyLoader.GamePacks.ResoniteModLoader/ModLoaderHook.cs new file mode 100644 index 0000000..31aa831 --- /dev/null +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/ModLoaderHook.cs @@ -0,0 +1,229 @@ +using Elements.Assets; +using FrooxEngine; +using HarmonyLib; +using MonkeyLoader; +using MonkeyLoader.Meta; +using MonkeyLoader.NuGet; +using MonkeyLoader.Patching; +using MonkeyLoader.Resonite; +using MonkeyLoader.Resonite.Features.FrooxEngine; +using MonkeyLoader.Resonite.Locale; +using NuGet.Packaging.Core; +using NuGet.Versioning; +using System.Reflection; +using System.Security.Cryptography; +using System.Text.Json; + +namespace ResoniteModLoader +{ + /// + /// Contains the actual mod loading hook executed by MonkeyLoader. + /// + [HarmonyPatchCategory(nameof(ModLoaderHook))] + [HarmonyPatch(typeof(EngineInitializer), nameof(EngineInitializer.InitializeFrooxEngine))] + internal sealed class ModLoaderHook : ResoniteAsyncEventHandlerMonkey + { + /// + /// Gets a sequence of all currently loaded localized s. + /// + public static IEnumerable LocalizedResoniteMods + => RmlMods.Where(static rmlMod => rmlMod.IsLocalized) + .SelectMany(static rmlMod => rmlMod.Monkeys) + .Cast(); + + /// + /// Gets a sequence of all currently loaded s. + /// + public static IEnumerable ResoniteMods + => RmlMods.SelectMany(static rmlMod => rmlMod.Monkeys) + .Cast(); + + /// + /// Gets a sequence of all currently loaded s. + /// + public static IEnumerable RmlMods + => Mod.Loader.RegularMods.OfType(); + + /// + public override string Name { get; } = "ModLoader"; + + public override int Priority => HarmonyLib.Priority.VeryLow; + + /// + protected override IEnumerable GetFeaturePatches() + { + yield return new FeaturePatch(PatchCompatibility.HookOnly); + } + + /// + /// Loads locale json files from the loaded assemblies. + /// + protected override async Task Handle(LocaleLoadingEvent eventData) + { + // We don't need to load the ResoniteModLoader locale here, as that will be handled by the ML Resonite Integration + // However the files are also included as embedded resources for completeness and compatibility with the reference + + foreach (var mod in LocalizedResoniteMods) + { + var type = mod.GetType(); + var assembly = type.Assembly; + var localeResourceName = $"{type.Namespace}.Locale.{eventData.LocaleCode}.json"; + + var realName = assembly.GetManifestResourceNames() + .FirstOrDefault(resourceName => localeResourceName.Equals(resourceName, StringComparison.OrdinalIgnoreCase)); + + if (realName is null) + continue; + + try + { + if (assembly.GetManifestResourceStream(realName) is not System.IO.Stream resourceStream) + continue; + + var localeData = await JsonSerializer.DeserializeAsync(resourceStream); + + if (localeData is null) + continue; + + if (!eventData.LocaleCode.Equals(localeData.LocaleCode, StringComparison.OrdinalIgnoreCase)) + Logger.Warn(() => $"Detected locale data with wrong locale code from locale resource! Wanted [{eventData.LocaleCode}] - got [{localeData.LocaleCode}] in file: {mod.Mod.Id}:{realName}"); + + eventData.LocaleResource.LoadDataAdditively(localeData); + } + catch (Exception ex) + { + Logger.Error(() => ex.Format($"Failed to deserialize resource as LocaleData: {mod.Mod.Id}:{realName}")); + } + } + } + + /// + protected override bool OnEngineInit() + { + LoadProgressReporter.AddFixedPhases(4); + + return base.OnEngineInit(); + } + + /// + protected override bool OnEngineReady() => true; + + /// + protected override bool OnLoaded() => base.OnEngineReady(); + + private static string GenerateSHA256(string filepath) + { + try + { + using var hasher = SHA256.Create(); + using var stream = File.OpenRead(filepath); + + return Convert.ToHexString(hasher.ComputeHash(stream)); + } + catch + { + return "Failed to generate hash"; + } + } + + private static IEnumerable GetAssemblyPaths(string root) + { + if (!Directory.Exists(root)) + yield break; + + foreach (var file in Directory.EnumerateFiles(root, "*.dll")) + { + if (Path.GetExtension(file).Equals(".dll", StringComparison.OrdinalIgnoreCase)) + yield return file; + } + } + + [HarmonyPostfix] + private static async Task InitializeFrooxEnginePostfixAsync(Task __result) + { + await __result; + + LoadProgressReporter.AdvanceFixedPhase("Loading RML Libraries..."); + + try + { + foreach (var file in GetAssemblyPaths("rml_libs")) + { + LoadProgressReporter.SetSubphase($"{Environment.NewLine}  {Path.GetFileNameWithoutExtension(file)}"); + + try + { + var assembly = Mod.Loader.AssemblyLoadStrategy.LoadFile(Path.GetFullPath(file)); + var name = assembly.GetName(); + + Mod.Loader.NuGet.Add(new LoadedNuGetPackage(new PackageIdentity(name.Name, new NuGetVersion(name.Version!)), NuGetHelper.Framework)); + + Logger.Info(() => $"Loaded library {name.Name}.{name.Version} from rml_libs: {file}"); + } + catch (Exception ex) + { + Logger.Warn(() => ex.Format($"Failed to load library from rml_libs: {file}")); + } + } + + LoadProgressReporter.AdvanceFixedPhase("Collecting RML Mods..."); + + var rmlMods = await LoadModsAsync().ToArrayAsync(); + + LoadProgressReporter.AdvanceFixedPhase("Running RML Mods..."); + await Task.Run(() => Mod.Loader.RunMods(rmlMods)); + LoadProgressReporter.AdvanceFixedPhase("Done with RML"); + } + catch (Exception ex) + { + Logger.Error(() => ex.Format("Exception in execution hook!")); + LoadProgressReporter.AdvanceFixedPhase("Error running RML Mods!"); + } + } + + private static async IAsyncEnumerable LoadModsAsync() + { + var modAssemblies = new List<(Assembly, string)>(); + + foreach (var file in GetAssemblyPaths("rml_mods")) + { + var hash = GenerateSHA256(file); + + try + { + var modAssembly = await Task.Run(() => Mod.Loader.AssemblyLoadStrategy.LoadFile(Path.GetFullPath(file!))); + modAssemblies.Add((modAssembly, hash)); + } + catch (Exception ex) + { + Logger.Warn(() => ex.Format($"Failed to load assembly from rml_mods: {file} with SHA256: {hash}")); + } + } + + foreach (var (modAssembly, hash) in modAssemblies) + { + var fileName = Path.GetFileName(modAssembly.Location); + LoadProgressReporter.SetSubphase($"{Environment.NewLine}  {modAssembly.GetName().Name!}"); + + RmlMod? rmlMod = null; + var success = true; + + try + { + rmlMod = new RmlMod(Mod.Loader, modAssembly); + Logger.Info(() => $"Loaded mod [{rmlMod.Id}/{rmlMod.Version}] ({fileName}) by {rmlMod.Authors.Join()} with SHA256: {hash}"); + + Mod.Loader.AddMod(rmlMod); + } + catch (Exception ex) + { + success = false; + Logger.Warn(() => ex.Format($"Failed to load mod from rml_mods: {fileName}")); + } + + if (success) + yield return rmlMod!; + } + } + } +} \ No newline at end of file diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/MonkeyLoader.GamePacks.ResoniteModLoader.csproj b/MonkeyLoader.GamePacks.ResoniteModLoader/MonkeyLoader.GamePacks.ResoniteModLoader.csproj index 389da76..a4a5ff3 100644 --- a/MonkeyLoader.GamePacks.ResoniteModLoader/MonkeyLoader.GamePacks.ResoniteModLoader.csproj +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/MonkeyLoader.GamePacks.ResoniteModLoader.csproj @@ -15,7 +15,7 @@ Banane9, Nytra - 4.2.0.1 + 5.0.1 This MonkeyLoader Game Pack for Resonite enables loading ResoniteModLoader mods as MonkeyLoader mods. README.md @@ -29,18 +29,18 @@ + + - - + - - + $(PkgMonkeyLoader_GamePacks_Resonite)\lib\net10.0\pre-patchers\MonkeyLoader.Resonite.Data.dll diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/ResoniteMod.cs b/MonkeyLoader.GamePacks.ResoniteModLoader/ResoniteMod.cs index 2deb69c..e67e889 100644 --- a/MonkeyLoader.GamePacks.ResoniteModLoader/ResoniteMod.cs +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/ResoniteMod.cs @@ -154,7 +154,7 @@ internal static Logger GetLoggerFromStackTrace(StackTrace stackTrace) } } - return ModLoader.Logger; + return ModLoaderHook.Logger; } /// diff --git a/MonkeyLoader.GamePacks.ResoniteModLoader/RmlMod.cs b/MonkeyLoader.GamePacks.ResoniteModLoader/RmlMod.cs index b200b9b..851edf2 100644 --- a/MonkeyLoader.GamePacks.ResoniteModLoader/RmlMod.cs +++ b/MonkeyLoader.GamePacks.ResoniteModLoader/RmlMod.cs @@ -19,9 +19,8 @@ internal sealed class RmlMod : Mod internal static readonly Dictionary AssemblyLookupMap = []; private static readonly Type _resoniteModType = typeof(ResoniteMod); - private static readonly Uri _rmlIconUrl = new("https://avatars.githubusercontent.com/u/145755526"); - private readonly Assembly _assembly; + private static readonly Uri _rmlIconUrl = new("https://avatars.githubusercontent.com/u/145755526"); /// public override string Description => "RML Mods don't have descriptions."; @@ -50,13 +49,24 @@ internal sealed class RmlMod : Mod /// public override NuGetFramework TargetFramework => NuGetHelper.Framework; + /// + /// Gets the this mod is defined in. + /// + internal Assembly Assembly { get; } + + /// + /// Gets whether this mod's contains locale + /// resources. + /// + internal bool IsLocalized { get; } + /// public RmlMod(MonkeyLoader.MonkeyLoader loader, Assembly assembly) : base(loader, assembly.Location, false) { FileSystem = new MemoryFileSystem() { Name = $"Dummy FileSystem for {assembly.GetName().Name}" }; - _assembly = assembly; + Assembly = assembly; var modType = assembly.GetTypes().Single(_resoniteModType.IsAssignableFrom); var resoniteMod = (ResoniteMod)Activator.CreateInstance(modType)!; @@ -81,18 +91,23 @@ public RmlMod(MonkeyLoader.MonkeyLoader loader, Assembly assembly) // Add dependencies after refactoring MKL //foreach (var referencedAssembly in assembly.GetReferencedAssemblies()) // dependencies.Add(referencedAssembly.Name, new DependencyReference()) + + var localePrefix = $"{modType.Namespace}.Locale."; + + IsLocalized = assembly.GetManifestResourceNames() + .Any(name => name.StartsWith(localePrefix, StringComparison.OrdinalIgnoreCase) && name.EndsWith(".json", StringComparison.OrdinalIgnoreCase)); } public override bool TryResolveAssembly(AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? assembly) { - if (assemblyName.Name != _assembly.GetName().FullName) + if (assemblyName.Name != Assembly.GetName().FullName) { assembly = null; return false; } - Logger.Debug(() => $"Resolving assembly {assemblyName.Name} to {_assembly.FullName} through RmlMod"); - assembly = _assembly; + Logger.Debug(() => $"Resolving assembly {assemblyName.Name} to {Assembly.FullName} through RmlMod"); + assembly = Assembly; return true; }