Repository navigation
Conversation
SaveableAutoRegistry.DiscoverSaveableTypes skipped assemblies by FullName prefix. On IL2CPP, Assembly-CSharp and Assembly-CSharp-firstpass match none of the prefixes, so the first save load of every session called Assembly.GetTypes() on both interop assemblies. That can end the process with an uncatchable ExecutionEngineException (0x80131506) inside BeforeBaseLoaders. Discovery now goes through ReflectionUtils.GetDerivedClasses<Saveable>(), which only enumerates assemblies that reference S1API and already skips Assembly-CSharp. A direct Saveable subclass has to reference S1API, so the result is the same set of types. GetNpcWrapperTypeById walked every loaded assembly with no filter at all; it now uses GetDerivedClasses<NPC>() for the same reason.
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 44 minutes. View limit details
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
|
Like I mentioned in #369, I can reach the unsafe scan locally but haven’t been able to trigger the fatal error. Do you have a small Polyfill setup that reproduces it? I’d rather leave this open until I can test the crash itself against the PR. I’m also a bit hesitant about switching both paths to |
Linked issue
Fixes #369
Contributor checklist
Summary
On IL2CPP,
SaveableAutoRegistry.DiscoverSaveableTypescalledAssembly.GetTypes()onAssembly-CSharpandAssembly-CSharp-firstpasson the first save load of every session. Its name-prefix filter does not match either interop assembly. That call can end the process with an uncatchable0x80131506insideGenericSaveablesPatches.BeforeBaseLoaders.Discovery now goes through
ReflectionUtils.GetDerivedClasses<Saveable>(), the bounded helper from #289. It only enumerates assemblies that reference S1API, and it already skipsAssembly-CSharp. The existing checks are unchanged: no S1API types, only direct subclasses ofSaveable, no abstract, interface or open generic types.ProductEffectPatches.GetNpcWrapperTypeByIdwalked every loaded assembly with no filter. It now usesGetDerivedClasses<NPC>()for the same reason.Compatibility
Saveablesubclass and an NPC wrapper have to reference S1API, so the reference filter drops no candidate. Discovery order now follows the scan plan's assembly order, which is the loaded-assembly order as before. Instances are still created once and cached.Validation
Built against 0.4.7f12 reference assemblies.
Mono
dotnet build S1API.sln -c MonoMelon --no-restore -p:AutomateLocalDeployment=false: 0 errorsdotnet test S1API.Tests/S1API.Tests.csproj -c MonoMelon --no-restore --no-build: 800 passedIL2CPP
dotnet build S1API.sln -c Il2CppMelon --no-restore -p:AutomateLocalDeployment=false: 0 errorsdotnet test S1API.Tests/S1API.Tests.csproj -c Il2CppMelon --no-restore --no-build: 774 passedNew
SaveableAutoRegistryTestsfreeze the discovery result: a direct concrete subclass is registered; an indirect subclass, an abstract subclass and S1API's own types are not. Repeated calls return the same instance.Runtime evidence
IL2CPP, game 0.4.7f12, MelonLoader 0.7.x, a co-op mod set of about 20 mods, host a lobby and load a save:
0x80131506inDiscoverSaveableTypesIn every 3.2.1 crash dump,
DiscoverSaveableTypesruns at tier 0, so its loop index is still on the stack. Mapped againstdumpdomain, the index points atIl2CppAssemblies\Assembly-CSharp.dllin all 9 dumps. With this branch, a mod'sSaveablestill loads its data in all 9 loads (OnLoadedlog line from the mod). The Mono runtime was not exercised in game; on Mono the change only narrows which assemblies are enumerated.Documentation
None. Both changed methods are private.
Summary by CodeRabbit