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