feat: conform to Adventure resource pack API, remove Minestom resource pack types

This commit is contained in:
mworzala 2024-01-06 03:20:32 -05:00 committed by Matt Worzala
parent e24cb62583
commit 40ac94d092
9 changed files with 98 additions and 120 deletions

View File

@ -1,5 +1,7 @@
package net.minestom.demo;
import net.kyori.adventure.resource.ResourcePackInfo;
import net.kyori.adventure.resource.ResourcePackRequest;
import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer;
import net.minestom.server.adventure.MinestomAdventure;
@ -18,7 +20,10 @@ import net.minestom.server.event.item.ItemDropEvent;
import net.minestom.server.event.item.PickupItemEvent;
import net.minestom.server.event.player.*;
import net.minestom.server.event.server.ServerTickMonitorEvent;
import net.minestom.server.instance.*;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceContainer;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.instance.LightingChunk;
import net.minestom.server.instance.block.Block;
import net.minestom.server.inventory.Inventory;
import net.minestom.server.inventory.InventoryType;
@ -32,8 +37,11 @@ import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.time.TimeUnit;
import net.minestom.server.world.DimensionType;
import java.net.URI;
import java.time.Duration;
import java.util.*;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicReference;
@ -122,10 +130,10 @@ public class PlayerInit {
event.getPlayer().sendMessage("MESSAGE " + ThreadLocalRandom.current().nextDouble());
if ("false".equals(block.getProperty("waterlogged")) && itemStack.material().equals(Material.WATER_BUCKET)) {
if ("false" .equals(block.getProperty("waterlogged")) && itemStack.material().equals(Material.WATER_BUCKET)) {
block = block.withProperty("waterlogged", "true");
System.out.println("SET WATERLOGGER");
} else if ("true".equals(block.getProperty("waterlogged")) && itemStack.material().equals(Material.BUCKET)) {
} else if ("true" .equals(block.getProperty("waterlogged")) && itemStack.material().equals(Material.BUCKET)) {
block = block.withProperty("waterlogged", "false");
System.out.println("SET NOT WATERLOGGED");
} else return;

View File

@ -8,6 +8,9 @@ import net.kyori.adventure.identity.Identified;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.inventory.Book;
import net.kyori.adventure.pointer.Pointers;
import net.kyori.adventure.resource.ResourcePackCallback;
import net.kyori.adventure.resource.ResourcePackRequest;
import net.kyori.adventure.resource.ResourcePackStatus;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.sound.SoundStop;
import net.kyori.adventure.text.Component;
@ -57,10 +60,7 @@ import net.minestom.server.network.PlayerProvider;
import net.minestom.server.network.packet.client.ClientPacket;
import net.minestom.server.network.packet.server.SendablePacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.common.DisconnectPacket;
import net.minestom.server.network.packet.server.common.KeepAlivePacket;
import net.minestom.server.network.packet.server.common.PluginMessagePacket;
import net.minestom.server.network.packet.server.common.ResourcePackPushPacket;
import net.minestom.server.network.packet.server.common.*;
import net.minestom.server.network.packet.server.login.LoginDisconnectPacket;
import net.minestom.server.network.packet.server.play.*;
import net.minestom.server.network.packet.server.play.data.DeathLocation;
@ -69,7 +69,6 @@ import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.network.player.PlayerSocketConnection;
import net.minestom.server.recipe.Recipe;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.resourcepack.ResourcePack;
import net.minestom.server.scoreboard.BelowNameTag;
import net.minestom.server.scoreboard.Team;
import net.minestom.server.snapshot.EntitySnapshot;
@ -234,6 +233,11 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
private Identity identity;
private final Pointers pointers;
// Resource packs
private final Map<UUID, ResourcePackCallback> resourcePackCallbacks = new HashMap<>();
// The future is non-null when a resource pack is in-flight, and completed when all statuses have been received.
private CompletableFuture<Void> resourcePackFuture = null;
public Player(@NotNull UUID uuid, @NotNull String username, @NotNull PlayerConnection playerConnection) {
super(EntityType.PLAYER, uuid);
this.username = username;
@ -1269,13 +1273,56 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
return !itemDropEvent.isCancelled();
}
@Override
public void sendResourcePacks(@NotNull ResourcePackRequest request) {
if (request.replace()) clearResourcePacks();
for (var pack : request.packs()) {
sendPacket(new ResourcePackPushPacket(pack, request.required(), request.prompt()));
resourcePackCallbacks.put(pack.id(), request.callback());
if (resourcePackFuture == null) {
resourcePackFuture = new CompletableFuture<>();
}
}
}
@Override
public void removeResourcePacks(@NotNull UUID id, @NotNull UUID @NotNull ... others) {
sendPacket(new ResourcePackPopPacket(id));
for (var other : others) {
sendPacket(new ResourcePackPopPacket(other));
}
}
@Override
public void clearResourcePacks() {
sendPacket(new ResourcePackPopPacket((UUID) null));
}
/**
* Sets the player resource pack.
*
* @param resourcePack the resource pack
* If there are resource packs in-flight, a future is returned which will be completed when
* all resource packs have been responded to by the client. Otherwise null is returned.
*/
public void setResourcePack(@NotNull ResourcePack resourcePack) {
sendPacket(new ResourcePackPushPacket(resourcePack));
@ApiStatus.Internal
public @Nullable CompletableFuture<Void> getResourcePackFuture() {
return resourcePackFuture;
}
@ApiStatus.Internal
public void onResourcePackStatus(@NotNull UUID id, @NotNull ResourcePackStatus status) {
var callback = resourcePackCallbacks.get(id);
if (callback == null) return;
callback.packEventReceived(id, status, this);
if (!status.intermediate()) {
// Remove the callback and finish the future if relevant
resourcePackCallbacks.remove(id);
if (resourcePackCallbacks.isEmpty() && resourcePackFuture != null) {
resourcePackFuture.complete(null);
resourcePackFuture = null;
}
}
}
/**
@ -2133,7 +2180,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
@Override
public @NotNull HoverEvent<ShowEntity> asHoverEvent(@NotNull UnaryOperator<ShowEntity> op) {
return HoverEvent.showEntity(ShowEntity.of(EntityType.PLAYER, this.uuid, this.displayName));
return HoverEvent.showEntity(ShowEntity.showEntity(EntityType.PLAYER, this.uuid, this.displayName));
}
/**

View File

@ -1,8 +1,8 @@
package net.minestom.server.event.player;
import net.kyori.adventure.resource.ResourcePackStatus;
import net.minestom.server.entity.Player;
import net.minestom.server.event.trait.PlayerEvent;
import net.minestom.server.resourcepack.ResourcePackStatus;
import org.jetbrains.annotations.NotNull;
/**

View File

@ -9,5 +9,8 @@ public class ResourcePackListener {
public static void listener(ClientResourcePackStatusPacket packet, Player player) {
EventDispatcher.call(new PlayerResourcePackStatusEvent(player, packet.status()));
// Run adventure callbacks for the resource pack
player.onResourcePackStatus(packet.id(), packet.status());
}
}

View File

@ -272,6 +272,10 @@ public final class ConnectionManager {
player.sendPacket(TagsPacket.DEFAULT_TAGS);
}
// Wait for pending resource packs if any
var packFuture = player.getResourcePackFuture();
if (packFuture != null) packFuture.join();
player.setPendingInstance(spawningInstance);
player.sendPacket(new FinishConfigurationPacket());
});

View File

@ -1,8 +1,8 @@
package net.minestom.server.network.packet.client.common;
import net.kyori.adventure.resource.ResourcePackStatus;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.client.ClientPacket;
import net.minestom.server.resourcepack.ResourcePackStatus;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
@ -12,7 +12,7 @@ public record ClientResourcePackStatusPacket(
@NotNull ResourcePackStatus status
) implements ClientPacket {
public ClientResourcePackStatusPacket(@NotNull NetworkBuffer reader) {
this(reader.read(NetworkBuffer.UUID), reader.readEnum(ResourcePackStatus.class));
this(reader.read(NetworkBuffer.UUID), readStatus(reader));
}
@Override
@ -20,4 +20,19 @@ public record ClientResourcePackStatusPacket(
writer.write(NetworkBuffer.UUID, id);
writer.writeEnum(ResourcePackStatus.class, status);
}
private static @NotNull ResourcePackStatus readStatus(@NotNull NetworkBuffer reader) {
var ordinal = reader.read(NetworkBuffer.VAR_INT);
return switch (ordinal) {
case 0 -> ResourcePackStatus.SUCCESSFULLY_LOADED;
case 1 -> ResourcePackStatus.DECLINED;
case 2 -> ResourcePackStatus.FAILED_DOWNLOAD;
case 3 -> ResourcePackStatus.ACCEPTED;
case 4 -> ResourcePackStatus.DOWNLOADED;
case 5 -> ResourcePackStatus.INVALID_URL;
case 6 -> ResourcePackStatus.FAILED_RELOAD;
case 7 -> ResourcePackStatus.DISCARDED;
default -> throw new IllegalStateException("Unexpected resource pack status: " + ordinal);
};
}
}

View File

@ -1,12 +1,12 @@
package net.minestom.server.network.packet.server.common;
import net.kyori.adventure.resource.ResourcePackInfo;
import net.kyori.adventure.text.Component;
import net.minestom.server.network.ConnectionState;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.resourcepack.ResourcePack;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -30,9 +30,8 @@ public record ResourcePackPushPacket(
reader.read(BOOLEAN), reader.readOptional(COMPONENT));
}
public ResourcePackPushPacket(@NotNull ResourcePack resourcePack) {
this(resourcePack.getId(), resourcePack.getUrl(), resourcePack.getHash(),
resourcePack.isForced(), resourcePack.getPrompt());
public ResourcePackPushPacket(@NotNull ResourcePackInfo resourcePackInfo, boolean required, @Nullable Component prompt) {
this(resourcePackInfo.id(), resourcePackInfo.uri().toString(), resourcePackInfo.hash(), required, prompt);
}
@Override

View File

@ -1,79 +0,0 @@
package net.minestom.server.resourcepack;
import net.kyori.adventure.text.Component;
import net.minestom.server.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
/**
* Represents a resource pack which can be sent with {@link Player#setResourcePack(ResourcePack)}.
*/
public class ResourcePack {
private final UUID id;
private final String url;
private final String hash;
private final boolean forced;
private final Component prompt;
private ResourcePack(@NotNull UUID id, @NotNull String url, @Nullable String hash, boolean forced, @Nullable Component prompt) {
this.id = id;
this.url = url;
// Optional, set to empty if null
this.hash = hash == null ? "" : hash;
this.forced = forced;
this.prompt = prompt;
}
public static ResourcePack optional(@NotNull UUID id, @NotNull String url, @Nullable String hash, @Nullable Component prompt) {
return new ResourcePack(id, url, hash, false, prompt);
}
public static ResourcePack optional(@NotNull UUID id, @NotNull String url, @Nullable String hash) {
return optional(id, url, hash, null);
}
public static ResourcePack forced(@NotNull UUID id, @NotNull String url, @Nullable String hash, @Nullable Component prompt) {
return new ResourcePack(id, url, hash, true, prompt);
}
public static ResourcePack forced(@NotNull UUID id, @NotNull String url, @Nullable String hash) {
return forced(id, url, hash, null);
}
public @NotNull UUID getId() {
return id;
}
/**
* Gets the resource pack URL.
*
* @return the resource pack URL
*/
public @NotNull String getUrl() {
return url;
}
/**
* Gets the resource pack hash.
* <p>
* WARNING: if null or empty, the player will probably waste bandwidth by re-downloading
* the resource pack.
*
* @return the resource pack hash, can be empty
*/
public @NotNull String getHash() {
return hash;
}
public boolean isForced() {
return forced;
}
public @Nullable Component getPrompt() {
return prompt;
}
}

View File

@ -1,19 +0,0 @@
package net.minestom.server.resourcepack;
import net.minestom.server.entity.Player;
/**
* Represents the result of {@link Player#setResourcePack(ResourcePack)} in
* {@link net.minestom.server.event.player.PlayerResourcePackStatusEvent}.
*/
public enum ResourcePackStatus {
SUCCESSFULLY_LOADED,
DECLINED,
FAILED_DOWNLOAD,
ACCEPTED,
DOWNLOADED,
INVALID_URL,
FAILED_RELOAD,
DISCARDED,
}