-
Notifications
You must be signed in to change notification settings - Fork 3
Solve the dependency resolving between mods #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
520649b
0e9de15
78d611e
09dfcae
a40f9be
f27d18a
e3ab314
dca54b6
ccdffe7
231f7a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,56 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| using System.Reflection; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| namespace HatModLoader.Source.AssemblyResolving | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| internal static class AssemblyResolveCompability | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
| internal static class AssemblyResolveCompability | |
| internal static class AssemblyResolveCompatibility |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name 'checkedVersion' is inconsistent with the naming convention. Consider using 'providedVersion' or 'availableVersion' to better describe what is being checked against the required version.
| private static bool CompareVersions(Version checkedVersion, Version requiredVersion, bool allowRollForward) | |
| { | |
| if (allowRollForward) | |
| { | |
| return checkedVersion >= requiredVersion; | |
| } | |
| return checkedVersion.Major == requiredVersion.Major && | |
| checkedVersion.Minor == requiredVersion.Minor && | |
| checkedVersion.Build == requiredVersion.Build && | |
| checkedVersion.Revision >= requiredVersion.Revision; | |
| private static bool CompareVersions(Version providedVersion, Version requiredVersion, bool allowRollForward) | |
| { | |
| if (allowRollForward) | |
| { | |
| return providedVersion >= requiredVersion; | |
| } | |
| return providedVersion.Major == requiredVersion.Major && | |
| providedVersion.Minor == requiredVersion.Minor && | |
| providedVersion.Build == requiredVersion.Build && | |
| providedVersion.Revision >= requiredVersion.Revision; |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When allowRollForward is false, the version comparison logic requires exact match on Major, Minor, and Build, but allows the Revision to be greater or equal. This means version 1.0.0.2 would be considered compatible with a request for 1.0.0.1, but 1.0.1.0 would not be compatible with a request for 1.0.0.1. This may be intentional behavior for mod compatibility, but consider documenting this specific versioning policy to clarify the expectations.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| namespace HatModLoader.Source.AssemblyResolving | ||
| { | ||
| internal static class AssemblyResolverRegistry | ||
| { | ||
| private static readonly HashSet<IAssemblyResolver> RegisteredResolvers = new(); | ||
|
|
||
| public static void Register(IAssemblyResolver resolver) | ||
| { | ||
| if (RegisteredResolvers.Contains(resolver)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| RegisteredResolvers.Add(resolver); | ||
| AppDomain.CurrentDomain.AssemblyResolve += resolver.ProvideAssembly; | ||
| } | ||
|
|
||
| public static void Unregister(IAssemblyResolver resolver) | ||
| { | ||
| if (!RegisteredResolvers.Contains(resolver)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| RegisteredResolvers.Remove(resolver); | ||
| AppDomain.CurrentDomain.AssemblyResolve -= resolver.ProvideAssembly; | ||
| } | ||
| } | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,69 @@ | ||||||
| using System.Reflection; | ||||||
|
|
||||||
| namespace HatModLoader.Source.AssemblyResolving | ||||||
| { | ||||||
| internal class HatSubdirectoryAssemblyResolver : IAssemblyResolver | ||||||
| { | ||||||
| private static readonly string DependencyDirectory = "HATDependencies"; | ||||||
|
|
||||||
| private readonly string _subdirectoryName; | ||||||
|
|
||||||
| public HatSubdirectoryAssemblyResolver(string subdirectoryName) | ||||||
| { | ||||||
| this._subdirectoryName = subdirectoryName; | ||||||
|
||||||
| this._subdirectoryName = subdirectoryName; | |
| _subdirectoryName = subdirectoryName; |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method returns null when an assembly cannot be provided. According to the AssemblyResolve event documentation, returning null is correct, but the method signature could be clearer if it had a nullable return type annotation (Assembly?). Since nullable is now disabled in the project, this is less critical but still worth noting.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| using System.Reflection; | ||
|
|
||
| namespace HatModLoader.Source.AssemblyResolving | ||
| { | ||
| internal interface IAssemblyResolver | ||
| { | ||
| public Assembly ProvideAssembly(object sender, ResolveEventArgs args); | ||
| } | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,68 @@ | ||||||
| using System.Reflection; | ||||||
| using HatModLoader.Source.ModDefinition; | ||||||
| using Mono.Cecil; | ||||||
|
|
||||||
| namespace HatModLoader.Source.AssemblyResolving | ||||||
| { | ||||||
| internal class ModInternalAssemblyResolver : IAssemblyResolver | ||||||
| { | ||||||
| private readonly ModIdentity _mod; | ||||||
|
|
||||||
| private readonly Dictionary<AssemblyName, string> _cachedAssemblyPaths = new(); | ||||||
|
|
||||||
| public ModInternalAssemblyResolver(ModIdentity mod) | ||||||
| { | ||||||
| _mod = mod; | ||||||
| CacheAssemblyPaths(); | ||||||
| } | ||||||
|
|
||||||
| public Assembly ProvideAssembly(object sender, ResolveEventArgs args) | ||||||
| { | ||||||
| if (_mod.CodeMod != null && _mod.CodeMod.Assembly.GetName().MatchesRequest(args, false)) | ||||||
| { | ||||||
| return _mod.CodeMod.Assembly; | ||||||
| } | ||||||
|
|
||||||
| foreach(var assemblyName in _cachedAssemblyPaths.Keys) | ||||||
| { | ||||||
| if (assemblyName.MatchesRequest(args, false)) | ||||||
| { | ||||||
| using var assemblyData = _mod.FileProxy.OpenFile(_cachedAssemblyPaths[assemblyName]); | ||||||
| var assemblyBytes = new byte[assemblyData.Length]; | ||||||
| assemblyData.Read(assemblyBytes, 0, assemblyBytes.Length); | ||||||
| return Assembly.Load(assemblyBytes); | ||||||
| } | ||||||
| } | ||||||
|
Comment on lines
+26
to
+35
|
||||||
| return null; | ||||||
|
||||||
| } | ||||||
|
|
||||||
| private void CacheAssemblyPaths() | ||||||
| { | ||||||
| foreach (var filePath in EnumerateAssemblyFilesInMod()) | ||||||
| { | ||||||
| using var assemblyFile = _mod.FileProxy.OpenFile(filePath); | ||||||
| using var assemblyDef = AssemblyDefinition.ReadAssembly(assemblyFile, new ReaderParameters { ReadSymbols = false }); | ||||||
| var fullName = new AssemblyName(assemblyDef.Name.ToString()); | ||||||
|
||||||
| var fullName = new AssemblyName(assemblyDef.Name.ToString()); | |
| var fullName = new AssemblyName(assemblyDef.Name.FullName); |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disabling nullable reference types removes compile-time null safety checks that were previously enabled. This makes the codebase more prone to NullReferenceException errors. Consider whether this change is necessary, or if specific nullable warnings could be suppressed instead where needed. If the change is intentional, ensure that null checks are added manually where nullable was previously providing protection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change is preferable due to dependency issues that arise when nullable types are used. Unless there's a direct reason for these issues to occur that can be resolved in other way, this is a quick fix.