diff --git a/CHANGES.md b/CHANGES.md index c9fc2d1..beee49c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,7 @@ # Release notes All notable changes should be documented in this file. -Since v2, ModTek adheres to [Semantic Versioning](http://semver.org/). +Since v2, ModTek adheres to [Semantic Versioning](http://semver.org/) for runtime compatibility with mods. ## Known Issues @@ -10,6 +10,12 @@ Since v2, ModTek adheres to [Semantic Versioning](http://semver.org/). however injected assemblies are now found under `Mods/.modtek/AssembliesInjected` or loaded directly into memory after injection. - The HarmonyX feature works well, however some mods might rely on some buggy Harmony 1.2 behaviors that HarmonyX shims don't replicate. +## Upcoming - CptMoore + +For modders: +- (Experimental!) Added ability to run injectors as part of a build task outside of BT. API will most likely change. +- Some libraries were renamed, as always don't just copy-paste, clean-copy-paste! + ## 4.2 - CptMoore For users: @@ -22,7 +28,7 @@ For modders: - Made run.sh depend on the doorstop.ini instead of having its own inline options - Updated HarmonyX - New HarmonyX is based on a major rewrite of MonoMod, several bugs were encountered and fixed - - Still providing an older version of HarmonyX in-case the new HarmonyX feels unstable + - Still providing an older version of HarmonyX due to reports of instability (those went away after a restart or 2) - Various Logging improvements and changes - Async logging is now highly optimized - reduction of 300+ ns to <100 ns spent on the caller thread (usually the unity main thread) diff --git a/CommonBase.props b/CommonBase.props index 403f7f2..134f1ac 100644 --- a/CommonBase.props +++ b/CommonBase.props @@ -7,6 +7,7 @@ Library 13 + embedded diff --git a/CommonBattleTech.props b/CommonBattleTech.props index d77df7b..618ea48 100644 --- a/CommonBattleTech.props +++ b/CommonBattleTech.props @@ -9,7 +9,6 @@ $(BattleTechGameDir)\BattleTech_Data\Managed true - embedded diff --git a/Directory.Packages.props b/Directory.Packages.props index aab19cd..92732f0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,6 +3,7 @@ true + diff --git a/ModTek.Common/Globals/Paths.cs b/ModTek.Common/Globals/Paths.cs index 6387e9d..5d7d296 100644 --- a/ModTek.Common/Globals/Paths.cs +++ b/ModTek.Common/Globals/Paths.cs @@ -1,15 +1,16 @@ -using System; -using System.IO; +using System.IO; +using System.Reflection; namespace ModTek.Common.Globals; internal class Paths { // Common paths - private const string ENV_DOORSTOP_MANAGED_FOLDER_DIR = "DOORSTOP_MANAGED_FOLDER_DIR"; - internal static readonly string ManagedDirectory = Environment.GetEnvironmentVariable(ENV_DOORSTOP_MANAGED_FOLDER_DIR) - ?? throw new Exception($"Can't find {ENV_DOORSTOP_MANAGED_FOLDER_DIR}"); - internal static readonly string BaseDirectory = Path.GetFullPath(Path.Combine(ManagedDirectory, "..", "..")); + + // BATTLETECH/Mods/ModTek/lib/ModTek.Common.dll -> BATTLETECH + internal static readonly string BaseDirectory = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "..", "..", "..")); + // Mac has Data, but we have a symlink from BattleTech_Data to Data anyway + internal static readonly string ManagedDirectory = Path.Combine(BaseDirectory, "BattleTech_Data", "Managed"); internal static readonly string ModsDirectory = Path.Combine(BaseDirectory, "Mods"); internal static readonly string ModTekDirectory = Path.Combine(ModsDirectory, "ModTek"); diff --git a/ModTek.Common/ModTek.Common.csproj b/ModTek.Common/ModTek.Common.csproj index e3ca030..5f710d7 100644 --- a/ModTek.Common/ModTek.Common.csproj +++ b/ModTek.Common/ModTek.Common.csproj @@ -1,6 +1,13 @@ + + + + + + + true @@ -8,6 +15,6 @@ - + \ No newline at end of file diff --git a/ModTek.InjectorRunner/Injector/AssemblyCache.cs b/ModTek.Injectors/AssemblyCache.cs similarity index 99% rename from ModTek.InjectorRunner/Injector/AssemblyCache.cs rename to ModTek.Injectors/AssemblyCache.cs index eea022a..93d6d9a 100644 --- a/ModTek.InjectorRunner/Injector/AssemblyCache.cs +++ b/ModTek.Injectors/AssemblyCache.cs @@ -6,7 +6,7 @@ using ModTek.Common.Utils; using Mono.Cecil; -namespace ModTek.InjectorRunner.Injector; +namespace ModTek.Injectors; class AssemblyCache : IAssemblyResolver { diff --git a/ModTek.InjectorRunner/Injector/InjectionCacheManifest.cs b/ModTek.Injectors/InjectionCacheManifest.cs similarity index 98% rename from ModTek.InjectorRunner/Injector/InjectionCacheManifest.cs rename to ModTek.Injectors/InjectionCacheManifest.cs index cbe15ed..d907724 100644 --- a/ModTek.InjectorRunner/Injector/InjectionCacheManifest.cs +++ b/ModTek.Injectors/InjectionCacheManifest.cs @@ -6,7 +6,7 @@ using ModTek.Common.Globals; using ModTek.Common.Utils; -namespace ModTek.InjectorRunner.Injector; +namespace ModTek.Injectors; internal class InjectionCacheManifest { diff --git a/ModTek.InjectorRunner/Logger.cs b/ModTek.Injectors/Logger.cs similarity index 83% rename from ModTek.InjectorRunner/Logger.cs rename to ModTek.Injectors/Logger.cs index c03d20d..432810a 100644 --- a/ModTek.InjectorRunner/Logger.cs +++ b/ModTek.Injectors/Logger.cs @@ -1,7 +1,7 @@ using ModTek.Common.Globals; using ModTek.Common.Logging; -namespace ModTek.InjectorRunner; +namespace ModTek.Injectors; internal class Logger { diff --git a/ModTek.InjectorRunner/ModTek.InjectorRunner.csproj b/ModTek.Injectors/ModTek.Injectors.csproj similarity index 61% rename from ModTek.InjectorRunner/ModTek.InjectorRunner.csproj rename to ModTek.Injectors/ModTek.Injectors.csproj index 6da4c38..84d62e9 100644 --- a/ModTek.InjectorRunner/ModTek.InjectorRunner.csproj +++ b/ModTek.Injectors/ModTek.Injectors.csproj @@ -1,12 +1,20 @@  + + + + + + + + - + diff --git a/ModTek.InjectorRunner/Injector/InjectorsRunner.cs b/ModTek.Injectors/Runner.cs similarity index 90% rename from ModTek.InjectorRunner/Injector/InjectorsRunner.cs rename to ModTek.Injectors/Runner.cs index 1252a2b..643b32b 100644 --- a/ModTek.InjectorRunner/Injector/InjectorsRunner.cs +++ b/ModTek.Injectors/Runner.cs @@ -6,13 +6,13 @@ using ModTek.Common.Logging; using ModTek.Common.Utils; -namespace ModTek.InjectorRunner.Injector; +namespace ModTek.Injectors; -internal class InjectorsRunner : IDisposable +internal class Runner : IDisposable { internal static void Run() { - using var injectorsRunner = new InjectorsRunner(); + using var injectorsRunner = new Runner(); if (injectorsRunner.IsUpToDate) { return; @@ -22,9 +22,14 @@ internal static void Run() injectorsRunner.SaveToDisk(); } + internal static string[] GetInjectedPaths() + { + return Directory.GetFiles(Paths.AssembliesInjectedDirectory, "*.dll"); + } + private readonly AssemblyCache _assemblyCache; private readonly InjectionCacheManifest _injectionCacheManifest; - private InjectorsRunner() + private Runner() { _assemblyCache = new AssemblyCache(); _injectionCacheManifest = new InjectionCacheManifest(); diff --git a/ModTek.InjectorsTask/ITaskItemExtensions.cs b/ModTek.InjectorsTask/ITaskItemExtensions.cs new file mode 100644 index 0000000..13f2db0 --- /dev/null +++ b/ModTek.InjectorsTask/ITaskItemExtensions.cs @@ -0,0 +1,17 @@ +using Microsoft.Build.Framework; + +namespace ModTek.InjectorsTask; + +// from Krafs.Publicizer +internal static class TaskItemExtensions +{ + internal static string FileName(this ITaskItem item) + { + return item.GetMetadata("Filename"); + } + + internal static string FullPath(this ITaskItem item) + { + return item.GetMetadata("Fullpath"); + } +} \ No newline at end of file diff --git a/ModTek.InjectorsTask/ModTek.InjectorsTask.csproj b/ModTek.InjectorsTask/ModTek.InjectorsTask.csproj new file mode 100644 index 0000000..3449fcc --- /dev/null +++ b/ModTek.InjectorsTask/ModTek.InjectorsTask.csproj @@ -0,0 +1,26 @@ + + + + + enable + + + + + + + + + + + + + + + + + All + runtime + + + diff --git a/ModTek.InjectorsTask/ModTek.InjectorsTask.props b/ModTek.InjectorsTask/ModTek.InjectorsTask.props new file mode 100644 index 0000000..c0dedc8 --- /dev/null +++ b/ModTek.InjectorsTask/ModTek.InjectorsTask.props @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/ModTek.InjectorsTask/ModTek.InjectorsTask.targets b/ModTek.InjectorsTask/ModTek.InjectorsTask.targets new file mode 100644 index 0000000..75d1f13 --- /dev/null +++ b/ModTek.InjectorsTask/ModTek.InjectorsTask.targets @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/ModTek.InjectorsTask/ModTekInjectorsRunner.cs b/ModTek.InjectorsTask/ModTekInjectorsRunner.cs new file mode 100644 index 0000000..29c42b1 --- /dev/null +++ b/ModTek.InjectorsTask/ModTekInjectorsRunner.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using ModTek.Injectors; + +namespace ModTek.InjectorsTask; + +/* + * see https://learn.microsoft.com/en-us/visualstudio/msbuild/tutorial-custom-task-code-generation?view=vs-2022#generate-a-console-app-and-use-the-custom-task + * see https://github.com/krafs/Publicizer/blob/main/src/Publicizer/Krafs.Publicizer.targets and props + * - run before Rafs.Publicizer + * - needs to modify the references? or just push them ahead so they are found? + * - see Rafs ReferencePathsToDelete and ReferencePathsToAdd and ReferencePaths etc.... + */ +public class ModTekInjectorsRunner : Task +{ + [Required] + public string BattleTechGameDir { get; set; } = null!; + + [Required] + public ITaskItem[] ReferencePaths { get; set; } = null!; + + [Output] + public ITaskItem[] ReferencePathsToDelete { get; set; } = []; + + [Output] + public ITaskItem[] ReferencePathsToAdd { get; set; } = []; + + public override bool Execute() + { + var executingAssembly = Assembly.GetExecutingAssembly(); + var applicationDirectory = Path.GetDirectoryName(executingAssembly.Location)!; + + var currentDomain = AppDomain.CurrentDomain; + currentDomain.AssemblyResolve += (_, args) => + { + var resolvingName = new AssemblyName(args.Name); + var path = Path.Combine(applicationDirectory, resolvingName.Name + ".dll"); + Log.LogWarning($"Loading assembly {args.Name} from {path}"); + return Assembly.LoadFrom(path); + }; + + Runner.Run(); + + var referencePathsToAdd = new List(); + var referencePathsToDelete = new List(); + foreach (var path in Runner.GetInjectedPaths()) + { + var filename = Path.GetFileNameWithoutExtension(path); + foreach (var referencePath in ReferencePaths) + { + if (filename == referencePath.FileName()) + { + var newReference = new TaskItem(path); + referencePathsToAdd.Add(newReference); + referencePathsToDelete.Add(referencePath); + break; + } + } + } + ReferencePathsToDelete = referencePathsToDelete.ToArray(); + ReferencePathsToAdd = referencePathsToAdd.ToArray(); + return !Log.HasLoggedErrors; + } +} \ No newline at end of file diff --git a/ModTek.Preloader/Harmony12X/ShimCacheManifest.cs b/ModTek.Preloader/Harmony12X/ShimCacheManifest.cs index f0d3dd0..e82506e 100644 --- a/ModTek.Preloader/Harmony12X/ShimCacheManifest.cs +++ b/ModTek.Preloader/Harmony12X/ShimCacheManifest.cs @@ -5,7 +5,7 @@ using System.Linq; using ModTek.Common.Globals; using ModTek.Common.Utils; -using ModTek.InjectorRunner.Injector; +using ModTek.Injectors; using Mono.Cecil; namespace ModTek.Preloader.Harmony12X; diff --git a/ModTek.Preloader/InjectorsAppDomain.cs b/ModTek.Preloader/InjectorsAppDomain.cs index bb1b35c..5a9747f 100644 --- a/ModTek.Preloader/InjectorsAppDomain.cs +++ b/ModTek.Preloader/InjectorsAppDomain.cs @@ -1,6 +1,6 @@ using System; using ModTek.Common.Globals; -using ModTek.InjectorRunner.Injector; +using ModTek.Injectors; namespace ModTek.Preloader; @@ -36,6 +36,6 @@ internal static void Run() private void RunInjectors() { - InjectorsRunner.Run(); + Runner.Run(); } } \ No newline at end of file diff --git a/ModTek.Preloader/Loader/Preloader.cs b/ModTek.Preloader/Loader/Preloader.cs index 26b99a7..0e11f77 100644 --- a/ModTek.Preloader/Loader/Preloader.cs +++ b/ModTek.Preloader/Loader/Preloader.cs @@ -4,7 +4,7 @@ using System.Reflection; using ModTek.Common.Globals; using ModTek.Common.Utils; -using ModTek.InjectorRunner.Injector; +using ModTek.Injectors; using ModTek.Preloader.Harmony12X; namespace ModTek.Preloader.Loader; @@ -37,7 +37,7 @@ internal static void Run() private static void PreloadAssembliesInjected() { Logger.Main.Log($"Preloading injected assemblies from `{FileUtils.GetRelativePath(Paths.AssembliesInjectedDirectory)}`:"); - foreach (var file in Directory.GetFiles(Paths.AssembliesInjectedDirectory, "*.dll").OrderBy(p => p)) + foreach (var file in Injectors.Runner.GetInjectedPaths().OrderBy(p => p)) { Logger.Main.Log($"\t{Path.GetFileName(file)}"); Assembly.LoadFile(file); diff --git a/ModTek.Preloader/ModTek.Preloader.csproj b/ModTek.Preloader/ModTek.Preloader.csproj index ed0282f..8176614 100644 --- a/ModTek.Preloader/ModTek.Preloader.csproj +++ b/ModTek.Preloader/ModTek.Preloader.csproj @@ -3,10 +3,10 @@ - + - + @@ -34,7 +34,7 @@ - - + + \ No newline at end of file diff --git a/ModTek.sln b/ModTek.sln index 5f09843..d29bedd 100644 --- a/ModTek.sln +++ b/ModTek.sln @@ -30,7 +30,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModTemplate", "examples\Mod EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModTek.Common", "ModTek.Common\ModTek.Common.csproj", "{67588F2A-B438-407C-AD16-ED6F4C9D12FC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModTek.InjectorRunner", "ModTek.InjectorRunner\ModTek.InjectorRunner.csproj", "{B9F6AA09-D3CD-4FAF-ADF8-18BF78B1CCD1}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModTek.Injectors", "ModTek.Injectors\ModTek.Injectors.csproj", "{B9F6AA09-D3CD-4FAF-ADF8-18BF78B1CCD1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModTek.InjectorsTask", "ModTek.InjectorsTask\ModTek.InjectorsTask.csproj", "{D4306496-4941-47A6-9AEA-B37A3E7BD15D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -74,6 +76,10 @@ Global {B9F6AA09-D3CD-4FAF-ADF8-18BF78B1CCD1}.Debug|Any CPU.Build.0 = Debug|Any CPU {B9F6AA09-D3CD-4FAF-ADF8-18BF78B1CCD1}.Release|Any CPU.ActiveCfg = Release|Any CPU {B9F6AA09-D3CD-4FAF-ADF8-18BF78B1CCD1}.Release|Any CPU.Build.0 = Release|Any CPU + {D4306496-4941-47A6-9AEA-B37A3E7BD15D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4306496-4941-47A6-9AEA-B37A3E7BD15D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4306496-4941-47A6-9AEA-B37A3E7BD15D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4306496-4941-47A6-9AEA-B37A3E7BD15D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE