Expected behavior
PlayerTeleportEvent should not be called after PlayerQuitEvent when a player leaves the server while inside a vehicle (e.g. boat).
This event is called when the Minecraft server teleports a player that is inside a vehicle while leaving the server, after Paper has already called PlayerQuitEvent.
Observed/Actual behavior
As the title says, when a player is inside a vehicle (e.g. a boat) and quits, a dangling PlayerTeleportEvent is called after PlayerQuitEvent has already been called. This might not be a bug/incompatibility, but this also might not be the behavior plugin developers expect.
Stacktrace for the PlayerTeleportEvent called after PlayerQuitEvent below. Note that the IllegalStateException exception is generated internally in the plugin, which only ever occurs if PlayerQuitEvent had been called before PlayerTeleportEvent.
[12:33:18 ERROR]: Could not pass event PlayerTeleportEvent to <plugin> v1.0.0
java.lang.IllegalStateException: <plugin generated message>
at <plugin>-1.0-SNAPSHOT.jar//<plugin method> ~[?:?]
at <plugin>-1.0-SNAPSHOT.jar//<plugin class>.onPlayerTeleportEvent(<plugin class>.java:117) ~[?:?]
at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:80) ~[paper-api-1.21.11-R0.1-SNAPSHOT.jar:?]
at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:71) ~[paper-api-1.21.11-R0.1-SNAPSHOT.jar:?]
at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:54) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:131) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:628) ~[paper-api-1.21.11-R0.1-SNAPSHOT.jar:?]
at net.minecraft.server.network.ServerGamePacketListenerImpl.teleport(ServerGamePacketListenerImpl.java:1851) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.level.ServerPlayer.teleportTo(ServerPlayer.java:2245) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.level.ServerPlayer.dismountTo(ServerPlayer.java:2234) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.world.entity.LivingEntity.dismountVehicle(LivingEntity.java:2955) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.world.entity.LivingEntity.stopRiding(LivingEntity.java:3923) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.world.entity.Entity.stopRiding(Entity.java:3328) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.players.PlayerList.remove(PlayerList.java:471) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.players.PlayerList.remove(PlayerList.java:430) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.network.ServerGamePacketListenerImpl.removePlayerFromWorld(ServerGamePacketListenerImpl.java:2259) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.network.ServerGamePacketListenerImpl.onDisconnect(ServerGamePacketListenerImpl.java:2237) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.network.Connection.handleDisconnection(Connection.java:820) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.network.ServerConnectionListener.tick(ServerConnectionListener.java:244) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.MinecraftServer.tickConnection(MinecraftServer.java:1872) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.dedicated.DedicatedServer.tickConnection(DedicatedServer.java:549) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.MinecraftServer.tickChildren(MinecraftServer.java:1835) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.MinecraftServer.tickServer(MinecraftServer.java:1613) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.dedicated.DedicatedServer.tickServer(DedicatedServer.java:427) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.MinecraftServer.processPacketsAndTick(MinecraftServer.java:1669) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1337) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at net.minecraft.server.MinecraftServer.lambda$spin$2(MinecraftServer.java:388) ~[paper-1.21.11.jar:1.21.11-69-94d0c97]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[?:?]
Steps/models to reproduce
Inside a Paper plugin, create a Listener that:
- Contains a
Set<Player> seenPlayers;
- Listens for
PlayerJoinEvent and adds the player to the set
- Listens for
PlayerQuitEvent and removes the player from the set
- Listens for
PlayerTeleportEvent and throw if PlayerTeleportEvent#getPlayer is not contained in seenPlayers
- Join the server, get inside a boat and quit
Plugin and Datapack List
n/a
Paper version
> version
[12:44:32 INFO]: Checking version, please wait...
[12:44:32 INFO]: This server is running Paper version 1.21.11-69-main@94d0c97 (2025-12-30T20:33:30Z) (Implementing API version 1.21.11-R0.1-SNAPSHOT)
You are running the latest version
Previous version: 1.21.10-115-af06383 (MC: 1.21.10)
Other
If I am mistaken and "dangling" player events may be called after PlayerQuitEvent has been called, I think the javadoc should mention this.
Expected behavior
PlayerTeleportEventshould not be called afterPlayerQuitEventwhen a player leaves the server while inside a vehicle (e.g. boat).This event is called when the Minecraft server teleports a player that is inside a vehicle while leaving the server, after Paper has already called
PlayerQuitEvent.Observed/Actual behavior
As the title says, when a player is inside a vehicle (e.g. a boat) and quits, a dangling
PlayerTeleportEventis called afterPlayerQuitEventhas already been called. This might not be a bug/incompatibility, but this also might not be the behavior plugin developers expect.Stacktrace for the
PlayerTeleportEventcalled afterPlayerQuitEventbelow. Note that theIllegalStateExceptionexception is generated internally in the plugin, which only ever occurs ifPlayerQuitEventhad been called beforePlayerTeleportEvent.Steps/models to reproduce
Inside a Paper plugin, create a
Listenerthat:Set<Player> seenPlayers;PlayerJoinEventand adds the player to the setPlayerQuitEventand removes the player from the setPlayerTeleportEventand throw ifPlayerTeleportEvent#getPlayeris not contained inseenPlayersPlugin and Datapack List
n/a
Paper version
Other
If I am mistaken and "dangling" player events may be called after
PlayerQuitEventhas been called, I think the javadoc should mention this.