From 75d68115effe52bb459207ce3c985e5da2502451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ZX=E5=A4=8F=E5=A4=9C=E4=B9=8B=E9=A3=8E?= Date: Mon, 10 Nov 2025 00:34:58 +0800 Subject: [PATCH] feat: PlayerChannelUnregisterEvent (#1686) * feat: PlayerChannelUnregisterEvent * style: fix checkstyle issues --- .../player/PlayerChannelUnregisterEvent.java | 44 +++++++++++++++++++ .../client/ClientPlaySessionHandler.java | 9 +++- 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 api/src/main/java/com/velocitypowered/api/event/player/PlayerChannelUnregisterEvent.java diff --git a/api/src/main/java/com/velocitypowered/api/event/player/PlayerChannelUnregisterEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/PlayerChannelUnregisterEvent.java new file mode 100644 index 000000000..86eeb2bc9 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/event/player/PlayerChannelUnregisterEvent.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 Velocity Contributors + * + * The Velocity API is licensed under the terms of the MIT License. For more details, + * reference the LICENSE file in the api top-level directory. + */ + +package com.velocitypowered.api.event.player; + +import com.google.common.base.Preconditions; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.messages.ChannelIdentifier; +import java.util.List; + +/** + * This event is fired when a client ({@link Player}) sends a plugin message through the + * unregister channel. Velocity will not wait on this event to finish firing. + */ +public final class PlayerChannelUnregisterEvent { + + private final Player player; + private final List channels; + + public PlayerChannelUnregisterEvent(Player player, List channels) { + this.player = Preconditions.checkNotNull(player, "player"); + this.channels = Preconditions.checkNotNull(channels, "channels"); + } + + public Player getPlayer() { + return player; + } + + public List getChannels() { + return channels; + } + + @Override + public String toString() { + return "PlayerChannelUnregisterEvent{" + + "player=" + player + + ", channels=" + channels + + '}'; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java index 17eb708b3..a4ddacc90 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -24,6 +24,7 @@ import com.mojang.brigadier.suggestion.Suggestion; import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.player.CookieReceiveEvent; import com.velocitypowered.api.event.player.PlayerChannelRegisterEvent; +import com.velocitypowered.api.event.player.PlayerChannelUnregisterEvent; import com.velocitypowered.api.event.player.PlayerClientBrandEvent; import com.velocitypowered.api.event.player.TabCompleteEvent; import com.velocitypowered.api.event.player.configuration.PlayerEnteredConfigurationEvent; @@ -318,8 +319,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { new PlayerChannelRegisterEvent(player, ImmutableList.copyOf(channels))); backendConn.write(packet.retain()); } else if (PluginMessageUtil.isUnregister(packet)) { - player.getClientsideChannels() - .removeAll(PluginMessageUtil.getChannels(0, packet, this.player.getProtocolVersion())); + List channels = + PluginMessageUtil.getChannels(0, packet, this.player.getProtocolVersion()); + player.getClientsideChannels().removeAll(channels); + server.getEventManager() + .fireAndForget( + new PlayerChannelUnregisterEvent(player, ImmutableList.copyOf(channels))); backendConn.write(packet.retain()); } else if (PluginMessageUtil.isMcBrand(packet)) { String brand = PluginMessageUtil.readBrandMessage(packet.content());