You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ChampionAsh5357 opened this issue
Jan 31, 2025
· 0 comments
· May be fixed by #1934
Labels
1.21Targeted at Minecraft 1.211.21.1Targeted at Minecraft 1.21.11.21.2Targeted at Minecraft 1.21.21.21.3Targeted at Minecraft 1.21.31.21.4Targeted at Minecraft 1.21.41.21.5Targeted at Minecraft 1.21.5bugA bug or error
Create a simple mod that registers something to one of the specified registries in NeoForgeRegistriesSetup#VANILLA_SYNC_REGISTRIES only on the physical client.
@Mod("examplemod")
publicclassExampleMod {
publicExampleMod(IEventBusmodBus, Distdist) {
if (dist == Dist.CLIENT) {
vartest = DeferredRegister.create(Registries.RECIPE_SERIALIZER, "examplemod");
test.register(modBus);
// Using a dummy as it doesn't matter since it's not used anywhere in the codebasetest.register("test", () -> newAbstractCookingRecipe.Serializer<>(CampfireCookingRecipe::new, 100));
}
}
}
Run a client and dedicated server (or an integrated server on a separate client).
Create a new singleplayer world and log in (doesn't matter what settings).
Log into the dedicated server from the client using the Multiplayer tab.
Disconnect through the pause screen.
Log into the singleplayer world again and notice the ArrayIndexOutOfBoundsException thrown during registry sync from the integrated server.
Description of issue:
This bug results from a stale reference to multiplayer synced registries when the physical client has an additional registry object registered.
When the RegistryManager takes a snapshot of some registry and applies it to the existing registered, there needs to be some method to sync the identifiers. However, we generally don't want to overwrite the entire registry itself, instead, only modifying the network used integer ids to match those on the server. To do so, the RegistryManager when constructing the registries to sync to the client, clears the ids to be repopulated via RegistryManager#applySnapshot via MappedRegistry#registerIdMapping.
Note that MappedRegistry#clear(false) (which is what's called when syncing registries) only clears the integer identifiers, not the resource keys. However, when the server has a subset of the client registry objects, that is fine. The constructed RegistrySnapshot when calling MappedRegistry#getId for the ResourceLocation of the registry objects will be able to return the available ids.
Now comes the problem, if we are replacing the identifiers on every world load, that would mean, depending on the world, there could be a differing number of entries between the server and client (assuming that there is no aliasing or other shenanigans going on). Well, not to worry! The RegistryManager takes a snapshot of the physical client registries after the RegisterEvents are finished. Then, whenever a world disconnects, either via ServerLifecycleHooks#handleServerStopped or ClientCommonPacketListenerImpl#onDisconnect, this snapshot is applied (via RegistryManager#revertToFrozen) to revert back to the state on the physical client.
So, what's the issue? Well, ServerLifecycleHooks#handleServerStopped is only called when a singleplayer world is exited, which ClientCommonPacketListenerImpl#onDisconnect is called when a client disconnects for an unexpected reason from the multiplayer server. It does not cover the case when the client clicks the disconnect button and disconnects normally from the multiplayer server. As such, the registry snapshot from the multiplayer server is still stored on the physical client. So, if let's say, the client has one more registry object than the server, when loading a singleplayer world, it will attempt to get the integer id for a key that was scrubbed by the multiplayer server.
The simplest way to fix this issue is to call RegistryManager#revertToFrozen at the end of Minecraft#disconnect as all instances of revertToFrozen are directly called after disconnect or once the integrated server is finished shutting down, which disconnect waits for anyway.
The text was updated successfully, but these errors were encountered:
1.21Targeted at Minecraft 1.211.21.1Targeted at Minecraft 1.21.11.21.2Targeted at Minecraft 1.21.21.21.3Targeted at Minecraft 1.21.31.21.4Targeted at Minecraft 1.21.41.21.5Targeted at Minecraft 1.21.5bugA bug or error
Minecraft Version: 1.21.1+, likely prior versions
NeoForge Version: 21.0.0-beta+
Original Issue: mezz/JustEnoughItems#3905 (Thank you to @pietro-lopes for the initial report and research)
Steps to Reproduce:
NeoForgeRegistriesSetup#VANILLA_SYNC_REGISTRIES
only on the physical client.Multiplayer
tab.ArrayIndexOutOfBoundsException
thrown during registry sync from the integrated server.Description of issue:
This bug results from a stale reference to multiplayer synced registries when the physical client has an additional registry object registered.
When the
RegistryManager
takes a snapshot of some registry and applies it to the existing registered, there needs to be some method to sync the identifiers. However, we generally don't want to overwrite the entire registry itself, instead, only modifying the network used integer ids to match those on the server. To do so, theRegistryManager
when constructing the registries to sync to the client, clears the ids to be repopulated viaRegistryManager#applySnapshot
viaMappedRegistry#registerIdMapping
.Note that
MappedRegistry#clear(false)
(which is what's called when syncing registries) only clears the integer identifiers, not the resource keys. However, when the server has a subset of the client registry objects, that is fine. The constructedRegistrySnapshot
when callingMappedRegistry#getId
for theResourceLocation
of the registry objects will be able to return the available ids.Now comes the problem, if we are replacing the identifiers on every world load, that would mean, depending on the world, there could be a differing number of entries between the server and client (assuming that there is no aliasing or other shenanigans going on). Well, not to worry! The
RegistryManager
takes a snapshot of the physical client registries after theRegisterEvent
s are finished. Then, whenever a world disconnects, either viaServerLifecycleHooks#handleServerStopped
orClientCommonPacketListenerImpl#onDisconnect
, this snapshot is applied (viaRegistryManager#revertToFrozen
) to revert back to the state on the physical client.So, what's the issue? Well,
ServerLifecycleHooks#handleServerStopped
is only called when a singleplayer world is exited, whichClientCommonPacketListenerImpl#onDisconnect
is called when a client disconnects for an unexpected reason from the multiplayer server. It does not cover the case when the client clicks the disconnect button and disconnects normally from the multiplayer server. As such, the registry snapshot from the multiplayer server is still stored on the physical client. So, if let's say, the client has one more registry object than the server, when loading a singleplayer world, it will attempt to get the integer id for a key that was scrubbed by the multiplayer server.The simplest way to fix this issue is to call
RegistryManager#revertToFrozen
at the end ofMinecraft#disconnect
as all instances ofrevertToFrozen
are directly called afterdisconnect
or once the integrated server is finished shutting down, whichdisconnect
waits for anyway.The text was updated successfully, but these errors were encountered: