Skip to content

fix(proxy): queue play packets when reentering configuration#1791

Closed
AlexProgrammerDE wants to merge 1 commit into
PaperMC:dev/3.0.0from
AlexProgrammerDE:fix/reconfiguration-play-packet-queue
Closed

fix(proxy): queue play packets when reentering configuration#1791
AlexProgrammerDE wants to merge 1 commit into
PaperMC:dev/3.0.0from
AlexProgrammerDE:fix/reconfiguration-play-packet-queue

Conversation

@AlexProgrammerDE
Copy link
Copy Markdown
Contributor

@AlexProgrammerDE AlexProgrammerDE commented May 13, 2026

Summary

Restore the full play-packet queue when a player connection reenters configuration state from play state.

This keeps the existing serverbound forwarding guards from the previous reconfiguration change, but avoids switching the client-side connection to outbound-only queueing during reconfiguration. Reentering configuration is a transitional protocol state, so both inbound and outbound play packets should be handled consistently until the configuration acknowledgement completes.

Root cause

The current code installs only PlayPacketQueueOutboundHandler when a play-state client is moved back into configuration. That protects clientbound play packets, but leaves the inbound side without the normal configuration-state queueing used elsewhere. In nested or rapid server-switch flows, this can leave the switch stuck waiting for reconfiguration to complete.

Reconfiguration flow

sequenceDiagram
    actor Player
    participant Velocity
    participant Server as Backend server

    Player->>Velocity: In PLAY on current backend
    Server-->>Velocity: New backend login succeeds
    Velocity->>Player: StartUpdatePacket
    Note over Player,Velocity: Client starts PLAY to CONFIG reconfiguration
    Velocity->>Velocity: pendingConfigurationSwitch = true

    alt Outbound-only queueing
        Velocity->>Velocity: Install PlayPacketQueueOutboundHandler only
        Note over Velocity: Clientbound PLAY packets are queued,<br/>but serverbound PLAY packets are not buffered
        Player-->>Velocity: Serverbound PLAY packet during transition
        Velocity--xVelocity: Packet reaches the transitional CONFIG path
        Server--xVelocity: Switch can stall before configuration completes
        Velocity--xPlayer: Client remains at reconfiguring until timeout
    else Full play-packet queueing
        Velocity->>Velocity: Install full play-packet queue
        Note over Velocity: PLAY packets in both directions are queued,<br/>while CONFIG packets continue through
        Player->>Velocity: FinishedUpdatePacket
        Velocity->>Server: Forward FinishedUpdatePacket
        Velocity->>Velocity: Backend enters CONFIG
        Server->>Velocity: JoinGame after config completes
        Velocity->>Player: JoinGame and switch packets
        Velocity->>Velocity: Remove play-packet queues
        Player->>Velocity: In PLAY on new backend
    end
Loading

Validation

  • git diff --check
  • JAVA_HOME=/home/alex/.gradle/jdks/eclipse_adoptium-21-amd64-linux.2 ./gradlew check --console=plain

Disclosure

This PR was made by codex to fix a bug I have when configuration transitions happen.


Regression was introduced as part of b1a1b8b.

@AlexProgrammerDE AlexProgrammerDE marked this pull request as ready for review May 13, 2026 22:35
Copilot AI review requested due to automatic review settings May 13, 2026 22:35
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Reverts a regression from b1a1b8b by restoring the full play-packet queue (both inbound and outbound) when a player connection reenters configuration state, rather than installing only the outbound queue handler. This ensures inbound play packets from the client are also queued during the transitional reconfiguration period.

Changes:

  • In MinecraftConnection.setState, drop the conditional that installed only PlayPacketQueueOutboundHandler for PLAY → CONFIG transitions on ConnectedPlayer connections, and unconditionally call addPlayPacketQueueHandler(). Removes the now-unused previousState local and ConnectedPlayer import.
  • In ConnectedPlayer.switchToConfigState, replace addPlayPacketQueueOutboundHandler() with addPlayPacketQueueHandler() so the inbound queue is installed alongside the outbound one when the client is moved into config state.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java Removes the PLAY → CONFIG special case and always installs the full play packet queue when entering CONFIG; cleans up unused import and local.
proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java Switches switchToConfigState from outbound-only queue handler to the full inbound+outbound queue handler.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@AlexProgrammerDE AlexProgrammerDE force-pushed the fix/reconfiguration-play-packet-queue branch from 31f2997 to 789a654 Compare May 13, 2026 22:53
@electronicboy
Copy link
Copy Markdown
Member

not sure why we'd be queing incoming play packets there, sounds like that was always a bad idea, should we not just be discarding them?

@WouterGritter
Copy link
Copy Markdown
Contributor

For context, #1088 is the PR that originally introduced the packet queue here, e.g.

if (state == StateRegistry.CONFIG) {
  // Activate the play packet queue
  addPlayPacketQueueHandler();
} else if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE) != null) {
  // Remove the queue
  this.channel.pipeline().remove(Connections.PLAY_PACKET_QUEUE);
}

in MinecraftConnection#setState.

@AlexProgrammerDE
Copy link
Copy Markdown
Contributor Author

AlexProgrammerDE commented May 17, 2026

I realized what we were doing was unidiomatic and did a big refactor internally now to use Velocity more idiomatically, so this isn't needed for me anymore, so will close. We were chaining velocity but after some consideration, it's not worth the trouble of patching velocity to make it work. We now just use one velocity + a plugin, which is way simpler and better.

@WouterGritter
Copy link
Copy Markdown
Contributor

Might still be worth looking into - chaining Velocity instances might not be explicitly supported but adding Velocity into the chain should not break the vanilla MC protocol.

Client - > Velocity_1 -> Velocity_2 -> Backend breaking suggests something is going wrong, either Velocity_1 breaks the vanilla protocol with the packets its sending serverbound, or Velocity_2 does not understand the vanilla protocol Velocity_1 is speaking clientbound.

@electronicboy
Copy link
Copy Markdown
Member

The patch which this is partially reverting fixed a case of chaining, which is why I'm dubious on touching stuff here without further considerations

@AlexProgrammerDE
Copy link
Copy Markdown
Contributor Author

AlexProgrammerDE commented May 17, 2026

chaining velocity is itself a very complex issue because of nested configuration phases and our loads of patches were very annoying to maintain.

For anyone trying to make this work, I wish you good luck, it's a pretty annoying issue.

@R00tB33rMan
Copy link
Copy Markdown
Contributor

Hey @AlexProgrammerDE, would you be able to see if your issue is recreatable with the PR attached to this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants