diff --git a/api/src/main/java/com/velocitypowered/api/proxy/Player.java b/api/src/main/java/com/velocitypowered/api/proxy/Player.java index dae239b1f..feaadfa61 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -234,6 +234,11 @@ public interface Player extends * Gets the {@link ResourcePackInfo} of the currently applied * resource-pack or null if none. * + *

Note that since 1.20.3 it is no longer recommended to use + * this method as it will only return the last applied + * resource pack. To get all applied resource packs, use + * {@link #getAppliedResourcePacks()} instead.

+ * * @return the applied resource pack or null if none. */ @Nullable @@ -245,6 +250,11 @@ public interface Player extends * the user is currently downloading or is currently * prompted to install or null if none. * + *

Note that since 1.20.3 it is no longer recommended to use + * this method as it will only return the last pending + * resource pack. To get all pending resource packs, use + * {@link #getPendingResourcePacks()} instead.

+ * * @return the pending resource pack or null if none */ @Nullable diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index df12320b5..b4953f1cd 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -64,6 +64,7 @@ import com.velocitypowered.proxy.protocol.packet.DisconnectPacket; import com.velocitypowered.proxy.protocol.packet.HeaderAndFooterPacket; 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.chat.ChatQueue; import com.velocitypowered.proxy.protocol.packet.chat.ChatType; @@ -84,6 +85,8 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import java.net.InetSocketAddress; import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -101,6 +104,9 @@ import net.kyori.adventure.permission.PermissionChecker; import net.kyori.adventure.platform.facet.FacetPointers; import net.kyori.adventure.platform.facet.FacetPointers.Type; import net.kyori.adventure.pointer.Pointers; +import net.kyori.adventure.resource.ResourcePackInfoLike; +import net.kyori.adventure.resource.ResourcePackRequest; +import net.kyori.adventure.resource.ResourcePackRequestLike; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.logger.slf4j.ComponentLogger; @@ -153,6 +159,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, private final Queue outstandingResourcePacks = new ArrayDeque<>(); private @Nullable ResourcePackInfo pendingResourcePack; private @Nullable ResourcePackInfo appliedResourcePack; + private @NotNull List pendingResourcePacks = new ArrayList<>(); + private @NotNull List appliedResourcePacks = new ArrayList<>(); private final @NotNull Pointers pointers = Player.super.pointers().toBuilder().withDynamic(Identity.UUID, this::getUniqueId) .withDynamic(Identity.NAME, this::getUsername) @@ -974,6 +982,44 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, } } + @Override + public void clearResourcePacks() { + if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { + connection.write(new RemoveResourcePackPacket()); + } + } + + @Override + public void removeResourcePacks(@NotNull UUID id, @NotNull UUID @NotNull ... others) { + if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { + connection.write(new RemoveResourcePackPacket(id)); + for (final UUID other : others) { + connection.write(new RemoveResourcePackPacket(other)); + } + } + } + + @Override + public void removeResourcePacks(@NotNull ResourcePackRequest request) { + for (final net.kyori.adventure.resource.ResourcePackInfo resourcePackInfo : request.packs()) { + removeResourcePacks(resourcePackInfo.id()); + } + } + + @Override + public void removeResourcePacks(@NotNull ResourcePackRequestLike request) { + removeResourcePacks(request.asResourcePackRequest()); + } + + @Override + @SuppressWarnings("checkstyle:linelength") + public void removeResourcePacks(@NotNull ResourcePackInfoLike request, @NotNull ResourcePackInfoLike @NotNull ... others) { + removeResourcePacks(request.asResourcePackInfo().id()); + for (final ResourcePackInfoLike other : others) { + removeResourcePacks(other.asResourcePackInfo().id()); + } + } + /** * Queues a resource-pack for sending to the player and sends it immediately if the queue is * empty. @@ -1027,25 +1073,23 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, @Override @Deprecated public @Nullable ResourcePackInfo getAppliedResourcePack() { - //TODO which resource pack should be returned here? return appliedResourcePack; } @Override @Deprecated public @Nullable ResourcePackInfo getPendingResourcePack() { - //TODO which resource pack should be returned here? return pendingResourcePack; } @Override public Collection getAppliedResourcePacks() { - return Collections.EMPTY_LIST; //TODO + return new ArrayList<>(appliedResourcePacks); } @Override public Collection getPendingResourcePacks() { - return Collections.EMPTY_LIST; //TODO + return new ArrayList<>(pendingResourcePacks); } /** @@ -1079,6 +1123,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, case ACCEPTED: previousResourceResponse = true; pendingResourcePack = queued; + if (this.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_3)) { + pendingResourcePacks.clear(); + } + pendingResourcePacks.add(queued); break; case DECLINED: previousResourceResponse = false; @@ -1086,9 +1134,41 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, case SUCCESSFUL: appliedResourcePack = queued; pendingResourcePack = null; + appliedResourcePacks.add(queued); + if (this.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_3)) { + pendingResourcePacks.clear(); + } + if (queued != null) { + pendingResourcePacks.removeIf(resourcePackInfo -> { + if (resourcePackInfo.getId() == null) { + return resourcePackInfo.getUrl().equals(queued.getUrl()) + && Arrays.equals(resourcePackInfo.getHash(), queued.getHash()); + } + return resourcePackInfo.getId().equals(queued.getId()); + }); + } break; case FAILED_DOWNLOAD: pendingResourcePack = null; + if (this.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_3)) { + pendingResourcePacks.clear(); + } + if (queued != null) { + pendingResourcePacks.removeIf(resourcePackInfo -> { + if (resourcePackInfo.getId() == null) { + return resourcePackInfo.getUrl().equals(queued.getUrl()) + && Arrays.equals(resourcePackInfo.getHash(), queued.getHash()); + } + return resourcePackInfo.getId().equals(queued.getId()); + }); + } + break; + case DISCARDED: + if (queued != null && queued.getId() != null) { + appliedResourcePacks.removeIf(resourcePackInfo -> { + return queued.getId().equals(resourcePackInfo.getId()); + }); + } break; default: break; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackRequestPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackRequestPacket.java index 8b8a56f6e..8f6cfe6e3 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackRequestPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackRequestPacket.java @@ -146,7 +146,12 @@ public class ResourcePackRequestPacket implements MinecraftPacket { @Override public String toString() { - return "ResourcePackRequest{" + "url='" + url + '\'' + ", hash='" + hash + '\'' + - ", isRequired=" + isRequired + ", prompt='" + prompt + '\'' + '}'; + return "ResourcePackRequestPacket{" + + "id=" + id + + ", url='" + url + '\'' + + ", hash='" + hash + '\'' + + ", isRequired=" + isRequired + + ", prompt=" + prompt + + '}'; } } \ No newline at end of file diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackResponsePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackResponsePacket.java index e5b0183ea..4ef08a009 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackResponsePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ResourcePackResponsePacket.java @@ -83,6 +83,10 @@ public class ResourcePackResponsePacket implements MinecraftPacket { @Override public String toString() { - return "ResourcePackResponse{" + "hash=" + hash + ", " + "status=" + status + '}'; + return "ResourcePackResponsePacket{" + + "id=" + id + + ", hash='" + hash + '\'' + + ", status=" + status + + '}'; } } \ No newline at end of file