[Bug]: PuzzlesUtil.loadServiceProvider uses context classloader
embeddedt opened this issue ยท 3 comments
Mod Loader (Required)
Forge
Minecraft Version (Required)
1.19.2
Mod Version (Required)
4.4.0
Notes (Required)
If PuzzlesUtil.loadServiceProvider
is invoked from the common ForkJoinPool (e.g. as a result of another mod using this to perform some task), it will fail to find the service class. This is because the ForkJoinPool's context classloader is not set to be the Knot/FML class loader, which means it cannot correctly load any new vanilla/mod classes. It will only work reliably with classes that were already loaded. In the Prominence modpack, this resulted in a crash when the right combination of mods were in use to trigger this scenario. ๐
Here is the relevant portion of the stacktrace:
Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.NullPointerException: Failed to load service for fuzs.diagonalfences.client.core.ClientAbstractions [in thread "ForkJoinPool.commonPool-worker-2"]
at fuzs.puzzleslib.util.PuzzlesUtil.lambda$loadServiceProvider$0(PuzzlesUtil.java:135)
at java.base/java.util.Optional.orElseThrow(Optional.java:403)
at fuzs.puzzleslib.util.PuzzlesUtil.loadServiceProvider(PuzzlesUtil.java:135)
at fuzs.diagonalfences.client.core.ClientAbstractions.<clinit>(ClientAbstractions.java:12)
at fuzs.diagonalfences.client.model.MultipartAppender.rotateMultipartSegment(MultipartAppender.java:106)
at fuzs.diagonalfences.client.model.RotatedVariant.method_4753(RotatedVariant.java:31)
at net.minecraft.class_1088.handler$jee000$modernfix$getOrLoadBakedModelDynamic(class_1088.java:1047)
at net.minecraft.class_1088.method_15878(class_1088.java)
at net.minecraft.class_807.method_4753(class_807.java:77)
at net.minecraft.class_816.method_4753(class_816.java:90)
at net.minecraft.class_1088.handler$jee000$modernfix$getOrLoadBakedModelDynamic(class_1088.java:1047)
at net.minecraft.class_1088.method_15878(class_1088.java)
at org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider.get(DynamicBakedModelProvider.java:112)
at org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider.get(DynamicBakedModelProvider.java:25)
at java.base/java.util.Map.getOrDefault(Map.java:671)
at net.minecraft.class_1092.method_4742(class_1092.java:38)
at net.minecraft.class_773.method_3335(class_773.java:542)
at net.minecraft.class_776.method_3349(class_776.java:91)
at net.minecraft.class_4970$class_4971$class_3752.redirect$jkb000$moreculling$shouldDoShapeCache(class_4970.java:2211)
at net.minecraft.class_4970$class_4971$class_3752.<init>(class_4970.java:1106)
at net.minecraft.class_4970$class_4971.method_26200(class_4970.java:709)
at net.minecraft.class_4970$class_4971.generateCache(class_4970.java:7672)
at net.minecraft.class_4970$class_4971.handler$jgg000$modernfix$generateCacheLithium3(class_4970.java:7710)
at net.minecraft.class_4970$class_4971.getAllFlags(class_4970.java)
at net.minecraft.class_2826.addToFlagCount(class_2826.java:598)
at net.minecraft.class_2826.md918784$lithium$lambda$calculateLithiumCounts$1$1(class_2826.java:579)
at net.minecraft.class_2841.handler$ike000$lithium$count(class_2841.java:1146)
at net.minecraft.class_2841.method_21732(class_2841.java)
at net.minecraft.class_2826.calculateLithiumCounts(class_2826.java:579)
at net.minecraft.class_2826.md918784$lithium$lambda$tryInitializeCountsByFlag$0$2(class_2826.java:572)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
A simple solution to this problem is to provide an explicit classloader to use, e.g. change ServiceLoader.load(clazz)
to ServiceLoader.load(clazz, PuzzlesUtil.class.getClassLoader())
and ensure PuzzlesUtil
is referenced somewhere during the mod loading process so that it's already loaded by the time something calls loadServiceProvider
.
latest.log (Optional)
No response
Thanks for this! So service providers are basially used everyhwere throughout my code, when would be a good place to load the ServiceLoader
class then, like what's the earliest I could do? Maybe in a IMixinConfigPlugin
?