Conversation
Fabric only untracks a listener's network addon from Connection.channelInactive or handleDisconnection, which a channel-less dummy never reaches, so leave() now calls handleDisconnect() itself before onDisconnect. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #20.
Fabric API tracks a
ServerPlayNetworkAddonfor everyServerGamePacketListenerImplin the staticServerNetworkingImpl.PLAYregistry and only unregisters it fromConnection.channelInactive()or from theonDisconnectcall insideConnection.handleDisconnection(). A dummy connection has no channel, so neither path ever runs, andDummy.leave()calls the listener'sonDisconnect()directly. The player left the world, but the static registry kept the addon, the listener, theDummyClientConnectionand everything it had queued reachable, and every spawn/leave cycle stacked on the previous one.leave()now ends the Fabric session right beforeonDisconnect(), the order Fabric itself uses for real connections.handleDisconnect()is idempotent and also firesServerPlayConnectionEvents.DISCONNECT, which mods listening to it never got for dummies.PacketListenerExtensionsis Fabric's implementation interface (its mixin makes everyServerGamePacketListenerImplimplement it), not public API. The alternative Fabric intends for fake players is a listener implementingUntrackedPacketListener(alsoimpl), which would need a@WrapOperationon the listener construction inPlayerList.placeNewPlayerto give dummies their own listener class. I went with the three-line version; happy to switch if you prefer the marker.Testing
Minecraft 26.3, Fabric API 0.161.0+26.3, dedicated server, 3 GB heap, 300 armor stands teleported every tick, 20 dummies spawned, kept online 30 s, then
dummy leave, twice.jcmd GC.run+GC.class_histogram, countingServerPlayNetworkAddon/ServerGamePacketListenerImpl/DummyClientConnection:The departed dummies are still reachable 3 s after leaving and gone by the 30 s check; something vanilla-side holds them briefly, it does not accumulate.
@ais empty after each leave in both builds, so the vanilla removal was never the problem.