Allow listening to plugin messages during configuration phase (#12775)

This commit is contained in:
Owen
2025-07-01 09:40:50 -04:00
committed by GitHub
parent 51d28af8fa
commit 6ea679eb07
12 changed files with 216 additions and 128 deletions

View File

@@ -1,9 +1,11 @@
package org.bukkit.plugin.messaging; package org.bukkit.plugin.messaging;
import java.util.Set; import java.util.Set;
import io.papermc.paper.connection.PlayerConnection;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
@@ -230,4 +232,14 @@ public interface Messenger {
* @param message Raw payload of the message. * @param message Raw payload of the message.
*/ */
public void dispatchIncomingMessage(@NotNull Player source, @NotNull String channel, byte @NotNull [] message); public void dispatchIncomingMessage(@NotNull Player source, @NotNull String channel, byte @NotNull [] message);
/**
* Dispatches the specified incoming message to any registered listeners.
*
* @param source Source of the message.
* @param channel Channel that the message was sent by.
* @param message Raw payload of the message.
*/
@ApiStatus.Experimental
public void dispatchIncomingMessage(@NotNull PlayerConnection source, @NotNull String channel, byte @NotNull [] message);
} }

View File

@@ -1,7 +1,9 @@
package org.bukkit.plugin.messaging; package org.bukkit.plugin.messaging;
import io.papermc.paper.connection.PlayerCommonConnection; import io.papermc.paper.connection.PlayerCommonConnection;
import io.papermc.paper.connection.PlayerConnection;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
@@ -28,7 +30,8 @@ public interface PluginMessageListener {
* @param connection Source of the message. * @param connection Source of the message.
* @param message The raw message that was sent. * @param message The raw message that was sent.
*/ */
default void onPluginMessageReceived(@NotNull String channel, @NotNull PlayerCommonConnection connection, byte @NotNull [] message) { @ApiStatus.Experimental
default void onPluginMessageReceived(@NotNull String channel, @NotNull PlayerConnection connection, byte @NotNull [] message) {
} }
} }

View File

