Uploaded image for project: 'Minecraft: Java Edition'
  1. Minecraft: Java Edition
  2. MC-265520

Switching connection protocols sometimes produces errors

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • 24w03a
    • 1.20.2
    • None
    • Plausible
    • Networking
    • Low
    • Platform

      When entering and instantly exiting the configuration phase, an error may sometimes occur:
       

      [15:10:52] [Render thread/INFO]: Stopping worker threads
      [15:10:52] [Render thread/ERROR]: Error executing task on Client
      java.lang.IllegalStateException: Trying to set listener for protocol configuration, but current CLIENTBOUND protocol is play
          at net.minecraft.network.Connection.setListener(Connection.java:237) ~[client-intermediary.jar:?]
          at net.minecraft.client.multiplayer.ClientPacketListener.handleConfigurationStart(ClientPacketListener.java:784) ~[client-intermediary.jar:?]
          at net.minecraft.network.protocol.game.ClientboundStartConfigurationPacket.handle(ClientboundStartConfigurationPacket.java:22) ~[client-intermediary.jar:?]
          at net.minecraft.network.protocol.game.ClientboundStartConfigurationPacket.handle(ClientboundStartConfigurationPacket.java:11) ~[client-intermediary.jar:?]
          at net.minecraft.network.protocol.PacketUtils.lambda$ensureRunningOnSameThread$0(PacketUtils.java:23) ~[client-intermediary.jar:?]
          at net.minecraft.util.thread.BlockableEventLoop.doRunTask(BlockableEventLoop.java:156) ~[client-intermediary.jar:?]
          at net.minecraft.util.thread.ReentrantBlockableEventLoop.doRunTask(ReentrantBlockableEventLoop.java:23) ~[client-intermediary.jar:?]
          at net.minecraft.util.thread.BlockableEventLoop.pollTask(BlockableEventLoop.java:130) ~[client-intermediary.jar:?]
          at net.minecraft.util.thread.BlockableEventLoop.runAllTasks(BlockableEventLoop.java:115) ~[client-intermediary.jar:?]
          at net.minecraft.client.Minecraft.runTick(Minecraft.java:1231) ~[client-intermediary.jar:?]
          at net.minecraft.client.Minecraft.run(Minecraft.java:856) ~[client-intermediary.jar:?]
          at net.minecraft.client.main.Main.main(Main.java:253) ~[minecraft-1.20.2-client.jar:?]
          at net.fabricmc.loader.impl.game.minecraft.MinecraftGameProvider.launch(MinecraftGameProvider.java:468) ~[fabric-loader-0.14.22.jar:?]
          at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:74) ~[fabric-loader-0.14.22.jar:?]
          at net.fabricmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:23) ~[fabric-loader-0.14.22.jar:?]
          at org.prismlauncher.launcher.impl.StandardLauncher.launch(StandardLauncher.java:87) ~[NewLaunch.jar:?]
          at org.prismlauncher.EntryPoint.listen(EntryPoint.java:125) ~[NewLaunch.jar:?]
          at org.prismlauncher.EntryPoint.main(EntryPoint.java:70) ~[NewLaunch.jar:?]
      [15:11:22] [Netty Epoll Client IO #0/INFO]: Timeout
      io.netty.handler.timeout.ReadTimeoutException: null
      [15:11:22] [Render thread/WARN]: Client disconnected with reason: Timed out 

      (stacktrace remapped using official mappings, can also provide obfuscated stacktrace from vanilla client)

      Explanation

      (cause is somewhat related to MC-265209)

      Server sends ClientboundStartConfigurationPacket, switches encoding protocol to CONFIGURATION, sends some configuration packets (e.g. ClientboundRegistryDataPacket) and then exits by sending ClientboundFinishConfigurationPacket and switching encoder protocol to PLAY again. All these packets get encoded on the server and get sent as one "batch" to the client.

      The client first receives the ClientboundStartConfigurationPacket, disables auto-read and switches decoder protocol to CONFIGURATION, handling of the packet is queued on the main thread. After this first packet, the client immediately decodes (without any handling) all remaining packets sent in the "batch" (e.g. ClientboundRegistryDataPacket and ClientboundFinishConfigurationPacket) and switches (done in PacketDecoder) the decoder protocol to PLAY again because of the finish packet. The important thing to consider is packet handling on the main thread still hasn't happened yet.
       

      When the main thread starts to process the queued packets (only ClientboundStartConfigurationPacket, remaining packets are held up by FlowControlHandler, as they should be) the above mentioned error occurres. This is because the packet listener is updated on the main thread and when updating the listener a check is done to check whether the listener protocol matches the currently set protocol. This check fails, as the decoder already updated the protocol to PLAY again.

      Solution

      There are multiple solutions for this problem.

      One being just removing the connection protocol check when updating listeners. The protocol is updated on the netty threads and I would consider checking its value while on the main thread "unsafe".

      Another solution for this could be to add another FlowControlHandler before the PacketDecoder. This also resolves this issue (I hope, it's pretty rare to reproduce). I didn't test this with in-memory connections yet.

      Something else to consider is adding a new attribute to the channel, which is only updated on the main thread after a packet has been processed there. This could then be used for listener protocol checks instead of the protocol synced to netty.

            boq [Mojang] Bartosz Bok
            booky10 booky 10
            Votes:
            5 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved:
              CHK: