Closed Lisias closed 1 year ago
The Loader can't have its own versioning, otherwise it will break clients using KSPAssmblyDependency
. The requirements were updated.
This is not going to fly.
In C++, we can write a Module Initialiser function easily, but on C# this is extremely convoluted unless you are using C# 9.
It doesn't worth the hassle - at least, for now.
More info: https://stackoverflow.com/questions/505237/net-running-code-when-assembly-is-loaded
The work done for this issue is preserved on the branch https://github.com/net-lisias-ksp/KSPe/tree/experimental/issue_50 for historical reference.
this tasked ended up being a duplicate from #41 (I had forgot about that one!)
I had drawn myself to a corner. I will need to tackle this down somehow, or I will get screwed when updating CurseForge.
Thanks the problem:
Besides the idiosyncrasies on how KSP load things (and that got worse on KSP >= 1.8), I let a important detail pass trough: the KSPAssemblyVersion.
KSPe.UI
, KSPe.HMI
, etc, all of them rely on the KSPe
KSPAssembly directive to control dependency, not to mention the client add'ons that rely on it too.
On a CurseForge update, these Assemblies are bumped up but the 000_KSPe.dll
is still one version behind, and so they are not loaded, exploding Exceptions everywhere and preventing KSPe.InstallChecker
from doing it's job (what's ironic, as I had managed to solve the KSP's problems on it by now - #sigh).
So, now, no matter the trouble I'm going to have, I need to have this issue tackled down as it appears to be my only chance of doing a clean job.
This is going to hurt….
Part of the problem (running code when the DLL is loaded) is solved by using:
I'm not a huge fan of external dependencies (not to mention a brittle system as NuGet, creating a hard dependency on external repositories that can go dark at any moment), but at least on the short run this is working. I can reevaluate the dependencies later.
This is not going to fly the way I initially intended.
I can load an Assembly without problems on the ModuleInitializer
using System.Reflection.Assembly.Load(bytes[])
. What I can't do is loading a KSP Plugin (AssemblyLoader.LoadPlugin
) on the damned thing without a huge breakage on everybody later:
[EXC 19:16:28.043] NullReferenceException: Object reference not set to an instance of an object
KSPAPIExtensions.SystemUtils+<>c__DisplayClass1_0.<TypeElectionWinner>b__0 (.LoadedAssembly ass)
System.Linq.Enumerable+<CreateWhereIterator>c__Iterator1D`1[AssemblyLoader+LoadedAssembly].MoveNext ()
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[AssemblyLoader+LoadedAssembly,<>f__AnonymousType0`2[AssemblyLoader+LoadedAssembly,System.Type]].MoveNext ()
System.Linq.Enumerable+<CreateWhereIterator>c__Iterator1D`1[<>f__AnonymousType0`2[AssemblyLoader+LoadedAssembly,System.Type]].MoveNext ()
System.Collections.Generic.List`1[<>f__AnonymousType0`2[AssemblyLoader+LoadedAssembly,System.Type]].AddEnumerable (IEnumerable`1 enumerable)
System.Collections.Generic.List`1[<>f__AnonymousType0`2[AssemblyLoader+LoadedAssembly,System.Type]]..ctor (IEnumerable`1 collection)
System.Linq.Enumerable.ToArray[<>f__AnonymousType0`2] (IEnumerable`1 source)
System.Linq.QuickSort`1[<>f__AnonymousType0`2[AssemblyLoader+LoadedAssembly,System.Type]]..ctor (IEnumerable`1 source, System.Linq.SortContext`1 context)
System.Linq.QuickSort`1+<Sort>c__Iterator21[<>f__AnonymousType0`2[AssemblyLoader+LoadedAssembly,System.Type]].MoveNext ()
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[<>f__AnonymousType0`2[AssemblyLoader+LoadedAssembly,System.Type],System.Type].MoveNext ()
System.Collections.Generic.List`1[System.Type].AddEnumerable (IEnumerable`1 enumerable)
System.Collections.Generic.List`1[System.Type]..ctor (IEnumerable`1 collection)
System.Linq.Enumerable.ToArray[Type] (IEnumerable`1 source)
KSPAPIExtensions.SystemUtils.TypeElectionWinner (System.Type targetCls, System.String assemName)
KSPAPIExtensions.OnEditorUpdateUtility..cctor ()
Rethrow as TypeInitializationException: An exception was thrown by the type initializer for KSPAPIExtensions.OnEditorUpdateUtility
KSPAPIExtensions.OnEditorUpdateUtility_1_7_5_108..ctor ()
UnityEngine.GameObject:AddComponent(Type)
AddonLoader:StartAddon(LoadedAssembly, Type, KSPAddon, Startup)
AddonLoader:StartAddons(Startup)
<LoadObjects>c__Iterator1:MoveNext()
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
<CreateDatabase>c__Iterator0:MoveNext()
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
GameDatabase:StartLoad()
<LoadSystems>c__Iterator0:MoveNext()
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
LoadingScreen:Start()
What kinda make sense, after all, as I was trying to load a plugin while a plugin was being loaded, I surelly broke something internal on the AssemblyLoader.LoadPlugin
.
So, nope. I'm not going to dynamically loading KSPe. KSPe.Loader
is dead, another approach is needed.
The experimental/issue_50
branch was updated with the most recent code.
Well… It ended not being exactly what I wanted, but it works (almost) the same. The ModuleInitializer
stunt guarantee that this code is run before anything else due a happy coincidence on how KSP load and start up things (TL;DR: I'm cheating the Assembly Loader/Resolver
by initialising the InstallChecker
before it initialises this little world).
This way, I managed to display a Modal asking the user to restart KSP before anything else could do it (including KSPe
itself).
Foreseeing that perhaps this stunt could need to be reversed by any reason, I'm also moving the KSPe.InstallChecker
to be loaded before KSPe
itself on GameData, so even by removing the ModuleInitializer
stunt, things still will work as intended in the future releases.
An emergencial release based on the 2.5.2.0 (2.5.2.1 and 2.5.2.2 were scrapped) will be issued, reducing to the minimum any changes on KSPe
to allow easier diagnosing if anything goes South on the damned thing.
Implemented on commit https://github.com/net-lisias-ksp/KSPe/commit/50cf7f31ecf495a4c0405dad326bfb1d43ba90ac .
Worked as intended on
So I'm assuming it will work fine on everything else (as long is supported).
The
KPSe.InstallChecker
stunt is nice, but it have a flaw:When the user updates KSP from CurseForge or GitHub, the first boot will have two
KSPe.dll
on memory - and so, the KSPe's initialisation was happening twice.This is less then ideal, but not exactly (too much) bad because the Install Checker will get rid of one of them after updating and then will ask the user for rebooting KSP. However, I left one use case unhandled: when the user updates https://github.com/net-lisias-ksp/ModularManagement but KSPe itself wasn't upgraded in the process, and so the Install Checker just removes the
KSPe.dll
insideGameData/000_KSPe
and call it a day.And so we have two KSPes in memory, being initialised twice, what means it will try to load the auxiliary DLLs twice. An annoyance on KSP >= 1.8.0, but a problem on KSP < 1.8.0 as it will load KSPe (and the auxiliary DLLs) on a new AppDomain slowing up everything until the next KSP boot.
Things got a bit worse with the implementation of the Task #49 , however. Now the second KSPe that was silently being reloading the auxiliary DLLs are being yelled at, and so I broke the update process.
Task #49 is important, it will help me (and eventual clientes) to avoid messing up the loading process, as well be yet a new barrier against a really nasty installation problem where multiple copies if the Add'On is installed by accident on the GameData.
So the best way out of the mess is to implement a Loader for what is, now,
KSPe.dll
(or000_KSPe.dll
) ~and move it toPluginData
, becoming an auxiliary DLL itself.~ This new Loader MUST:KSPe
, and and the older one, now a new Auxiliary, will beKSPe.Main
.~KSPe
will work fine - no cascading changes on the eco system.~This will give us the following benefits:
~It will almost eliminate the need to reboot KSP when
KSPe.Main
is updated.~~It will prevent the now
KSPe.Main
to be loaded twice at all circumstances.~~It will cause no impact on the current toolchain~
EDIT: Requirements removed due a strategy change.
Solution implemented:
KSPe.InstallChecker
was moved intoGameData/000_KSPe.dll
KSPe
will not be the first thing loaded and will lose control of the whole installation).KSPe
itself is now onGameData/001_KSPe.dll