@@ -8,6 +8,7 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import io.papermc.paper.connection.PlayerConnection;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -462,6 +463,30 @@ public class StandardMessenger implements Messenger {
} }
} }
@Override
public void dispatchIncomingMessage(@NotNull PlayerConnection source, @NotNull String channel, byte @NotNull [] message) {
if (source == null) {
throw new IllegalArgumentException("Player source cannot be null");
}
if (message == null) {
throw new IllegalArgumentException("Message cannot be null");
}
channel = validateAndCorrectChannel(channel);
Set<PluginMessageListenerRegistration> registrations = getIncomingChannelRegistrations(channel);
for (PluginMessageListenerRegistration registration : registrations) {
try {
registration.getListener().onPluginMessageReceived(channel, source, message);
} catch (Throwable t) {
registration.getPlugin().getLogger().log(Level.WARNING,
String.format("Plugin %s generated an exception whilst handling plugin message",
registration.getPlugin().getDescription().getFullName()
), t);
}
}
}
/** /**
* Validates a Plugin Channel name. * Validates a Plugin Channel name.
* *

View File

@@ -85,22 +85,22 @@ index 0000000000000000000000000000000000000000..4a2520f554c2ee74faf86d7c93baccf0
+ } + }
+} +}
diff --git a/net/minecraft/server/network/CommonListenerCookie.java b/net/minecraft/server/network/CommonListenerCookie.java diff --git a/net/minecraft/server/network/CommonListenerCookie.java b/net/minecraft/server/network/CommonListenerCookie.java
index b94988d6dd98bfb7d3d2f08c2adaa96d7c7a8b55..7e61c8222d567feb8c2b988699e1cfe83bf4be31 100644 index 962084054c0208470d0c3c99c5dca6327c9b8752..2abc21102bbd2da79dc0c50826cff7da01a0f9bc 100644
--- a/net/minecraft/server/network/CommonListenerCookie.java --- a/net/minecraft/server/network/CommonListenerCookie.java
+++ b/net/minecraft/server/network/CommonListenerCookie.java +++ b/net/minecraft/server/network/CommonListenerCookie.java
@@ -3,8 +3,8 @@ package net.minecraft.server.network; @@ -3,8 +3,8 @@ package net.minecraft.server.network;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import net.minecraft.server.level.ClientInformation; import net.minecraft.server.level.ClientInformation;
-public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jetbrains.annotations.Nullable String brandName) { // Paper -public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jetbrains.annotations.Nullable String brandName, java.util.Set<String> channels) { // Paper
+public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jetbrains.annotations.Nullable String brandName, io.papermc.paper.util.KeepAlive keepAlive) { // Paper +public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jetbrains.annotations.Nullable String brandName, java.util.Set<String> channels, io.papermc.paper.util.KeepAlive keepAlive) { // Paper
public static CommonListenerCookie createInitial(GameProfile gameProfile, boolean transferred) { public static CommonListenerCookie createInitial(GameProfile gameProfile, boolean transferred) {
- return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null); // Paper - return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null, new java.util.HashSet<>()); // Paper
+ return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null, new io.papermc.paper.util.KeepAlive()); // Paper + return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null, new java.util.HashSet<>(), new io.papermc.paper.util.KeepAlive()); // Paper
} }
} }
diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
index fae1523404b7afa2b904faad9242273b3c406aac..16962ccab91d0941e428a2e53aa37e9ca975f62f 100644 index ab2bcd5b547c8db418de7ea0b7f144058aa8b0f4..1ae3724eec5fac852c6cc6bb88b1ecb9f0b790f0 100644
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java --- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
@@ -38,12 +38,13 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack @@ -38,12 +38,13 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@@ -121,7 +121,7 @@ index fae1523404b7afa2b904faad9242273b3c406aac..16962ccab91d0941e428a2e53aa37e9c
private volatile boolean suspendFlushingOnServerThread = false; private volatile boolean suspendFlushingOnServerThread = false;
// CraftBukkit start // CraftBukkit start
protected final org.bukkit.craftbukkit.CraftServer cserver; protected final org.bukkit.craftbukkit.CraftServer cserver;
@@ -57,12 +58,13 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack @@ -60,13 +61,14 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) { public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) {
this.server = server; this.server = server;
this.connection = connection; this.connection = connection;
@@ -132,11 +132,12 @@ index fae1523404b7afa2b904faad9242273b3c406aac..16962ccab91d0941e428a2e53aa37e9c
// Paper start // Paper start
this.playerBrand = cookie.brandName(); this.playerBrand = cookie.brandName();
this.cserver = server.server; this.cserver = server.server;
this.pluginMessagerChannels = cookie.channels();
+ this.keepAlive = cookie.keepAlive(); + this.keepAlive = cookie.keepAlive();
// Paper end // Paper end
} }
@@ -89,13 +91,41 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack @@ -93,13 +95,41 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@Override @Override
public void handleKeepAlive(ServerboundKeepAlivePacket packet) { public void handleKeepAlive(ServerboundKeepAlivePacket packet) {
@@ -184,7 +185,7 @@ index fae1523404b7afa2b904faad9242273b3c406aac..16962ccab91d0941e428a2e53aa37e9c
} }
@Override @Override
@@ -148,20 +178,23 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack @@ -225,20 +255,23 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
protected void keepConnectionAlive() { protected void keepConnectionAlive() {
Profiler.get().push("keepAlive"); Profiler.get().push("keepAlive");
long millis = Util.getMillis(); long millis = Util.getMillis();
@@ -222,13 +223,11 @@ index fae1523404b7afa2b904faad9242273b3c406aac..16962ccab91d0941e428a2e53aa37e9c
} }
} }
@@ -341,6 +374,6 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack @@ -418,6 +451,6 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
} }
protected CommonListenerCookie createCookie(ClientInformation clientInformation) { protected CommonListenerCookie createCookie(ClientInformation clientInformation) {
- return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand); // Paper - return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels); // Paper
+ return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.keepAlive); // Paper + return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels, this.keepAlive); // Paper
} }
-} }
+}
\ No newline at end of file

View File

@@ -5,9 +5,9 @@
import net.minecraft.server.level.ClientInformation; import net.minecraft.server.level.ClientInformation;
-public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred) { -public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred) {
+public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jetbrains.annotations.Nullable String brandName) { // Paper +public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jetbrains.annotations.Nullable String brandName, java.util.Set<String> channels) { // Paper
public static CommonListenerCookie createInitial(GameProfile gameProfile, boolean transferred) { public static CommonListenerCookie createInitial(GameProfile gameProfile, boolean transferred) {
- return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred); - return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred);
+ return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null); // Paper + return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null, new java.util.HashSet<>()); // Paper
} }
} }

View File

@@ -9,7 +9,7 @@
private final boolean transferred; private final boolean transferred;
private long keepAliveTime; private long keepAliveTime;
private boolean keepAlivePending; private boolean keepAlivePending;
@@ -45,6 +_,14 @@ @@ -45,6 +_,17 @@
private boolean closed = false; private boolean closed = false;
private int latency; private int latency;
private volatile boolean suspendFlushingOnServerThread = false; private volatile boolean suspendFlushingOnServerThread = false;
@@ -20,17 +20,21 @@
+ public final java.util.Map<java.util.UUID, net.kyori.adventure.resource.ResourcePackCallback> packCallbacks = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - adventure resource pack callbacks + public final java.util.Map<java.util.UUID, net.kyori.adventure.resource.ResourcePackCallback> packCallbacks = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - adventure resource pack callbacks
+ private static final long KEEPALIVE_LIMIT = Long.getLong("paper.playerconnection.keepalive", 30) * 1000; // Paper - provide property to set keepalive limit + private static final long KEEPALIVE_LIMIT = Long.getLong("paper.playerconnection.keepalive", 30) * 1000; // Paper - provide property to set keepalive limit
+ protected static final net.minecraft.resources.ResourceLocation MINECRAFT_BRAND = net.minecraft.resources.ResourceLocation.withDefaultNamespace("brand"); // Paper - Brand support + protected static final net.minecraft.resources.ResourceLocation MINECRAFT_BRAND = net.minecraft.resources.ResourceLocation.withDefaultNamespace("brand"); // Paper - Brand support
+ public @Nullable String playerBrand; // Paper + // Paper start - retain certain values
+ public @Nullable String playerBrand;
+ public final java.util.Set<String> pluginMessagerChannels;
+ // Paper end - retain certain values
public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) { public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) {
this.server = server; this.server = server;
@@ -52,6 +_,10 @@ @@ -52,6 +_,11 @@
this.keepAliveTime = Util.getMillis(); this.keepAliveTime = Util.getMillis();
this.latency = cookie.latency(); this.latency = cookie.latency();
this.transferred = cookie.transferred(); this.transferred = cookie.transferred();
+ // Paper start + // Paper start
+ this.playerBrand = cookie.brandName(); + this.playerBrand = cookie.brandName();
+ this.cserver = server.server; + this.cserver = server.server;
+ this.pluginMessagerChannels = cookie.channels();
+ // Paper end + // Paper end
} }
@@ -44,6 +48,88 @@
} }
} }
@@ -90,8 +_,81 @@
public void handlePong(ServerboundPongPacket packet) {
}
+ // Paper start
+ public static final net.minecraft.resources.ResourceLocation CUSTOM_REGISTER = net.minecraft.resources.ResourceLocation.withDefaultNamespace("register");
+ private static final net.minecraft.resources.ResourceLocation CUSTOM_UNREGISTER = net.minecraft.resources.ResourceLocation.withDefaultNamespace("unregister");
+ // Paper end
+
@Override
public void handleCustomPayload(ServerboundCustomPayloadPacket packet) {
+ // Paper start
+ if (!(packet.payload() instanceof final net.minecraft.network.protocol.common.custom.DiscardedPayload discardedPayload)) {
+ return;
+ }
+
+ net.minecraft.network.protocol.PacketUtils.ensureRunningOnSameThread(packet, this, this.server);
+
+ final net.minecraft.resources.ResourceLocation identifier = packet.payload().type().id();
+ final byte[] data = discardedPayload.data();
+ try {
+ final boolean registerChannel = CUSTOM_REGISTER.equals(identifier);
+ if (registerChannel || CUSTOM_UNREGISTER.equals(identifier)) {
+ // Strings separated by zeros instead of length prefixes
+ int startIndex = 0;
+ for (int i = 0; i < data.length; i++) {
+ final byte b = data[i];
+ if (b != 0) {
+ continue;
+ }
+
+ readChannelIdentifier(data, startIndex, i, registerChannel);
+ startIndex = i + 1;
+ }
+
+ // Read the last one
+ readChannelIdentifier(data, startIndex, data.length, registerChannel);
+ return;
+ }
+
+ if (identifier.equals(MINECRAFT_BRAND)) {
+ this.playerBrand = new net.minecraft.network.FriendlyByteBuf(io.netty.buffer.Unpooled.wrappedBuffer(data)).readUtf(256);
+ }
+
+
+ if (this instanceof ServerGamePacketListenerImpl gamePacketListener) {
+ this.cserver.getMessenger().dispatchIncomingMessage(gamePacketListener.player.getBukkitEntity(), identifier.toString(), data);
+ } else if (this instanceof ServerConfigurationPacketListenerImpl configurationPacketListener) {
+ this.cserver.getMessenger().dispatchIncomingMessage(configurationPacketListener.paperConnection, identifier.toString(), data);
+ }
+ } catch (final Exception e) {
+ ServerGamePacketListenerImpl.LOGGER.error("Couldn't handle custom payload on channel {}", identifier, e);
+ this.disconnect(net.minecraft.network.chat.Component.literal("Invalid custom payload payload!"), io.papermc.paper.connection.DisconnectionReason.INVALID_PAYLOAD); // Paper - kick event cause
+ }
+ }
+
+ private void readChannelIdentifier(final byte[] data, final int from, final int to, final boolean register) {
+ io.papermc.paper.connection.PluginMessageBridgeImpl bridge = switch (this) {
+ case ServerGamePacketListenerImpl gamePacketListener -> gamePacketListener.player.getBukkitEntity();
+ case ServerConfigurationPacketListenerImpl commonPacketListener -> commonPacketListener.paperConnection;
+ default -> null;
+ };
+ if (bridge == null) {
+ return;
+ }
+
+
+ final int length = to - from;
+ if (length == 0) {
+ return;
+ }
+
+ final String channel = new String(data, from, length, java.nio.charset.StandardCharsets.US_ASCII);
+ if (register) {
+ bridge.addChannel(channel);
+ } else {
+ bridge.removeChannel(channel);
+ }
+ // Paper end
}
@Override
@@ -105,21 +_,46 @@ @@ -105,21 +_,46 @@
PacketUtils.ensureRunningOnSameThread(packet, this, this.server); PacketUtils.ensureRunningOnSameThread(packet, this, this.server);
if (packet.action() == ServerboundResourcePackPacket.Action.DECLINED && this.server.isResourcePackRequired()) { if (packet.action() == ServerboundResourcePackPacket.Action.DECLINED && this.server.isResourcePackRequired()) {
@@ -245,6 +331,6 @@
protected CommonListenerCookie createCookie(ClientInformation clientInformation) { protected CommonListenerCookie createCookie(ClientInformation clientInformation) {
- return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred); - return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred);
+ return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand); // Paper + return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels); // Paper
} }
} }

View File

@@ -99,7 +99,7 @@
this.connection.send(new ClientboundDisconnectPacket(DISCONNECT_REASON_INVALID_DATA)); this.connection.send(new ClientboundDisconnectPacket(DISCONNECT_REASON_INVALID_DATA));
this.connection.disconnect(DISCONNECT_REASON_INVALID_DATA); this.connection.disconnect(DISCONNECT_REASON_INVALID_DATA);
} }
@@ -180,4 +_,31 @@ @@ -180,4 +_,29 @@
this.startNextTask(); this.startNextTask();
} }
} }
@@ -125,9 +125,7 @@
+ +
+ @Override + @Override
+ public void handleCustomPayload(net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket packet) { + public void handleCustomPayload(net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket packet) {
+ if (packet.payload() instanceof net.minecraft.network.protocol.common.custom.BrandPayload(String brand)) { + super.handleCustomPayload(packet);
+ this.playerBrand = brand;
+ }
+ } + }
+ // Paper end + // Paper end
} }

View File

@@ -2576,7 +2576,7 @@
} }
} }
@@ -2027,27 +_,27 @@ @@ -2027,27 +_,32 @@
private void resetPlayerChatState(RemoteChatSession chatSession) { private void resetPlayerChatState(RemoteChatSession chatSession) {
this.chatSession = chatSession; this.chatSession = chatSession;
@@ -2596,10 +2596,11 @@
+ }); + });
} }
); );
- } }
-
- @Override @Override
- public void handleCustomPayload(ServerboundCustomPayloadPacket packet) { public void handleCustomPayload(ServerboundCustomPayloadPacket packet) {
+ super.handleCustomPayload(packet); // Paper
} }
@Override @Override
@@ -2609,7 +2610,7 @@
if (!this.receivedMovementThisTick) { if (!this.receivedMovementThisTick) {
this.player.setKnownMovement(Vec3.ZERO); this.player.setKnownMovement(Vec3.ZERO);
} }
@@ -2078,4 +_,157 @@ @@ -2078,4 +_,93 @@
interface EntityInteraction { interface EntityInteraction {
InteractionResult run(ServerPlayer player, Entity entity, InteractionHand hand); InteractionResult run(ServerPlayer player, Entity entity, InteractionHand hand);
} }
@@ -2661,70 +2662,6 @@
+ }); + });
+ } + }
+ +
+ // Paper start
+ public static final ResourceLocation CUSTOM_REGISTER = ResourceLocation.withDefaultNamespace("register"); // CraftBukkit
+ private static final ResourceLocation CUSTOM_UNREGISTER = ResourceLocation.withDefaultNamespace("unregister"); // CraftBukkit
+
+ @Override
+ public void handleCustomPayload(ServerboundCustomPayloadPacket packet) {
+ // Paper start
+ if (packet.payload() instanceof net.minecraft.network.protocol.common.custom.BrandPayload(String brand)) {
+ this.playerBrand = brand;
+ }
+
+ if (!(packet.payload() instanceof final net.minecraft.network.protocol.common.custom.DiscardedPayload discardedPayload)) {
+ return;
+ }
+
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level());
+
+ final net.minecraft.resources.ResourceLocation identifier = packet.payload().type().id();
+ final byte[] data = discardedPayload.data();
+ try {
+ final boolean registerChannel = CUSTOM_REGISTER.equals(identifier);
+ if (registerChannel || CUSTOM_UNREGISTER.equals(identifier)) {
+ // Strings separated by zeros instead of length prefixes
+ int startIndex = 0;
+ for (int i = 0; i < data.length; i++) {
+ final byte b = data[i];
+ if (b != 0) {
+ continue;
+ }
+
+ readChannelIdentifier(data, startIndex, i, registerChannel);
+ startIndex = i + 1;
+ }
+
+ // Read the last one
+ readChannelIdentifier(data, startIndex, data.length, registerChannel);
+ return;
+ }
+
+ if (identifier.equals(MINECRAFT_BRAND)) {
+ this.player.connection.playerBrand = new net.minecraft.network.FriendlyByteBuf(io.netty.buffer.Unpooled.wrappedBuffer(data)).readUtf(256);
+ }
+
+ this.cserver.getMessenger().dispatchIncomingMessage(this.player.getBukkitEntity(), identifier.toString(), data);
+ } catch (final Exception e) {
+ ServerGamePacketListenerImpl.LOGGER.error("Couldn't handle custom payload on channel {}", identifier, e);
+ this.disconnect(Component.literal("Invalid custom payload payload!"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause
+ }
+ }
+
+ private void readChannelIdentifier(final byte[] data, final int from, final int to, final boolean register) {
+ final int length = to - from;
+ if (length == 0) {
+ return;
+ }
+
+ final String channel = new String(data, from, length, java.nio.charset.StandardCharsets.US_ASCII);
+ if (register) {
+ this.getCraftPlayer().addChannel(channel);
+ } else {
+ this.getCraftPlayer().removeChannel(channel);
+ }
+ }
+
+ public final boolean isDisconnected() { + public final boolean isDisconnected() {
+ return (!this.player.joining && !this.connection.isConnected()) || this.processedDisconnect; // Paper - Fix duplication bugs + return (!this.player.joining && !this.connection.isConnected()) || this.processedDisconnect; // Paper - Fix duplication bugs
+ } + }

View File

@@ -11,6 +11,7 @@ public interface DisconnectionReason {
DisconnectionReason RESOURCE_PACK_REJECTION = game(PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION); DisconnectionReason RESOURCE_PACK_REJECTION = game(PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION);
DisconnectionReason INVALID_COOKIE = game(PlayerKickEvent.Cause.INVALID_COOKIE); DisconnectionReason INVALID_COOKIE = game(PlayerKickEvent.Cause.INVALID_COOKIE);
DisconnectionReason DUPLICATE_LOGIN_MESSAGE = game(PlayerKickEvent.Cause.DUPLICATE_LOGIN); DisconnectionReason DUPLICATE_LOGIN_MESSAGE = game(PlayerKickEvent.Cause.DUPLICATE_LOGIN);
DisconnectionReason INVALID_PAYLOAD = game(PlayerKickEvent.Cause.INVALID_PAYLOAD);
Optional<PlayerKickEvent.Cause> game(); Optional<PlayerKickEvent.Cause> game();

View File

@@ -1,7 +1,5 @@
package io.papermc.paper.connection; package io.papermc.paper.connection;
import com.destroystokyo.paper.ClientOption;
import com.destroystokyo.paper.PaperSkinParts;
import com.destroystokyo.paper.profile.CraftPlayerProfile; import com.destroystokyo.paper.profile.CraftPlayerProfile;
import com.destroystokyo.paper.profile.PlayerProfile; import com.destroystokyo.paper.profile.PlayerProfile;
import io.papermc.paper.adventure.PaperAdventure; import io.papermc.paper.adventure.PaperAdventure;
@@ -19,11 +17,13 @@ import net.minecraft.server.network.ConfigurationTask;
import net.minecraft.server.network.ServerConfigurationPacketListenerImpl; import net.minecraft.server.network.ServerConfigurationPacketListenerImpl;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
public class PaperPlayerConfigurationConnection extends PaperCommonConnection<ServerConfigurationPacketListenerImpl> implements PlayerConfigurationConnection, Audience { public class PaperPlayerConfigurationConnection extends PaperCommonConnection<ServerConfigurationPacketListenerImpl> implements PlayerConfigurationConnection, Audience, PluginMessageBridgeImpl {
private @Nullable Pointers adventurePointers; private @Nullable Pointers adventurePointers;
@@ -101,4 +101,9 @@ public class PaperPlayerConfigurationConnection extends PaperCommonConnection<Se
this.handle.returnToWorld(); this.handle.returnToWorld();
} }
@Override
public Set<String> channels() {
return this.handle.pluginMessagerChannels;
}
} }

View File

@@ -0,0 +1,43 @@
package io.papermc.paper.connection;
import com.google.common.base.Preconditions;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.event.player.PlayerRegisterChannelEvent;
import org.bukkit.event.player.PlayerUnregisterChannelEvent;
import org.bukkit.plugin.messaging.StandardMessenger;
import org.jspecify.annotations.NullMarked;
import java.util.Set;
@NullMarked
public interface PluginMessageBridgeImpl {
boolean DISABLE_CHANNEL_LIMIT = System.getProperty("paper.disableChannelLimit") != null; // Paper - add a flag to disable the channel limit
default boolean addChannel(String channel) {
Preconditions.checkState(DISABLE_CHANNEL_LIMIT || this.channels().size() < 128, "Cannot register channel. Too many channels registered!"); // Paper - flag to disable channel limit
channel = StandardMessenger.validateAndCorrectChannel(channel);
if (channels().add(channel)) {
if (this instanceof CraftPlayer player) {
Bukkit.getPluginManager().callEvent(new PlayerRegisterChannelEvent(player, channel));
}
return true;
}
return false;
}
default boolean removeChannel(String channel) {
channel = StandardMessenger.validateAndCorrectChannel(channel);
if (channels().remove(channel)) {
if (this instanceof CraftPlayer player) {
Bukkit.getPluginManager().callEvent(new PlayerUnregisterChannelEvent(player, channel));
}
return true;
}
return false;
};
Set<String> channels();
}

View File

@@ -8,8 +8,8 @@ import com.google.common.io.BaseEncoding;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import io.papermc.paper.FeatureHooks; import io.papermc.paper.FeatureHooks;
import io.papermc.paper.configuration.GlobalConfiguration;
import io.papermc.paper.connection.PlayerGameConnection; import io.papermc.paper.connection.PlayerGameConnection;
import io.papermc.paper.connection.PluginMessageBridgeImpl;
import io.papermc.paper.entity.LookAnchor; import io.papermc.paper.entity.LookAnchor;
import io.papermc.paper.entity.PaperPlayerGiveResult; import io.papermc.paper.entity.PaperPlayerGiveResult;
import io.papermc.paper.entity.PlayerGiveResult; import io.papermc.paper.entity.PlayerGiveResult;
@@ -33,11 +33,9 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.WeakHashMap; import java.util.WeakHashMap;
@@ -54,20 +52,13 @@ import net.minecraft.commands.Commands;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.ConnectionProtocol;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.PlayerChatMessage; import net.minecraft.network.chat.PlayerChatMessage;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.common.ClientboundResourcePackPopPacket; import net.minecraft.network.protocol.common.ClientboundResourcePackPopPacket;
import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket; import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket;
import net.minecraft.network.protocol.common.ClientboundServerLinksPacket; import net.minecraft.network.protocol.common.ClientboundServerLinksPacket;
import net.minecraft.network.protocol.common.ClientboundStoreCookiePacket;
import net.minecraft.network.protocol.common.ClientboundTransferPacket;
import net.minecraft.network.protocol.common.custom.DiscardedPayload; import net.minecraft.network.protocol.common.custom.DiscardedPayload;
import net.minecraft.network.protocol.cookie.ClientboundCookieRequestPacket;
import net.minecraft.network.protocol.cookie.ServerboundCookieResponsePacket;
import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket; import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
@@ -196,10 +187,8 @@ import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerExpCooldownChangeEvent; import org.bukkit.event.player.PlayerExpCooldownChangeEvent;
import org.bukkit.event.player.PlayerHideEntityEvent; import org.bukkit.event.player.PlayerHideEntityEvent;
import org.bukkit.event.player.PlayerRegisterChannelEvent;
import org.bukkit.event.player.PlayerShowEntityEvent; import org.bukkit.event.player.PlayerShowEntityEvent;
import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.player.PlayerUnregisterChannelEvent;
import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.InventoryView.Property; import org.bukkit.inventory.InventoryView.Property;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@@ -215,7 +204,7 @@ import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
@DelegateDeserialization(CraftOfflinePlayer.class) @DelegateDeserialization(CraftOfflinePlayer.class)
public class CraftPlayer extends CraftHumanEntity implements Player { public class CraftPlayer extends CraftHumanEntity implements Player, PluginMessageBridgeImpl {
private static final PointersSupplier<Player> POINTERS_SUPPLIER = PointersSupplier.<Player>builder() private static final PointersSupplier<Player> POINTERS_SUPPLIER = PointersSupplier.<Player>builder()
.parent(CraftEntity.POINTERS_SUPPLIER) .parent(CraftEntity.POINTERS_SUPPLIER)
.resolving(Identity.NAME, Player::getName) .resolving(Identity.NAME, Player::getName)
@@ -227,7 +216,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
private long lastPlayed = 0; private long lastPlayed = 0;
private boolean hasPlayedBefore = false; private boolean hasPlayedBefore = false;
private final ConversationTracker conversationTracker = new ConversationTracker(); private final ConversationTracker conversationTracker = new ConversationTracker();
private final Set<String> channels = new HashSet<String>();
private final Map<UUID, Set<WeakReference<Plugin>>> invertedVisibilityEntities = new HashMap<>(); private final Map<UUID, Set<WeakReference<Plugin>>> invertedVisibilityEntities = new HashMap<>();
private final Set<UUID> unlistedEntities = new HashSet<>(); // Paper - Add Listing API for Player private final Set<UUID> unlistedEntities = new HashSet<>(); // Paper - Add Listing API for Player
private static final WeakHashMap<Plugin, WeakReference<Plugin>> pluginWeakReferences = new WeakHashMap<>(); private static final WeakHashMap<Plugin, WeakReference<Plugin>> pluginWeakReferences = new WeakHashMap<>();
@@ -238,7 +226,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
private CraftWorldBorder clientWorldBorder = null; private CraftWorldBorder clientWorldBorder = null;
private BorderChangeListener clientWorldBorderListener = this.createWorldBorderListener(); private BorderChangeListener clientWorldBorderListener = this.createWorldBorderListener();
public org.bukkit.event.player.PlayerResourcePackStatusEvent.Status resourcePackStatus; // Paper - more resource pack API public org.bukkit.event.player.PlayerResourcePackStatusEvent.Status resourcePackStatus; // Paper - more resource pack API
private static final boolean DISABLE_CHANNEL_LIMIT = System.getProperty("paper.disableChannelLimit") != null; // Paper - add a flag to disable the channel limit
private long lastSaveTime; // Paper - getLastPlayed replacement API private long lastSaveTime; // Paper - getLastPlayed replacement API
public CraftPlayer(CraftServer server, ServerPlayer entity) { public CraftPlayer(CraftServer server, ServerPlayer entity) {
@@ -2378,7 +2365,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
StandardMessenger.validatePluginMessage(this.server.getMessenger(), source, channel, message); StandardMessenger.validatePluginMessage(this.server.getMessenger(), source, channel, message);
if (this.getHandle().connection == null) return; if (this.getHandle().connection == null) return;
if (this.channels.contains(channel)) { if (this.channels().contains(channel)) {
ResourceLocation id = ResourceLocation.parse(StandardMessenger.validateAndCorrectChannel(channel)); ResourceLocation id = ResourceLocation.parse(StandardMessenger.validateAndCorrectChannel(channel));
this.sendCustomPayload(id, message); this.sendCustomPayload(id, message);
} }
@@ -2530,24 +2517,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
this.getHandle().connection.send(resourcePackPushPacket); this.getHandle().connection.send(resourcePackPushPacket);
} }
public void addChannel(String channel) { @Override
Preconditions.checkState(DISABLE_CHANNEL_LIMIT || this.channels.size() < 128, "Cannot register channel. Too many channels registered!"); // Paper - flag to disable channel limit public Set<String> channels() {
channel = StandardMessenger.validateAndCorrectChannel(channel); if (this.getHandle().connection == null) return new HashSet<>();
if (this.channels.add(channel)) {
this.server.getPluginManager().callEvent(new PlayerRegisterChannelEvent(this, channel));
}
}
public void removeChannel(String channel) { return this.getHandle().connection.pluginMessagerChannels;
channel = StandardMessenger.validateAndCorrectChannel(channel);
if (this.channels.remove(channel)) {
this.server.getPluginManager().callEvent(new PlayerUnregisterChannelEvent(this, channel));
}
} }
@Override @Override
public Set<String> getListeningPluginChannels() { public Set<String> getListeningPluginChannels() {
return ImmutableSet.copyOf(this.channels); return ImmutableSet.copyOf(this.channels());
} }
public void sendSupportedChannels() { public void sendSupportedChannels() {