From 91da7e6f250bb706267c5bb7a08e61714d778621 Mon Sep 17 00:00:00 2001 From: aromaa Date: Sat, 18 May 2024 17:53:55 +0300 Subject: [PATCH] Use Adventure ResourcePackInfo --- SpongeAPI | 2 +- .../ClientboundResourcePackPacketBridge.java | 6 +- .../bridge/server/MinecraftServerBridge.java | 4 +- ...ServerCommonPacketListenerImplBridge.java} | 47 ++---- .../ServerGamePacketListenerImplBridge.java | 6 - .../phase/packet/BasicPacketContext.java | 3 - .../tracking/phase/packet/PacketPhase.java | 4 +- .../packet/player/ResourcePackState.java | 93 ------------ .../registry/SpongeFactoryProvider.java | 3 - .../resourcepack/SpongeResourcePack.java | 136 ------------------ .../resourcepack/SpongeWorldResourcePack.java | 77 ---------- .../server/MinecraftServerMixin_API.java | 4 +- .../server/level/ServerPlayerMixin_API.java | 28 ++-- ...lientboundResourcePackPushPacketMixin.java | 26 ++-- .../core/server/MinecraftServerMixin.java | 6 +- .../dedicated/DedicatedServerMixin.java | 13 +- .../ServerCommonPacketListenerImplMixin.java | 122 ++++++++++++++-- .../ServerGamePacketListenerImplMixin.java | 20 --- .../test/resourcepack/ResourcePackTest.java | 128 +++++++++++++++++ .../resources/META-INF/sponge_plugins.json | 6 + 20 files changed, 304 insertions(+), 430 deletions(-) rename src/main/java/org/spongepowered/common/{resourcepack/SpongeURIResourcePack.java => bridge/server/network/ServerCommonPacketListenerImplBridge.java} (52%) delete mode 100644 src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/ResourcePackState.java delete mode 100644 src/main/java/org/spongepowered/common/resourcepack/SpongeResourcePack.java delete mode 100644 src/main/java/org/spongepowered/common/resourcepack/SpongeWorldResourcePack.java create mode 100644 testplugins/src/main/java/org/spongepowered/test/resourcepack/ResourcePackTest.java diff --git a/SpongeAPI b/SpongeAPI index aca1deab771..a6f2cfdba45 160000 --- a/SpongeAPI +++ b/SpongeAPI @@ -1 +1 @@ -Subproject commit aca1deab7718573d0585f1ca833056d8a0d9b94f +Subproject commit a6f2cfdba45835126f70d9b2b70153d0960e3803 diff --git a/src/main/java/org/spongepowered/common/bridge/network/protocol/game/ClientboundResourcePackPacketBridge.java b/src/main/java/org/spongepowered/common/bridge/network/protocol/game/ClientboundResourcePackPacketBridge.java index 0d40eab041b..c321781cd4c 100644 --- a/src/main/java/org/spongepowered/common/bridge/network/protocol/game/ClientboundResourcePackPacketBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/network/protocol/game/ClientboundResourcePackPacketBridge.java @@ -24,11 +24,11 @@ */ package org.spongepowered.common.bridge.network.protocol.game; -import org.spongepowered.api.resourcepack.ResourcePack; +import net.kyori.adventure.resource.ResourcePackInfo; public interface ClientboundResourcePackPacketBridge { - ResourcePack bridge$getSpongePack(); + void bridge$setPackInfo(ResourcePackInfo resourcePackInfo); - void bridge$setSpongePack(ResourcePack pack); + ResourcePackInfo bridge$getPackInfo(); } diff --git a/src/main/java/org/spongepowered/common/bridge/server/MinecraftServerBridge.java b/src/main/java/org/spongepowered/common/bridge/server/MinecraftServerBridge.java index 73c7fa07593..4720b5b2612 100644 --- a/src/main/java/org/spongepowered/common/bridge/server/MinecraftServerBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/server/MinecraftServerBridge.java @@ -25,12 +25,12 @@ package org.spongepowered.common.bridge.server; import com.google.inject.Injector; +import net.kyori.adventure.resource.ResourcePackRequest; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.thread.BlockableEventLoop; import net.minecraft.world.Difficulty; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.Game; -import org.spongepowered.api.resourcepack.ResourcePack; import org.spongepowered.common.service.server.SpongeServerScopedServiceProvider; import org.spongepowered.common.user.SpongeUserManager; @@ -40,7 +40,7 @@ public interface MinecraftServerBridge { SpongeServerScopedServiceProvider bridge$getServiceProvider(); - @Nullable ResourcePack bridge$getResourcePack(); + @Nullable ResourcePackRequest bridge$getResourcePack(); void bridge$setDifficulty(ServerLevel world, Difficulty newDifficulty, boolean forceDifficulty); diff --git a/src/main/java/org/spongepowered/common/resourcepack/SpongeURIResourcePack.java b/src/main/java/org/spongepowered/common/bridge/server/network/ServerCommonPacketListenerImplBridge.java similarity index 52% rename from src/main/java/org/spongepowered/common/resourcepack/SpongeURIResourcePack.java rename to src/main/java/org/spongepowered/common/bridge/server/network/ServerCommonPacketListenerImplBridge.java index cd35a2b6639..0755e812e49 100644 --- a/src/main/java/org/spongepowered/common/resourcepack/SpongeURIResourcePack.java +++ b/src/main/java/org/spongepowered/common/bridge/server/network/ServerCommonPacketListenerImplBridge.java @@ -22,49 +22,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.resourcepack; +package org.spongepowered.common.bridge.server.network; -import net.kyori.adventure.text.Component; -import org.checkerframework.checker.nullness.qual.Nullable; +import net.kyori.adventure.resource.ResourcePackRequest; +import org.checkerframework.checker.nullness.qual.NonNull; -import java.net.URI; -import java.net.URISyntaxException; +import java.util.UUID; +public interface ServerCommonPacketListenerImplBridge { -public final class SpongeURIResourcePack extends SpongeResourcePack { + void bridge$sendResourcePacks(final @NonNull ResourcePackRequest request); - private final URI uri; - private final String name; - - public SpongeURIResourcePack(final URI uri, final @Nullable String hash, final Component component) { - super(hash, component); - this.uri = uri; - this.name = this.getName0(); - } - - public SpongeURIResourcePack(final String uri, final @Nullable String hash, final Component component) throws URISyntaxException { - this(new URI(uri), hash, component); - } - - private String getName0() { - String name = this.uri.getPath(); - name = name.substring(name.lastIndexOf("/") + 1); - return name.replaceAll("\\W", ""); - } - - @Override - public String getUrlString() { - return this.uri.toString(); - } - - @Override - public String name() { - return this.name; - } - - @Override - public URI uri() { - return this.uri; - } + void bridge$removeResourcePacks(final @NonNull UUID id, final @NonNull UUID @NonNull... others); + void bridge$clearResourcePacks(); } diff --git a/src/main/java/org/spongepowered/common/bridge/server/network/ServerGamePacketListenerImplBridge.java b/src/main/java/org/spongepowered/common/bridge/server/network/ServerGamePacketListenerImplBridge.java index 1214eea9a4e..bdde2cec02c 100644 --- a/src/main/java/org/spongepowered/common/bridge/server/network/ServerGamePacketListenerImplBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/server/network/ServerGamePacketListenerImplBridge.java @@ -24,16 +24,10 @@ */ package org.spongepowered.common.bridge.server.network; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.resourcepack.ResourcePack; import org.spongepowered.api.world.server.ServerLocation; public interface ServerGamePacketListenerImplBridge { - @Nullable ResourcePack bridge$popReceivedResourcePack(boolean markAccepted); - - @Nullable ResourcePack bridge$popAcceptedResourcePack(); - void bridge$setLastMoveLocation(ServerLocation location); long bridge$getLastTryBlockPacketTimeStamp(); diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/BasicPacketContext.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/BasicPacketContext.java index 60c4ba2f271..38db83cc709 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/BasicPacketContext.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/BasicPacketContext.java @@ -35,9 +35,6 @@ public BasicPacketContext(final PacketState state, final Pha @SuppressWarnings("unchecked") @Override public boolean hasCaptures() { - if (this.state == PacketPhase.General.RESOURCE_PACK) { - return true; - } if (this.state == PacketPhase.General.CLOSE_WINDOW) { return true; } diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhase.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhase.java index a26df3b2a65..74d5759cbef 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhase.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhase.java @@ -92,7 +92,6 @@ import org.spongepowered.common.event.tracking.phase.packet.player.PacketCommandState; import org.spongepowered.common.event.tracking.phase.packet.player.PlaceBlockPacketState; import org.spongepowered.common.event.tracking.phase.packet.player.PlayerCommandPhaseContext; -import org.spongepowered.common.event.tracking.phase.packet.player.ResourcePackState; import org.spongepowered.common.event.tracking.phase.packet.player.StopSleepingPacketState; import org.spongepowered.common.event.tracking.phase.packet.player.UnknownPacketState; import org.spongepowered.common.event.tracking.phase.packet.player.UseItemPacketState; @@ -130,7 +129,6 @@ public static final class General { static final IPhaseState TAB_COMPLETE = new BasicPacketState(); public static final IPhaseState CLOSE_WINDOW = new CloseWindowState(); public static final IPhaseState UPDATE_SIGN = new BasicPacketState(); - static final IPhaseState RESOURCE_PACK = new ResourcePackState(); static final IPhaseState STOP_RIDING_JUMP = new BasicPacketState(); static final IPhaseState HANDLED_EXTERNALLY = new UnknownPacketState(); static final IPhaseState START_FALL_FLYING = new BasicPacketState(); @@ -333,7 +331,7 @@ private void setupPacketToStateMapping() { }); this.packetTranslationMap.put(ServerboundCustomPayloadPacket.class, packet -> PacketPhase.General.HANDLED_EXTERNALLY); this.packetTranslationMap.put(ServerboundTeleportToEntityPacket.class, packet -> PacketPhase.General.IGNORED); - this.packetTranslationMap.put(ServerboundResourcePackPacket.class, packet -> PacketPhase.General.RESOURCE_PACK); + this.packetTranslationMap.put(ServerboundResourcePackPacket.class, packet -> PacketPhase.General.HANDLED_EXTERNALLY); this.packetTranslationMap.put(ServerboundPlaceRecipePacket.class, packet -> PacketPhase.Inventory.PLACE_RECIPE); } diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/ResourcePackState.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/ResourcePackState.java deleted file mode 100644 index abff0641f67..00000000000 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/player/ResourcePackState.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.event.tracking.phase.packet.player; - -import net.minecraft.network.protocol.common.ServerboundResourcePackPacket; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.entity.living.player.server.ServerPlayer; -import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.event.entity.living.player.ResourcePackStatusEvent; -import org.spongepowered.api.resourcepack.ResourcePack; -import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.bridge.server.network.ServerGamePacketListenerImplBridge; -import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.event.tracking.phase.packet.BasicPacketContext; -import org.spongepowered.common.event.tracking.phase.packet.BasicPacketState; - -public final class ResourcePackState extends BasicPacketState { - - @Override - public void unwind(final BasicPacketContext phaseContext) { - final net.minecraft.server.level.ServerPlayer player = phaseContext.getPacketPlayer(); - - final ServerGamePacketListenerImpl connection = player.connection; - final ServerGamePacketListenerImplBridge mixinHandler = (ServerGamePacketListenerImplBridge) connection; - final ServerboundResourcePackPacket resource = phaseContext.getPacket(); - final ResourcePackStatusEvent.ResourcePackStatus status; - final @Nullable ResourcePack pack; - switch (resource.action()) { - case ACCEPTED: - pack = mixinHandler.bridge$popReceivedResourcePack(true); - status = ResourcePackStatusEvent.ResourcePackStatus.ACCEPTED; - break; - case DECLINED: - pack = mixinHandler.bridge$popReceivedResourcePack(false); - status = ResourcePackStatusEvent.ResourcePackStatus.DECLINED; - break; - case SUCCESSFULLY_LOADED: - pack = mixinHandler.bridge$popAcceptedResourcePack(); - status = ResourcePackStatusEvent.ResourcePackStatus.SUCCESSFULLY_LOADED; - break; - case FAILED_DOWNLOAD: - pack = mixinHandler.bridge$popAcceptedResourcePack(); - status = ResourcePackStatusEvent.ResourcePackStatus.FAILED; - break; - case DOWNLOADED: - case INVALID_URL: - case FAILED_RELOAD: - case DISCARDED: - return; //TODO: Update API - default: - throw new AssertionError(); - } - - if (pack == null) { - return; - } - - SpongeCommon.post(SpongeEventFactory.createResourcePackStatusEvent( - PhaseTracker.getCauseStackManager().currentCause(), - pack, - (ServerPlayer) player, - status - )); - } - - @Override - protected boolean alwaysUnwinds() { - return true; - } -} diff --git a/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java b/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java index d6bc190b919..90bb93d8ce6 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java @@ -62,7 +62,6 @@ import org.spongepowered.api.resource.ResourcePath; import org.spongepowered.api.resource.pack.PackStatus; import org.spongepowered.api.resource.pack.PackType; -import org.spongepowered.api.resourcepack.ResourcePack; import org.spongepowered.api.scoreboard.ScoreFormat; import org.spongepowered.api.scoreboard.displayslot.DisplaySlot; import org.spongepowered.api.service.permission.NodeTree; @@ -131,7 +130,6 @@ import org.spongepowered.common.resource.SpongeResourcePath; import org.spongepowered.common.resource.pack.SpongePackStatusFactory; import org.spongepowered.common.resource.pack.SpongePackTypeFactory; -import org.spongepowered.common.resourcepack.SpongeResourcePack; import org.spongepowered.common.scoreboard.SpongeDisplaySlotFactory; import org.spongepowered.common.scoreboard.SpongeScoreFormatFactory; import org.spongepowered.common.service.server.permission.SpongeNodeTree; @@ -214,7 +212,6 @@ public void registerDefaultFactories() { .registerFactory(CommandTreeNode.NodeFactory.class, new SpongeCommandTreeBuilderFactory()) .registerFactory(ItemStackSnapshot.Factory.class, () -> SpongeItemStackSnapshot.EMPTY) .registerFactory(Parameter.Value.Factory.class, new SpongeParameterFactory()) - .registerFactory(ResourcePack.Factory.class, new SpongeResourcePack.Factory()) .registerFactory(ServerLocation.Factory.class, new SpongeServerLocation.Factory()) .registerFactory(SpongeComponents.Factory.class, new SpongeAdventure.Factory()) .registerFactory(Transform.Factory.class, new SpongeTransform.Factory()) diff --git a/src/main/java/org/spongepowered/common/resourcepack/SpongeResourcePack.java b/src/main/java/org/spongepowered/common/resourcepack/SpongeResourcePack.java deleted file mode 100644 index 2d59bce65be..00000000000 --- a/src/main/java/org/spongepowered/common/resourcepack/SpongeResourcePack.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.resourcepack; - -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; -import net.kyori.adventure.text.Component; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.resourcepack.ResourcePack; -import org.spongepowered.common.SpongeCommon; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.util.Objects; -import java.util.Optional; -import java.util.StringJoiner; -import java.util.UUID; - -public abstract class SpongeResourcePack implements ResourcePack { - - private final String hash; - private final Component prompt; - private final String id = UUID.randomUUID().toString(); - - public static final int HASH_SIZE = 40; - - public SpongeResourcePack(@Nullable String hash, Component prompt) { - this.hash = hash; - this.prompt = prompt; - } - - public abstract String getUrlString(); - - @Override - public String id() { - return this.id; - } - - @Override - public Optional hash() { - return Optional.of(this.hash); - } - - @Override - public Component prompt() { - return this.prompt; - } - - @Override - public String toString() { - return new StringJoiner(", ", SpongeResourcePack.class.getSimpleName() + "[", "]") - .add("id=" + this.id()) - .add("uri=" + this.uri()).toString(); - } - - public static SpongeResourcePack create(String uri, String hash, Component component) throws URISyntaxException { - if (uri.startsWith(SpongeWorldResourcePack.LEVEL_PACK_PROTOCOL)) { - return new SpongeWorldResourcePack(uri, hash, component); - } - if (hash != null && hash.length() != SpongeResourcePack.HASH_SIZE) { - hash = null; - } - return new SpongeURIResourcePack(uri, hash, component); - } - - public static SpongeResourcePack create(URI uri, String hash, Component component) { - if (uri.toString().startsWith(SpongeWorldResourcePack.LEVEL_PACK_PROTOCOL)) { - return new SpongeWorldResourcePack(uri, hash, component); - } - return new SpongeURIResourcePack(uri, hash, component); - } - - public static final class Factory implements ResourcePack.Factory { - @Override - public ResourcePack fromUri(final URI uri) throws FileNotFoundException { - Objects.requireNonNull(uri); - try { - Hasher hasher = Hashing.sha1().newHasher(); - try (InputStream in = org.spongepowered.common.resourcepack.SpongeResourcePack.Factory.openStream(uri)) { - byte[] buf = new byte[256]; - while (true) { - int read = in.read(buf); - if (read <= 0) { - break; - } - hasher.putBytes(buf, 0, read); - } - } - return SpongeResourcePack.create(uri, hasher.hash().toString(), Component.empty()); - } catch (final IOException e) { - FileNotFoundException ex = new FileNotFoundException(e.toString()); - ex.initCause(e); - throw ex; - } - } - - private static InputStream openStream(final URI uri) throws IOException { - if (uri.toString().startsWith(SpongeWorldResourcePack.LEVEL_PACK_PROTOCOL)) { - return Files.newInputStream(SpongeCommon.gameDirectory().resolve(uri.toString().substring(SpongeWorldResourcePack.LEVEL_PACK_PROTOCOL.length - ()))); - } - return uri.toURL().openStream(); - } - - @Override - public ResourcePack fromUriUnchecked(final URI uri) { - return SpongeResourcePack.create(Objects.requireNonNull(uri), null, Component.empty()); - } - } -} diff --git a/src/main/java/org/spongepowered/common/resourcepack/SpongeWorldResourcePack.java b/src/main/java/org/spongepowered/common/resourcepack/SpongeWorldResourcePack.java deleted file mode 100644 index fa04a4eec26..00000000000 --- a/src/main/java/org/spongepowered/common/resourcepack/SpongeWorldResourcePack.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.common.resourcepack; - -import net.kyori.adventure.text.Component; -import org.checkerframework.checker.nullness.qual.Nullable; - -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URLDecoder; -import java.net.URLEncoder; - -public final class SpongeWorldResourcePack extends SpongeResourcePack { - - private final String path; - private final URI uri; - public static final String LEVEL_PACK_PROTOCOL = "level://"; - - public SpongeWorldResourcePack(final String levelUri, final @Nullable String hash, final Component component) { - super(hash, component); - this.path = levelUri.substring(SpongeWorldResourcePack.LEVEL_PACK_PROTOCOL.length()); - try { - this.uri = URI.create(SpongeWorldResourcePack.LEVEL_PACK_PROTOCOL + URLEncoder.encode(this.path, "UTF-8")); - } catch (final UnsupportedEncodingException e) { - throw new AssertionError(e); - } - } - - public SpongeWorldResourcePack(final URI levelUri, final @Nullable String hash, final Component component) { - super(hash, component); - final String path = levelUri.toString().substring(SpongeWorldResourcePack.LEVEL_PACK_PROTOCOL.length()); - try { - this.path = URLDecoder.decode(path, "UTF-8"); - } catch (final UnsupportedEncodingException e) { - throw new AssertionError(e); - } - this.uri = levelUri; - } - - @Override - public String name() { - return "resourceszip"; - } - - @Override - public String getUrlString() { - return SpongeWorldResourcePack.LEVEL_PACK_PROTOCOL + this.path; - } - - @Override - public URI uri() { - return this.uri; - } - -} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/MinecraftServerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/MinecraftServerMixin_API.java index 4443e82cdbd..1f2070c6da1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/MinecraftServerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/MinecraftServerMixin_API.java @@ -30,6 +30,7 @@ import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.resource.ResourcePackRequest; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.minecraft.commands.Commands; @@ -59,7 +60,6 @@ import org.spongepowered.api.map.MapStorage; import org.spongepowered.api.profile.GameProfileManager; import org.spongepowered.api.resource.ResourceManager; -import org.spongepowered.api.resourcepack.ResourcePack; import org.spongepowered.api.scoreboard.Scoreboard; import org.spongepowered.api.service.ServiceProvider; import org.spongepowered.api.util.Ticks; @@ -392,7 +392,7 @@ public SpongeCommandManager commandManager() { return ((CommandsBridge) this.shadow$getCommands()).bridge$commandManager(); } - public Optional server$resourcePack() { + public Optional server$resourcePack() { return Optional.ofNullable(((MinecraftServerBridge) this).bridge$getResourcePack()); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ServerPlayerMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ServerPlayerMixin_API.java index 77818ea5475..b0a12a1592b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ServerPlayerMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/server/level/ServerPlayerMixin_API.java @@ -31,6 +31,7 @@ import net.kyori.adventure.inventory.Book; import net.kyori.adventure.permission.PermissionChecker; import net.kyori.adventure.pointer.Pointers; +import net.kyori.adventure.resource.ResourcePackRequest; import net.kyori.adventure.sound.Sound; import net.kyori.adventure.sound.SoundStop; import net.kyori.adventure.text.Component; @@ -46,7 +47,6 @@ import net.minecraft.network.chat.MessageSignature; import net.minecraft.network.chat.PlayerChatMessage; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket; import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; import net.minecraft.network.protocol.game.ClientboundClearTitlesPacket; import net.minecraft.network.protocol.game.ClientboundDeleteChatPacket; @@ -88,7 +88,6 @@ import org.spongepowered.api.event.world.ChangeWorldBorderEvent; import org.spongepowered.api.network.ServerSideConnection; import org.spongepowered.api.profile.GameProfile; -import org.spongepowered.api.resourcepack.ResourcePack; import org.spongepowered.api.scoreboard.Scoreboard; import org.spongepowered.api.world.WorldType; import org.spongepowered.api.world.border.WorldBorder; @@ -107,6 +106,7 @@ import org.spongepowered.common.bridge.server.PlayerAdvancementsBridge; import org.spongepowered.common.bridge.server.ServerScoreboardBridge; import org.spongepowered.common.bridge.server.level.ServerPlayerBridge; +import org.spongepowered.common.bridge.server.network.ServerCommonPacketListenerImplBridge; import org.spongepowered.common.bridge.world.level.border.WorldBorderBridge; import org.spongepowered.common.effect.particle.SpongeParticleHelper; import org.spongepowered.common.effect.record.SpongeMusicDisc; @@ -115,13 +115,11 @@ import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.mixin.api.minecraft.world.entity.player.PlayerMixin_API; import org.spongepowered.common.profile.SpongeGameProfile; -import org.spongepowered.common.resourcepack.SpongeResourcePack; import org.spongepowered.common.util.BookUtil; import org.spongepowered.common.util.NetworkUtil; import org.spongepowered.math.vector.Vector3d; import org.spongepowered.math.vector.Vector3i; -import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; @@ -273,13 +271,6 @@ public void stopMusicDisc(final Vector3i position) { this.connection.send(SpongeMusicDisc.createPacket(position, null)); } - @Override - public void sendResourcePack(final ResourcePack pack) { - Objects.requireNonNull(pack, "pack"); - final UUID uuid = UUID.nameUUIDFromBytes(pack.name().getBytes(StandardCharsets.UTF_8)); - this.connection.send(new ClientboundResourcePackPushPacket(uuid, ((SpongeResourcePack) pack).getUrlString(), pack.hash().orElse(""), false, Optional.ofNullable(SpongeAdventure.asVanilla(pack.prompt())))); - } - @Override public TabList tabList() { return this.api$tabList; @@ -660,4 +651,19 @@ public void openBook(@NonNull final Book book) { private int api$durationToTicks(final Duration duration) { return (int) (duration.toMillis() / 50L); } + + @Override + public void sendResourcePacks(final @NonNull ResourcePackRequest request) { + ((ServerCommonPacketListenerImplBridge) this.connection).bridge$sendResourcePacks(request); + } + + @Override + public void removeResourcePacks(final @NonNull UUID id, final @NonNull UUID @NonNull... others) { + ((ServerCommonPacketListenerImplBridge) this.connection).bridge$removeResourcePacks(id, others); + } + + @Override + public void clearResourcePacks() { + ((ServerCommonPacketListenerImplBridge) this.connection).bridge$clearResourcePacks(); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/network/protocol/game/ClientboundResourcePackPushPacketMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/network/protocol/game/ClientboundResourcePackPushPacketMixin.java index c98e203daaa..5e59b3acff5 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/network/protocol/game/ClientboundResourcePackPushPacketMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/network/protocol/game/ClientboundResourcePackPushPacketMixin.java @@ -24,18 +24,16 @@ */ package org.spongepowered.common.mixin.core.network.protocol.game; -import net.minecraft.network.chat.Component; +import net.kyori.adventure.resource.ResourcePackInfo; +import net.kyori.adventure.text.Component; import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket; -import org.spongepowered.api.resourcepack.ResourcePack; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.network.protocol.game.ClientboundResourcePackPacketBridge; -import org.spongepowered.common.resourcepack.SpongeResourcePack; +import java.net.URI; import java.net.URISyntaxException; import java.util.Optional; import java.util.UUID; @@ -43,30 +41,24 @@ @Mixin(ClientboundResourcePackPushPacket.class) public abstract class ClientboundResourcePackPushPacketMixin implements ClientboundResourcePackPacketBridge { - @Shadow private String url; - @Shadow private String hash; + private ResourcePackInfo impl$pack; - private ResourcePack impl$pack; - - @Inject(method = "", at = @At("RETURN") , remap = false) + @Inject(method = "", at = @At("RETURN")) private void impl$setResourcePackOrThrowException(final UUID uuid, final String url, final String hash, final boolean required, final Optional prompt, final CallbackInfo ci) { try { - this.impl$pack = SpongeResourcePack.create(url, hash, prompt.isEmpty() ? net.kyori.adventure.text.Component.empty() : SpongeAdventure.asAdventure(prompt)); + this.impl$pack = ResourcePackInfo.resourcePackInfo(uuid, new URI(url), hash); } catch (final URISyntaxException e) { throw new IllegalArgumentException(e); } } @Override - public void bridge$setSpongePack(final ResourcePack pack) { - this.impl$pack = pack; - this.url = ((SpongeResourcePack) pack).getUrlString(); - this.hash = pack.hash().orElse(""); + public void bridge$setPackInfo(final ResourcePackInfo resourcePackInfo) { + this.impl$pack = resourcePackInfo; } @Override - public ResourcePack bridge$getSpongePack() { + public ResourcePackInfo bridge$getPackInfo() { return this.impl$pack; } - } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/MinecraftServerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/MinecraftServerMixin.java index c8cf8d080eb..6f836199e1d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/MinecraftServerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/MinecraftServerMixin.java @@ -25,6 +25,7 @@ package org.spongepowered.common.mixin.core.server; import com.google.inject.Injector; +import net.kyori.adventure.resource.ResourcePackRequest; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.RegistryAccess; import net.minecraft.network.chat.ChatDecorator; @@ -52,7 +53,6 @@ import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.world.UnloadWorldEvent; -import org.spongepowered.api.resourcepack.ResourcePack; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.service.permission.SubjectProxy; import org.spongepowered.api.world.SerializationBehavior; @@ -120,7 +120,7 @@ public abstract class MinecraftServerMixin implements SpongeServer, MinecraftSer private final ChatDecorator impl$spongeDecorator = new SpongeChatDecorator(); private @Nullable SpongeServerScopedServiceProvider impl$serviceProvider; - protected @Nullable ResourcePack impl$resourcePack; + protected @Nullable ResourcePackRequest impl$resourcePack; private final BlockableEventLoop impl$spongeMainThreadExecutor = new BlockableEventLoop<>("Sponge") { //Used to schedule internal Sponge tasks to the main thread @@ -161,7 +161,7 @@ public Subject subject() { } @Override - public ResourcePack bridge$getResourcePack() { + public ResourcePackRequest bridge$getResourcePack() { return this.impl$resourcePack; } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/DedicatedServerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/DedicatedServerMixin.java index cc83694188b..f7d9271dd55 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/DedicatedServerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/DedicatedServerMixin.java @@ -25,7 +25,8 @@ package org.spongepowered.common.mixin.core.server.dedicated; import com.mojang.datafixers.DataFixer; -import net.kyori.adventure.text.Component; +import net.kyori.adventure.resource.ResourcePackInfo; +import net.kyori.adventure.resource.ResourcePackRequest; import net.minecraft.server.Services; import net.minecraft.server.WorldStem; import net.minecraft.server.dedicated.DedicatedServer; @@ -40,13 +41,15 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.bridge.server.players.GameProfileCacheBridge; import org.spongepowered.common.datapack.SpongeDataPackManager; import org.spongepowered.common.launch.Launch; import org.spongepowered.common.mixin.core.server.MinecraftServerMixin; -import org.spongepowered.common.resourcepack.SpongeResourcePack; +import java.net.URI; import java.net.URISyntaxException; +import java.util.Optional; @Mixin(DedicatedServer.class) public abstract class DedicatedServerMixin extends MinecraftServerMixin { @@ -56,7 +59,11 @@ public abstract class DedicatedServerMixin extends MinecraftServerMixin { DedicatedServerSettings $$4, DataFixer $$5, Services $$6, ChunkProgressListenerFactory $$7, CallbackInfo ci) { $$4.getProperties().serverResourcePackInfo.ifPresent(packInfo -> { try { - this.impl$resourcePack = SpongeResourcePack.create(packInfo.url(), packInfo.hash(), Component.empty()); + this.impl$resourcePack = ResourcePackRequest.resourcePackRequest() + .packs(ResourcePackInfo.resourcePackInfo(packInfo.id(), new URI(packInfo.url()), packInfo.hash())) + .required(packInfo.isRequired()) + .prompt(SpongeAdventure.asAdventure(Optional.ofNullable(packInfo.prompt()))) + .build(); } catch (final URISyntaxException e) { e.printStackTrace(); } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerCommonPacketListenerImplMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerCommonPacketListenerImplMixin.java index e924d962776..5ff0b5245b1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerCommonPacketListenerImplMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerCommonPacketListenerImplMixin.java @@ -24,36 +24,62 @@ */ package org.spongepowered.common.mixin.core.server.network; +import com.mojang.authlib.GameProfile; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.resource.ResourcePackCallback; +import net.kyori.adventure.resource.ResourcePackInfo; +import net.kyori.adventure.resource.ResourcePackRequest; +import net.kyori.adventure.resource.ResourcePackStatus; import net.minecraft.network.Connection; import net.minecraft.network.PacketSendListener; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.common.ClientboundResourcePackPopPacket; import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket; import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; +import net.minecraft.network.protocol.common.ServerboundResourcePackPacket; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerCommonPacketListenerImpl; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.resourcepack.ResourcePack; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.network.ServerSideConnection; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.SpongeCommon; +import org.spongepowered.common.adventure.SpongeAdventure; +import org.spongepowered.common.bridge.network.ConnectionBridge; import org.spongepowered.common.bridge.network.protocol.game.ClientboundResourcePackPacketBridge; +import org.spongepowered.common.bridge.server.network.ServerCommonPacketListenerImplBridge; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.profile.SpongeGameProfile; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; @Mixin(ServerCommonPacketListenerImpl.class) -public abstract class ServerCommonPacketListenerImplMixin { +public abstract class ServerCommonPacketListenerImplMixin implements ServerCommonPacketListenerImplBridge { // @formatter:off @Shadow @Final protected Connection connection; @Shadow @Final protected MinecraftServer server; - @Shadow public abstract void shadow$send(final Packet $$0, @Nullable final PacketSendListener $$1); + @Shadow public abstract void shadow$send(final Packet $$0); @Shadow public abstract void shadow$disconnect(Component reason); @Shadow public void shadow$handleCustomPayload(final ServerboundCustomPayloadPacket $$0) { } + @Shadow protected abstract GameProfile shadow$playerProfile(); // @formatter:on - @Nullable public ResourcePack impl$lastReceivedPack; + private Map impl$resourcePackInfos = new ConcurrentHashMap<>(); + private Map impl$resourcePackCallbacks = new ConcurrentHashMap<>(); @Inject( method = "send(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketSendListener;)V", @@ -61,13 +87,93 @@ public abstract class ServerCommonPacketListenerImplMixin { ) private void impl$onClientboundPacketSend(final Packet packet, final PacketSendListener listener, final CallbackInfo ci) { this.impl$modifyClientBoundPacket(packet); - } public void impl$modifyClientBoundPacket(final Packet packet) { - if (packet instanceof ClientboundResourcePackPushPacket) { - final ResourcePack pack = ((ClientboundResourcePackPacketBridge) packet).bridge$getSpongePack(); - this.impl$lastReceivedPack = pack; + if (packet instanceof ClientboundResourcePackPushPacket packPacket) { + this.impl$resourcePackInfos.put(packPacket.id(), ((ClientboundResourcePackPacketBridge) (Object) packPacket).bridge$getPackInfo()); + } + } + + @Inject(method = "handleResourcePackResponse", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/util/thread/BlockableEventLoop;)V", + shift = At.Shift.AFTER)) + private void impl$onHandleResourcePackResponse(final ServerboundResourcePackPacket $$0, final CallbackInfo ci) { + final @Nullable ResourcePackInfo pack = this.impl$resourcePackInfos.get($$0.id()); + if (pack == null) { + return; + } + + final @Nullable ServerPlayer player; + if ((Object) this instanceof ServerGamePacketListenerImpl gamePacketListener) { + player = gamePacketListener.player; + } else { + player = null; + } + + final ResourcePackStatus status = switch ($$0.action()) { + case ACCEPTED -> ResourcePackStatus.ACCEPTED; + case DECLINED -> ResourcePackStatus.DECLINED; + case INVALID_URL -> ResourcePackStatus.INVALID_URL; + case FAILED_DOWNLOAD -> ResourcePackStatus.FAILED_DOWNLOAD; + case DOWNLOADED -> ResourcePackStatus.DOWNLOADED; + case FAILED_RELOAD -> ResourcePackStatus.FAILED_RELOAD; + case DISCARDED -> ResourcePackStatus.DISCARDED; + case SUCCESSFULLY_LOADED -> ResourcePackStatus.SUCCESSFULLY_LOADED; + default -> throw new AssertionError(); + }; + + final @Nullable ResourcePackCallback callback = this.impl$resourcePackCallbacks.get($$0.id()); + if (callback != null && player != null) { + callback.packEventReceived($$0.id(), status, (Audience) player); + } + + if ($$0.action().isTerminal()) { + this.impl$resourcePackInfos.remove($$0.id()); + this.impl$resourcePackCallbacks.remove($$0.id()); + } + + SpongeCommon.post(SpongeEventFactory.createResourcePackStatusEvent( + PhaseTracker.getCauseStackManager().currentCause(), + (ServerSideConnection) ((ConnectionBridge) this.connection).bridge$getEngineConnection(), + pack, + Optional.ofNullable((org.spongepowered.api.entity.living.player.server.ServerPlayer) player), + SpongeGameProfile.of(this.shadow$playerProfile()), + status + )); + } + + @Override + public void bridge$sendResourcePacks(final @NonNull ResourcePackRequest request) { + Objects.requireNonNull(request, "request"); + if (request.replace()) { + this.bridge$clearResourcePacks(); + } + final Optional prompt = SpongeAdventure.asVanillaOpt(request.prompt()); + for (final ResourcePackInfo pack : request.packs()) { + final ClientboundResourcePackPushPacket packet = + new ClientboundResourcePackPushPacket(pack.id(), pack.uri().toASCIIString(), pack.hash(), request.required(), prompt); + ((ClientboundResourcePackPacketBridge) (Object) packet).bridge$setPackInfo(pack); + if (request.callback() != ResourcePackCallback.noOp()) { + this.impl$resourcePackCallbacks.put(pack.id(), request.callback()); + } + this.shadow$send(packet); } } + + @Override + public void bridge$removeResourcePacks(final @NonNull UUID id, final @NonNull UUID @NonNull... others) { + Objects.requireNonNull(id, "id"); + Objects.requireNonNull(id, "others"); + this.shadow$send(new ClientboundResourcePackPopPacket(Optional.of(id))); + for (final UUID other : others) { + this.shadow$send(new ClientboundResourcePackPopPacket(Optional.of(other))); + } + } + + @Override + public void bridge$clearResourcePacks() { + this.shadow$send(new ClientboundResourcePackPopPacket(Optional.empty())); + } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImplMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImplMixin.java index 3c8af3b358b..2fdd8d028fc 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImplMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImplMixin.java @@ -82,7 +82,6 @@ import org.spongepowered.api.event.cause.entity.MovementTypes; import org.spongepowered.api.event.entity.living.AnimateHandEvent; import org.spongepowered.api.event.network.ServerSideConnectionEvent; -import org.spongepowered.api.resourcepack.ResourcePack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -135,8 +134,6 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack @Shadow protected abstract ParseResults shadow$parseCommand(final String $$0); // @formatter:on - @Nullable private ResourcePack impl$lastAcceptedPack; - private int impl$ignorePackets; @Override @@ -522,21 +519,4 @@ public abstract class ServerGamePacketListenerImplMixin extends ServerCommonPack this.shadow$performSignedChatCommand($$0, $$1); } } - - @Override - public @Nullable ResourcePack bridge$popReceivedResourcePack(final boolean markAccepted) { - final ResourcePack pack = this.impl$lastReceivedPack; - this.impl$lastReceivedPack = null; - if (markAccepted) { - this.impl$lastAcceptedPack = pack; - } - return pack; - } - - @Override - public @Nullable ResourcePack bridge$popAcceptedResourcePack() { - final ResourcePack pack = this.impl$lastAcceptedPack; - this.impl$lastAcceptedPack = null; - return pack; - } } diff --git a/testplugins/src/main/java/org/spongepowered/test/resourcepack/ResourcePackTest.java b/testplugins/src/main/java/org/spongepowered/test/resourcepack/ResourcePackTest.java new file mode 100644 index 00000000000..e8f1a552cbb --- /dev/null +++ b/testplugins/src/main/java/org/spongepowered/test/resourcepack/ResourcePackTest.java @@ -0,0 +1,128 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.test.resourcepack; + +import com.google.inject.Inject; +import net.kyori.adventure.resource.ResourcePackInfo; +import net.kyori.adventure.resource.ResourcePackRequest; +import net.kyori.adventure.text.Component; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.command.Command; +import org.spongepowered.api.command.CommandResult; +import org.spongepowered.api.command.parameter.CommandContext; +import org.spongepowered.api.command.parameter.Parameter; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.entity.living.player.ResourcePackStatusEvent; +import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.builtin.jvm.Plugin; +import org.spongepowered.test.LoadableModule; + +import java.net.URISyntaxException; +import java.net.URL; +import java.util.List; +import java.util.UUID; + +@Plugin("resourcepacktests") +public final class ResourcePackTest implements LoadableModule { + + private final PluginContainer pluginContainer; + + @Inject + public ResourcePackTest(final PluginContainer pluginContainer) { + this.pluginContainer = pluginContainer; + } + + @Override + public void enable(final CommandContext ctx) { + Sponge.game().eventManager().registerListeners(this.pluginContainer, new Listeners()); + } + + @Listener + private void registerCommands(final RegisterCommandEvent event) { + final Parameter.Value uuidParameter = Parameter.uuid().key("uuid").build(); + final Parameter.Value urlParameter = Parameter.url().key("url").build(); + final Parameter.Value hashParameter = Parameter.string().key("hash").build(); + final Parameter.Value requiredParameter = Parameter.bool().key("required").optional().build(); + final Parameter.Value replaceParameter = Parameter.bool().key("replace").optional().build(); + final Parameter.Value promptParameter = Parameter.formattingCodeTextOfRemainingElements().key("prompt").optional().build(); + + event.register(this.pluginContainer, Command.builder() + .addParameter(uuidParameter) + .addParameter(urlParameter) + .addParameter(hashParameter) + .addParameter(requiredParameter) + .addParameter(replaceParameter) + .addParameter(promptParameter) + .executor(ctx -> { + final UUID uuid = ctx.requireOne(uuidParameter); + final URL url = ctx.requireOne(urlParameter); + final String hash = ctx.requireOne(hashParameter); + final Boolean required = ctx.one(requiredParameter).orElse(false); + final Boolean replace = ctx.one(replaceParameter).orElse(false); + final @Nullable Component prompt = ctx.one(promptParameter).orElse(null); + final ServerPlayer player = ctx.cause().first(ServerPlayer.class).get(); + try { + player.sendResourcePacks(ResourcePackRequest.resourcePackRequest() + .packs(ResourcePackInfo.resourcePackInfo(uuid, url.toURI(), hash)) + .required(required) + .replace(replace) + .prompt(prompt) + .callback((id, status, audience) -> + Sponge.systemSubject().sendMessage(Component.text("ResourcePackCallback: Received resource pack status " + status + " for " + id))) + .build()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + return CommandResult.success(); + }).build(), "sendResourcePack"); + + event.register(this.pluginContainer, Command.builder() + .addParameter(uuidParameter) + .executor(ctx -> { + final UUID uuid = ctx.requireOne(uuidParameter); + final ServerPlayer player = ctx.cause().first(ServerPlayer.class).get(); + player.removeResourcePacks(List.of(uuid)); + return CommandResult.success(); + }).build(), "removeResourcePack"); + + event.register(this.pluginContainer, Command.builder() + .executor(ctx -> { + final ServerPlayer player = ctx.cause().first(ServerPlayer.class).get(); + player.clearResourcePacks(); + return CommandResult.success(); + }).build(), "clearResourcePacks"); + } + + private static final class Listeners { + + @Listener + private void onResourcePack(final ResourcePackStatusEvent event) { + Sponge.systemSubject().sendMessage(Component.text("ResourcePackStatusEvent: Received resource pack status " + event.status() + " for " + event.pack().id() + " from " + event.profile())); + } + } +} diff --git a/testplugins/src/main/resources/META-INF/sponge_plugins.json b/testplugins/src/main/resources/META-INF/sponge_plugins.json index 16966540939..1e06d977256 100644 --- a/testplugins/src/main/resources/META-INF/sponge_plugins.json +++ b/testplugins/src/main/resources/META-INF/sponge_plugins.json @@ -321,6 +321,12 @@ "name": "Connection Tests", "entrypoint": "org.spongepowered.test.connection.ConnectionTests", "description": "Connection Tests" + }, + { + "id": "resourcepacktests", + "name": "Resource Pack Tests", + "entrypoint": "org.spongepowered.test.resourcepack.ResourcePackTest", + "description": "Resource Pack Tests" } ] }