QuiltMC / quilt-loader

The loader for Quilt mods.
Apache License 2.0
469 stars 86 forks source link

The client and server mods loading order inconsistent #387

Open SAGESSE-CN opened 9 months ago

SAGESSE-CN commented 9 months ago

Exception

I found that the loading order of the Quilt loader on the server and client is different.

I know has some client mods do not exist on the server, but after excluding this mods, they are still in inconsistent order.

The client mods order: full client logs Index Mod ID Version Type File Hash (SHA-1) File(s) Sub-File
22 Terraform Wood API (v1) terraform-wood-api-v1 4.2.0 Fabric 5b81eda6459643db8298e28dbc29cf28dced3894 \blockus-2.5.11+1.19.2.jar /META-INF/jars/terraform-wood-api-v1-4.2.0.jar
198 Armourer's Workshop armourers_workshop 2.0.11-dev.192 Fabric 05c872cf84b08063568947032daa648c09378012 \armourersworkshop-fabric-1.19.2-2.0.11-dev.192.jar
277 FrozenLib frozenlib 1.2.6-Fabric+1.19.2 Fabric 54991dce2a303ca2ad47affa9e4eebf951b34ecc \FrozenLib-1.2.6-Fabric+1.19.2.jar
226 Fzzy Core fzzy_core 0.2.7+1.19 Fabric 0d74ec51d8da7b8f002fedea30d34a416c8bcf8f \fzzy_core-0.2.7+1.19.jar
The server mods order: full server logs Index Mod ID Version Type File Hash (SHA-1) File(s) Sub-File
23 Terraform Wood API (v1) terraform-wood-api-v1 4.2.0 Fabric 5b81eda6459643db8298e28dbc29cf28dced3894 /bewitchment-1.19-7.jar /META-INF/jars/terraform-wood-api-v1-4.2.0.jar
133 FrozenLib frozenlib 1.2.6-Fabric+1.19.2 Fabric 71f835f32763869c10c0f45b0e88ea8b54ec00c2 /FrozenLib-1.2.6-Fabric+1.19.2.jar
227 Fzzy Core fzzy_core 0.2.7+1.19 Fabric 0d74ec51d8da7b8f002fedea30d34a416c8bcf8f /fzzy_core-0.2.7+1.19.jar
278 Armourer's Workshop armourers_workshop 2.0.11-dev.192 Fabric 05c872cf84b08063568947032daa648c09378012 /armourersworkshop-fabric-1.19.2-2.0.11-dev.192.jar

Why loading order is important?

The entity serializer relies heavily on loading order, when the loading order changes, the entity serializer id will be completely different.

The client entity serializers: Entity Serializer ID Registered Class Mod ID
23 com.terraformersmc.terraform.boat.impl.TerraformBoatTrackedData Terraform Wood API (v1)
24 moe.plushie.armourers_workshop.init.platform.fabric.proxy Armourer's Workshop
25 net.frozenblock.wilderwild.entity.variant.FireflyColor FrozenLib
26 net.frozenblock.wilderwild.entity.variant.JellyfishVariant FrozenLib
27 me.fzzyhmstrs.amethyst_imbuement.entity.living.HamsterVariant Fzzy Core
The Server entity serializers: Entity Serializer ID Registered Class Mod ID
23 com.terraformersmc.terraform.boat.impl.TerraformBoatTrackedData Terraform Wood API (v1)
24 net.frozenblock.wilderwild.entity.variant.FireflyColor FrozenLib
25 net.frozenblock.wilderwild.entity.variant.JellyfishVariant FrozenLib
26 me.fzzyhmstrs.amethyst_imbuement.entity.living.HamsterVariant Fzzy Core
27 moe.plushie.armourers_workshop.init.platform.fabric.proxy Armourer's Workshop

The entity serializer register entry:

net.fabricmc.api.ModInitializer.onInitialize
org.quiltmc.loader.impl.game.minecraft.Hooks.lambda$startClient$0(Hooks.java:55)
org.quiltmc.loader.impl.entrypoint.EntrypointUtils.lambda$invoke$0(EntrypointUtils.java:36)
org.quiltmc.loader.impl.entrypoint.EntrypointUtils.invoke0(EntrypointUtils.java:62)
org.quiltmc.loader.impl.entrypoint.EntrypointUtils.invokeContainer(EntrypointUtils.java:49)
org.quiltmc.loader.impl.entrypoint.EntrypointUtils.invoke(EntrypointUtils.java:36)
org.quiltmc.loader.impl.game.minecraft.Hooks.startClient(Hooks.java:55)
net.minecraft.class_310.<init>(class_310.java:459)
net.minecraft.client.main.Main.method_44604(Main.java:205)
net.minecraft.client.main.Main.main(Main.java:51)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:568)
org.quiltmc.loader.impl.game.minecraft.MinecraftGameProvider.launch(MinecraftGameProvider.java:551)
org.quiltmc.loader.impl.launch.knot.Knot.launch(Knot.java:84)
org.quiltmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:28)
org.prismlauncher.launcher.impl.StandardLauncher.launch(StandardLauncher.java:87)
org.prismlauncher.EntryPoint.listen(EntryPoint.java:130)
org.prismlauncher.EntryPoint.main(EntryPoint.java:70)
SAGESSE-CN commented 2 months ago

have any plan?

AlexIIL commented 2 months ago

Realistically this means doing one of the following:

Basically this needs us to add different load order algorithms.

(In theory QSL could go in and sort entity serializer IDs, but that assumes you even use QSL)

OroArmor commented 2 months ago

QSL does have an api for syncing these, but it does require the mods to use the api. There is effectively no way to sync the handlers without additional information, and there should be an warning printed to the log when this happens.

SAGESSE-CN commented 2 months ago

The entity serializer IDs is just one of problems, and any code that uses mixin may face this problem.

I think the problem is here. https://github.com/QuiltMC/quilt-loader/blob/a358422d8b72956d5aeacad1d0444ccd769e17e0/src/main/java/org/quiltmc/loader/impl/QuiltLoaderImpl.java#L342-L343 When the size of the mod list is inconsistent, the seed will be different.

TropheusJ commented 2 months ago

Mod load order is undefined on fabric as well (and intentionally shuffled in dev), and that should not change.

the solution is an API that avoids order-dependent problems, which quilt has, and fabric doesn’t. Fabric needs to get one (it’s one of the oldest issues on the repo), but in the meantime, mods need to avoid using the dangerous vanillla method.

I have a mod that finds this dangerous behavior as well as a few others. I’ve reported most of the ones you listed already.

SAGESSE-CN commented 2 months ago

The shuffled(although I don't know why need this) list not is a problem, the big problem is server side and client have a different load behaviors.

If QSL maintains the same loading behavior on both side (even use filename order, like forge), the mod developers will found issues and fix issue (use some QSL API). But now that because the QSL loading behavior completely unpredictable, you can't expect a developer to fix an quantum problem (maybe exist, maybe not exist).

The my problem is one of loading problems, even though I solved the problem by using the QSL API (still has register order problem, I don't know your how to solve it), the load problem still exists. I think the loader job is ensure loading same order, but seem your think it's the mod developer job.

SAGESSE-CN commented 2 months ago

A minimal changes suggestion, splite two mod list by (client only, server), use the same seed for shuffle.

var clientModLists = modList.stream().filter(it -> is client only)...
var serverModLists = modList.stream().filter(it -> has server)...
int seed = serverModLists.stream().map(it -> it.id).toList().hasCode(); 
Collections.shuffle(serverModLists, new Random(seed)); 
Collections.shuffle(clientModLists, new Random(seed)); 
modList = serverModLists + clientModLists