diff --git a/api/src/main/java/com/velocitypowered/api/event/player/ServerResourcePackRemoveEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/ServerResourcePackRemoveEvent.java new file mode 100644 index 000000000..96d1bb8e5 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/event/player/ServerResourcePackRemoveEvent.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2018-2023 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.event.ResultedEvent; +import com.velocitypowered.api.event.annotation.AwaitingEvent; +import com.velocitypowered.api.proxy.ServerConnection; +import java.util.UUID; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * This event is fired when the downstream server tries to remove a resource pack from player + * or clear all of them. The proxy will wait on this event to finish before forwarding the + * action to the user. If this event is denied, no resource packs will be removed from player. + */ +@AwaitingEvent +public class ServerResourcePackRemoveEvent implements ResultedEvent { + + private GenericResult result; + private final @MonotonicNonNull UUID packId; + private final ServerConnection serverConnection; + + /** + * Instantiates this event. + */ + public ServerResourcePackRemoveEvent(UUID packId, ServerConnection serverConnection) { + this.result = ResultedEvent.GenericResult.allowed(); + this.packId = packId; + this.serverConnection = serverConnection; + } + + /** + * Returns the id of the resource pack, if it's null all the resource packs + * from player will be cleared. + * + * @return the id + */ + @Nullable + public UUID getPackId() { + return packId; + } + + /** + * Returns the server that tries to remove a resource pack from player or clear all of them. + * + * @return the server connection + */ + public ServerConnection getServerConnection() { + return serverConnection; + } + + @Override + public GenericResult getResult() { + return this.result; + } + + @Override + public void setResult(GenericResult result) { + this.result = Preconditions.checkNotNull(result, "result"); + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java index 14989b1a3..128d5c370 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java @@ -29,6 +29,7 @@ import com.velocitypowered.api.event.connection.PreTransferEvent; import com.velocitypowered.api.event.player.CookieRequestEvent; import com.velocitypowered.api.event.player.CookieStoreEvent; import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent; +import com.velocitypowered.api.event.player.ServerResourcePackRemoveEvent; import com.velocitypowered.api.event.player.ServerResourcePackSendEvent; import com.velocitypowered.api.event.proxy.ProxyPingEvent; import com.velocitypowered.api.network.ProtocolVersion; @@ -258,14 +259,26 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { @Override public boolean handle(RemoveResourcePackPacket packet) { - final ConnectedPlayer player = serverConn.getPlayer(); - final ResourcePackHandler handler = player.resourcePackHandler(); - if (packet.getId() != null) { - handler.remove(packet.getId()); - } else { - handler.clearAppliedResourcePacks(); - } - playerConnection.write(packet); + final ServerResourcePackRemoveEvent event = new ServerResourcePackRemoveEvent( + packet.getId(), this.serverConn); + server.getEventManager().fire(event).thenAcceptAsync(serverResourcePackRemoveEvent -> { + if (playerConnection.isClosed()) { + return; + } + if (serverResourcePackRemoveEvent.getResult().isAllowed()) { + final ConnectedPlayer player = serverConn.getPlayer(); + final ResourcePackHandler handler = player.resourcePackHandler(); + if (packet.getId() != null) { + handler.remove(packet.getId()); + } else { + handler.clearAppliedResourcePacks(); + } + playerConnection.write(packet); + } + }, playerConnection.eventLoop()).exceptionally((ex) -> { + logger.error("Exception while handling resource pack remove for {}", playerConnection, ex); + return null; + }); return true; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java index a19854eb9..74f0576c1 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java @@ -21,6 +21,7 @@ import com.velocitypowered.api.event.connection.PreTransferEvent; import com.velocitypowered.api.event.player.CookieRequestEvent; import com.velocitypowered.api.event.player.CookieStoreEvent; import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent; +import com.velocitypowered.api.event.player.ServerResourcePackRemoveEvent; import com.velocitypowered.api.event.player.ServerResourcePackSendEvent; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.player.ResourcePackInfo; @@ -30,6 +31,7 @@ import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.client.ClientConfigSessionHandler; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo; +import com.velocitypowered.proxy.connection.player.resourcepack.handler.ResourcePackHandler; import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl; @@ -41,6 +43,7 @@ import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket; import com.velocitypowered.proxy.protocol.packet.DisconnectPacket; import com.velocitypowered.proxy.protocol.packet.KeepAlivePacket; import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket; +import com.velocitypowered.proxy.protocol.packet.RemoveResourcePackPacket; import com.velocitypowered.proxy.protocol.packet.ResourcePackRequestPacket; import com.velocitypowered.proxy.protocol.packet.ResourcePackResponsePacket; import com.velocitypowered.proxy.protocol.packet.TransferPacket; @@ -192,6 +195,33 @@ public class ConfigSessionHandler implements MinecraftSessionHandler { return true; } + @Override + public boolean handle(RemoveResourcePackPacket packet) { + final MinecraftConnection playerConnection = this.serverConn.getPlayer().getConnection(); + + final ServerResourcePackRemoveEvent event = new ServerResourcePackRemoveEvent( + packet.getId(), this.serverConn); + server.getEventManager().fire(event).thenAcceptAsync(serverResourcePackRemoveEvent -> { + if (playerConnection.isClosed()) { + return; + } + if (serverResourcePackRemoveEvent.getResult().isAllowed()) { + final ConnectedPlayer player = serverConn.getPlayer(); + final ResourcePackHandler handler = player.resourcePackHandler(); + if (packet.getId() != null) { + handler.remove(packet.getId()); + } else { + handler.clearAppliedResourcePacks(); + } + playerConnection.write(packet); + } + }, playerConnection.eventLoop()).exceptionally((ex) -> { + logger.error("Exception while handling resource pack remove for {}", playerConnection, ex); + return null; + }); + return true; + } + @Override public boolean handle(FinishedUpdatePacket packet) { final MinecraftConnection smc = serverConn.ensureConnected();