--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java @@ -26,6 +30,17 @@ import net.minecraft.util.thread.BlockableEventLoop; import org.slf4j.Logger; +// CraftBukkit start +import io.netty.buffer.ByteBuf; +import java.util.concurrent.ExecutionException; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.craftbukkit.util.CraftLocation; +import org.bukkit.craftbukkit.util.Waitable; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerResourcePackStatusEvent; +// CraftBukkit end + public abstract class ServerCommonPacketListenerImpl implements ServerCommonPacketListener { private static final Logger LOGGER = LogUtils.getLogger(); @@ -39,13 +54,21 @@ private int latency; private volatile boolean suspendFlushingOnServerThread = false; - public ServerCommonPacketListenerImpl(MinecraftServer minecraftserver, Connection connection, CommonListenerCookie commonlistenercookie) { + public ServerCommonPacketListenerImpl(MinecraftServer minecraftserver, Connection networkmanager, CommonListenerCookie commonlistenercookie, ServerPlayer player) { // CraftBukkit this.server = minecraftserver; this.connection = connection; this.keepAliveTime = Util.getMillis(); this.latency = commonlistenercookie.latency(); + // CraftBukkit start - add fields and methods + this.player = player; + this.cserver = minecraftserver.server; } + public CraftPlayer getCraftPlayer() { + return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity(); + // CraftBukkit end + } + @Override @Override public void onDisconnect(Component component) { @@ -59,6 +83,7 @@ @Override @Override public void handleKeepAlive(ServerboundKeepAlivePacket serverboundkeepalivepacket) { + PacketUtils.ensureRunningOnSameThread(serverboundkeepalivepacket, this, this.player.serverLevel()); // CraftBukkit if (this.keepAlivePending && serverboundkeepalivepacket.getId() == this.keepAliveChallenge) { int i = (int) (Util.getMillis() - this.keepAliveTime); @@ -74,10 +98,52 @@ @Override public void handlePong(ServerboundPongPacket serverboundpongpacket) {} + // CraftBukkit start + private static final ResourceLocation CUSTOM_REGISTER = new ResourceLocation("register"); + private static final ResourceLocation CUSTOM_UNREGISTER = new ResourceLocation("unregister"); + @Override @Override public void handleCustomPayload(ServerboundCustomPayloadPacket serverboundcustompayloadpacket) {} + if (identifier.equals(CUSTOM_REGISTER)) { + try { + String channels = payload.toString(com.google.common.base.Charsets.UTF_8); + for (String channel : channels.split("\0")) { + getCraftPlayer().addChannel(channel); + } + } catch (Exception ex) { + ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t register custom payload", ex); + this.disconnect("Invalid payload REGISTER!"); + } + } else if (identifier.equals(CUSTOM_UNREGISTER)) { + try { + String channels = payload.toString(com.google.common.base.Charsets.UTF_8); + for (String channel : channels.split("\0")) { + getCraftPlayer().removeChannel(channel); + } + } catch (Exception ex) { + ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t unregister custom payload", ex); + this.disconnect("Invalid payload UNREGISTER!"); + } + } else { + try { + byte[] data = new byte[payload.readableBytes()]; + payload.readBytes(data); + cserver.getMessenger().dispatchIncomingMessage(player.getBukkitEntity(), identifier.toString(), data); + } catch (Exception ex) { + ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t dispatch custom payload", ex); + this.disconnect("Invalid custom payload!"); + } + } + + } + + public final boolean isDisconnected() { + return !this.player.joining && !this.connection.isConnected(); + } + // CraftBukkit end + @Override @Override public void handleResourcePackResponse(ServerboundResourcePackPacket serverboundresourcepackpacket) { @@ -86,6 +156,7 @@ ServerCommonPacketListenerImpl.LOGGER.info("Disconnecting {} due to resource pack {} rejection", this.playerProfile().getName(), serverboundresourcepackpacket.id()); this.disconnect(Component.translatable("multiplayer.requiredTexturePrompt.disconnect")); } + this.cserver.getPluginManager().callEvent(new PlayerResourcePackStatusEvent(getCraftPlayer(), serverboundresourcepackpacket.id(), PlayerResourcePackStatusEvent.Status.values()[serverboundresourcepackpacket.action().ordinal()])); // CraftBukkit } @@ -93,7 +164,7 @@ this.server.getProfiler().push("keepAlive"); long i = Util.getMillis(); - if (i - this.keepAliveTime >= 15000L) { + if (i - this.keepAliveTime >= 25000L) { // CraftBukkit if (this.keepAlivePending) { this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE); } else { @@ -121,6 +192,14 @@ } public void send(Packet packet, @Nullable PacketSendListener packetsendlistener) { + // CraftBukkit start + if (packet == null) { + return; + } else if (packet instanceof ClientboundSetDefaultSpawnPositionPacket) { + ClientboundSetDefaultSpawnPositionPacket packet6 = (ClientboundSetDefaultSpawnPositionPacket) packet; + this.player.compassTarget = CraftLocation.toBukkit(packet6.pos, this.getCraftPlayer().getWorld()); + } + // CraftBukkit end boolean flag = !this.suspendFlushingOnServerThread || !this.server.isSameThread(); try { @@ -136,16 +215,67 @@ } } - public void disconnect(Component component) { - this.connection.send(new ClientboundDisconnectPacket(component), PacketSendListener.thenRun(() -> { - this.connection.disconnect(component); + // CraftBukkit start + @Deprecated + public void disconnect(Component ichatbasecomponent) { + disconnect(CraftChatMessage.fromComponent(ichatbasecomponent)); + } + // CraftBukkit end + + public void disconnect(String s) { + // CraftBukkit start - fire PlayerKickEvent + if (this.processedDisconnect) { + return; + } + if (!this.cserver.isPrimaryThread()) { + Waitable waitable = new Waitable() { + @Override + protected Object evaluate() { + ServerCommonPacketListenerImpl.this.disconnect(s); + return null; + } + }; + + this.server.processQueue.add(waitable); + + try { + waitable.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + return; + } + + String leaveMessage = ChatFormatting.YELLOW + this.player.getScoreboardName() + " left the game."; + + PlayerKickEvent event = new PlayerKickEvent(this.player.getBukkitEntity(), s, leaveMessage); + + if (this.cserver.getServer().isRunning()) { + this.cserver.getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + // Do not kick the player + return; + } + this.player.kickLeaveMessage = event.getLeaveMessage(); // CraftBukkit - SPIGOT-3034: Forward leave message to PlayerQuitEvent + // Send the possibly modified leave message + final Component ichatbasecomponent = CraftChatMessage.fromString(event.getReason(), true)[0]; + // CraftBukkit end + + this.connection.send(new ClientboundDisconnectPacket(ichatbasecomponent), PacketSendListener.thenRun(() -> { + this.connection.disconnect(ichatbasecomponent); })); + this.onDisconnect(ichatbasecomponent); // CraftBukkit - fire quit instantly this.connection.setReadOnly(); MinecraftServer minecraftserver = this.server; Connection connection = this.connection; Objects.requireNonNull(this.connection); - minecraftserver.executeBlocking(connection::handleDisconnection); + // CraftBukkit - Don't wait + minecraftserver.wrapRunnable(networkmanager::handleDisconnection); } protected boolean isSingleplayerOwner() {