diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoadResourcePacksEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoadResourcePacksEvent.java index d5bcd1784..43757f916 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoadResourcePacksEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoadResourcePacksEvent.java @@ -69,7 +69,7 @@ public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent { * @return true if the resource pack was added successfully, * or false if already present */ - public abstract boolean register(@NonNull ResourcePack resourcePack, @Nullable ResourcePackOption... options); + public abstract boolean register(@NonNull ResourcePack resourcePack, @Nullable ResourcePackOption... options); /** * Sets {@link ResourcePackOption}'s for a resource pack @@ -78,7 +78,7 @@ public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent { * @param options the options to register for the pack * @throws IllegalArgumentException if the pack is not registered. */ - public abstract void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption... options); + public abstract void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption... options); /** * Returns the subpack options set for a specific resource pack uuid. @@ -87,7 +87,7 @@ public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent { * @param uuid the resourcePack for which the options are set * @return a list of {@link ResourcePackOption} */ - public abstract Collection options(UUID uuid); + public abstract Collection> options(@NonNull UUID uuid); /** * Unregisters a resource pack from being sent to the client. diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineResourcePacksEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineResourcePacksEvent.java index 2613f8c5b..68ccc1e5b 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineResourcePacksEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineResourcePacksEvent.java @@ -26,8 +26,10 @@ package org.geysermc.geyser.api.event.lifecycle; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.event.Event; import org.geysermc.geyser.api.pack.ResourcePack; +import org.geysermc.geyser.api.pack.option.ResourcePackOption; import java.util.Collection; import java.util.List; @@ -46,21 +48,33 @@ public abstract class GeyserDefineResourcePacksEvent implements Event { public abstract @NonNull List resourcePacks(); /** - * Registers a {@link ResourcePack} to be sent to clients. + * Registers a {@link ResourcePack} to be sent to the client, optionally alongside + * specific options. * - * @param resourcePack a resource pack that will be sent to clients. + * @param resourcePack a resource pack that will be sent to the client. + * @param options {@link ResourcePackOption}'s that specify how clients load the pack * @return true if the resource pack was added successfully, * or false if already present */ - public abstract boolean register(@NonNull ResourcePack resourcePack); + public abstract boolean register(@NonNull ResourcePack resourcePack, @Nullable ResourcePackOption... options); /** - * Registers a collection of {@link ResourcePack}'s to be sent to clients. + * Sets {@link ResourcePackOption}'s for a resource pack * - * @param resourcePacks a collection of resource pack's that will be sent to clients. + * @param uuid the resource pack uuid to register the options for + * @param options the options to register for the pack + * @throws IllegalArgumentException if the pack is not registered. */ - public abstract void registerAll(@NonNull Collection resourcePacks); + public abstract void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption... options); + /** + * Returns the subpack options set for a specific resource pack uuid. + * These are not modifiable. + * + * @param uuid the resourcePack for which the options are set + * @return a list of {@link ResourcePackOption} + */ + public abstract Collection> options(@NonNull UUID uuid); /** * Unregisters a {@link ResourcePack} from being sent to clients. diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java index 98b34126e..d3e494c14 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java @@ -32,12 +32,8 @@ import java.nio.file.Path; import java.util.List; /** - * Called when resource packs are loaded within Geyser. * @deprecated Use {@link GeyserDefineResourcePacksEvent} instead. - * - * @param resourcePacks a mutable list of the currently listed resource packs */ - -@Deprecated +@Deprecated(forRemoval = true) public record GeyserLoadResourcePacksEvent(@NonNull List resourcePacks) implements Event { } diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/PackCodec.java b/api/src/main/java/org/geysermc/geyser/api/pack/PackCodec.java index b09b09f89..37d66177e 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/PackCodec.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/PackCodec.java @@ -26,7 +26,6 @@ package org.geysermc.geyser.api.pack; import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.GeyserApi; import java.io.IOException; @@ -92,30 +91,18 @@ public abstract class PackCodec { * @return the new pack provider */ @NonNull - public static PackCodec path(@NonNull Path path) { + public static PathPackCodec path(@NonNull Path path) { return GeyserApi.api().provider(PathPackCodec.class, path); } /** - * Creates a new pack provider from the given url with no content key. + * Creates a new pack provider from the given url. * * @param url the url to create the pack provider from * @return the new pack provider */ @NonNull - public static PackCodec url(@NonNull String url) { - return url(url, null); - } - - /** - * Creates a new pack provider from the given url and content key. - * - * @param url the url to create the pack provider from - * @param contentKey the content key, leave empty or null if pack is not encrypted - * @return the new pack provider - */ - @NonNull - public static PackCodec url(@NonNull String url, @Nullable String contentKey) { - return GeyserApi.api().provider(UrlPackCodec.class, url, contentKey); + public static UrlPackCodec url(@NonNull String url) { + return GeyserApi.api().provider(UrlPackCodec.class, url); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/ResourcePack.java b/api/src/main/java/org/geysermc/geyser/api/pack/ResourcePack.java index f246f7c63..646c59e1d 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/ResourcePack.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/ResourcePack.java @@ -27,9 +27,7 @@ package org.geysermc.geyser.api.pack; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.GeyserApi; -import org.geysermc.geyser.api.pack.option.ResourcePackOption; -import java.util.Collection; import java.util.UUID; /** @@ -72,17 +70,6 @@ public interface ResourcePack { return manifest().header().uuid(); } - /** - * Gets the currently set default options of this resource pack. - * These can be a priority defining how the Bedrock client applies multiple packs, - * or a default subpack. - *

- * These can be overridden in the {@link org.geysermc.geyser.api.event.bedrock.SessionLoadResourcePacksEvent} - * - * @return a collection of default {@link ResourcePackOption}s - */ - Collection defaultOptions(); - /** * Creates a resource pack with the given {@link PackCodec}. * @@ -134,18 +121,6 @@ public interface ResourcePack { */ Builder contentKey(@NonNull String contentKey); - /** - * @return the current default {@link ResourcePackOption}s - */ - Collection defaultOptions(); - - /** - * Sets default options for this resource pack. - * - * @return this builder - */ - Builder defaultOptions(ResourcePackOption... defaultOptions); - /** * @return the resource pack */ diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/UrlPackCodec.java b/api/src/main/java/org/geysermc/geyser/api/pack/UrlPackCodec.java index 8f279ae0d..1f0cc0241 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/UrlPackCodec.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/UrlPackCodec.java @@ -46,13 +46,4 @@ public abstract class UrlPackCodec extends PackCodec { */ @NonNull public abstract String url(); - - /** - * If the remote pack has an encryption key, it must be specified here. - * This will return empty if none is specified. - * - * @return the encryption key of the resource pack - */ - @NonNull - public abstract String contentKey(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/option/PriorityOption.java b/api/src/main/java/org/geysermc/geyser/api/pack/option/PriorityOption.java index a889799cc..506d62b8f 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/option/PriorityOption.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/option/PriorityOption.java @@ -32,7 +32,7 @@ import org.geysermc.geyser.api.GeyserApi; * Multiple resource packs can override each other. The higher the priority, the "higher" in the stack * a pack is, and the more a pack can override other packs. */ -public interface PriorityOption extends ResourcePackOption { +public interface PriorityOption extends ResourcePackOption { PriorityOption HIGHEST = PriorityOption.priority(10); PriorityOption HIGH = PriorityOption.priority(8); @@ -40,13 +40,6 @@ public interface PriorityOption extends ResourcePackOption { PriorityOption LOW = PriorityOption.priority(3); PriorityOption LOWEST = PriorityOption.priority(0); - /** - * The priority of the resource pack - * - * @return priority - */ - double priority(); - /** * Constructs a priority option based on a value between 0 and 10 * diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/option/ResourcePackOption.java b/api/src/main/java/org/geysermc/geyser/api/pack/option/ResourcePackOption.java index b17a4d702..bedbf71ba 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/option/ResourcePackOption.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/option/ResourcePackOption.java @@ -32,13 +32,18 @@ import org.geysermc.geyser.api.pack.ResourcePack; * Represents a resource pack option that can be used to specify how a resource * pack is sent to Bedrock clients. */ -public interface ResourcePackOption { +public interface ResourcePackOption { /** * @return the option type */ @NonNull Type type(); + /** + * @return the value of the option + */ + @NonNull T value(); + /** * Used to validate a specific options for a pack. * Some options are not applicable to some packs. diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/option/SubpackOption.java b/api/src/main/java/org/geysermc/geyser/api/pack/option/SubpackOption.java index 882f6de70..5ebbc5290 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/option/SubpackOption.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/option/SubpackOption.java @@ -33,7 +33,7 @@ import org.geysermc.geyser.api.pack.ResourcePackManifest; * Can be used to specify which subpack from a resource pack a player should load. * Available subpacks can be seen in a resource pack manifest {@link ResourcePackManifest#subpacks()} */ -public interface SubpackOption extends ResourcePackOption { +public interface SubpackOption extends ResourcePackOption { /** * Creates a subpack option based on a {@link ResourcePackManifest.Subpack} @@ -56,7 +56,12 @@ public interface SubpackOption extends ResourcePackOption { } /** - * @return the subpack name of the chosen subpack. + * Creates a subpack option with no subpack specified + * + * @return a subpack option specifying no subpack */ - String subpackName(); + static SubpackOption empty() { + return GeyserApi.api().provider(SubpackOption.class, ""); + } + } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java index fb66a002a..543dedf92 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java @@ -96,7 +96,8 @@ public interface GeyserConfiguration { boolean isForceResourcePacks(); - List getResourcePackUrls(); + // TODO configurate - till then, api only + //List getResourcePackUrls(); @SuppressWarnings("BooleanMethodIsAlwaysInverted") boolean isXboxAchievementsEnabled(); diff --git a/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineResourcePacksEventImpl.java b/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineResourcePacksEventImpl.java index b97712ca9..ea4623d92 100644 --- a/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineResourcePacksEventImpl.java +++ b/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineResourcePacksEventImpl.java @@ -27,45 +27,94 @@ package org.geysermc.geyser.event.type; import lombok.Getter; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent; import org.geysermc.geyser.api.pack.ResourcePack; +import org.geysermc.geyser.api.pack.option.ResourcePackOption; +import org.geysermc.geyser.pack.GeyserResourcePack; +import org.geysermc.geyser.pack.ResourcePackHolder; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.UUID; @Getter public class GeyserDefineResourcePacksEventImpl extends GeyserDefineResourcePacksEvent { + private final Map packs; - private final Map packs; - - public GeyserDefineResourcePacksEventImpl(Map packMap) { + public GeyserDefineResourcePacksEventImpl(Map packMap) { this.packs = packMap; } @Override public @NonNull List resourcePacks() { - return List.copyOf(packs.values()); + return List.copyOf(packs.values().stream().map(ResourcePackHolder::pack).toList()); } @Override - public boolean register(@NonNull ResourcePack resourcePack) { - String packID = resourcePack.manifest().header().uuid().toString(); - if (packs.containsValue(resourcePack) || packs.containsKey(packID)) { + public boolean register(@NonNull ResourcePack resourcePack, @Nullable ResourcePackOption... options) { + GeyserResourcePack pack = validate(resourcePack); + + UUID uuid = resourcePack.uuid(); + if (packs.containsKey(uuid)) { return false; } - packs.put(resourcePack.manifest().header().uuid().toString(), resourcePack); + + ResourcePackHolder holder = ResourcePackHolder.of(pack); + packs.put(uuid, holder); + + // register options + registerOption(holder, options); return true; } @Override - public void registerAll(@NonNull Collection resourcePacks) { - resourcePacks.forEach(this::register); + public void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption... options) { + Objects.requireNonNull(uuid); + Objects.requireNonNull(options); + + ResourcePackHolder packHolder = packs.get(uuid); + if (packHolder == null) { + throw new IllegalArgumentException("ResourcePack with " + uuid + " not found, unable to provide options"); + } + + registerOption(packHolder, options); + } + + @Override + public Collection> options(@NonNull UUID uuid) { + Objects.requireNonNull(uuid); + ResourcePackHolder packHolder = packs.get(uuid); + if (packHolder == null) { + throw new IllegalArgumentException("ResourcePack with " + uuid + " not found, unable to provide options"); + } + + return packHolder.optionHolder().immutableValues(); } @Override public boolean unregister(@NonNull UUID uuid) { - return packs.remove(uuid.toString()) != null; + return packs.remove(uuid) != null; + } + + private void registerOption(@NonNull ResourcePackHolder holder, @Nullable ResourcePackOption... options) { + if (options == null) { + return; + } + + holder.optionHolder().add(options); + holder.optionHolder().validateOptions(holder.pack()); + } + + private GeyserResourcePack validate(@NonNull ResourcePack resourcePack) { + Objects.requireNonNull(resourcePack); + if (resourcePack instanceof GeyserResourcePack geyserResourcePack) { + return geyserResourcePack; + } else { + throw new IllegalArgumentException("Unknown resource pack implementation: %s". + formatted(resourcePack.getClass().getSuperclass().getName())); + } } } diff --git a/core/src/main/java/org/geysermc/geyser/event/type/SessionLoadResourcePacksEventImpl.java b/core/src/main/java/org/geysermc/geyser/event/type/SessionLoadResourcePacksEventImpl.java index c1fe7334f..e06e02e70 100644 --- a/core/src/main/java/org/geysermc/geyser/event/type/SessionLoadResourcePacksEventImpl.java +++ b/core/src/main/java/org/geysermc/geyser/event/type/SessionLoadResourcePacksEventImpl.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.event.type; import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Getter; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -34,11 +35,11 @@ import org.cloudburstmc.protocol.bedrock.packet.ResourcePacksInfoPacket; import org.geysermc.geyser.api.event.bedrock.SessionLoadResourcePacksEvent; import org.geysermc.geyser.api.pack.ResourcePack; import org.geysermc.geyser.api.pack.ResourcePackManifest; +import org.geysermc.geyser.api.pack.UrlPackCodec; import org.geysermc.geyser.api.pack.option.PriorityOption; import org.geysermc.geyser.api.pack.option.ResourcePackOption; -import org.geysermc.geyser.api.pack.option.SubpackOption; import org.geysermc.geyser.pack.GeyserResourcePack; -import org.geysermc.geyser.pack.option.GeyserSubpackOption; +import org.geysermc.geyser.pack.ResourcePackHolder; import org.geysermc.geyser.pack.option.OptionHolder; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.session.GeyserSession; @@ -47,8 +48,6 @@ import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -59,89 +58,72 @@ import java.util.stream.Collectors; public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksEvent { @Getter - private final Map packs; - private final Map options = new HashMap<>(); + private final Map packs; + private final Map options; public SessionLoadResourcePacksEventImpl(GeyserSession session) { super(session); this.packs = new Object2ObjectLinkedOpenHashMap<>(Registries.RESOURCE_PACKS.get()); - } - - public LinkedList orderedPacks() { - TreeSet> sortedPacks = packs.values().stream() - // Map each ResourcePack to a pair of (ResourcePack, Priority) - .map(pack -> new AbstractMap.SimpleEntry<>(pack, getPriority(pack))) - // Sort by priority in ascending order - .collect(Collectors.toCollection(() -> new TreeSet<>(Map.Entry.comparingByValue(Comparator.naturalOrder())))); - - // Convert the sorted entries to a LinkedList of ResourcePackStackPacket.Entry - return sortedPacks.stream() - .map(entry -> { - ResourcePackManifest.Header header = entry.getKey().manifest().header(); - return new ResourcePackStackPacket.Entry( - header.uuid().toString(), - header.version().toString(), - getSubpackName(entry.getKey()) - ); - }) - .collect(Collectors.toCollection(LinkedList::new)); - } - - // Helper method to get the priority of a ResourcePack - private double getPriority(GeyserResourcePack pack) { - ResourcePackOption option; - OptionHolder holder = options.get(pack.uuid()); - - if (holder == null) { - // priority is always set - option = pack.options().get(ResourcePackOption.Type.PRIORITY); - } else { - option = options.get(pack.uuid()) - .getOrDefault(ResourcePackOption.Type.PRIORITY, pack, PriorityOption.NORMAL); - } - - return ((PriorityOption) option).priority(); - } - - public List infoPacketEntries() { - List entries = new ArrayList<>(); - - for (GeyserResourcePack pack : packs.values()) { - ResourcePackManifest.Header header = pack.manifest().header(); - entries.add(new ResourcePacksInfoPacket.Entry( - header.uuid().toString(), header.version().toString(), pack.codec().size(), pack.contentKey(), - getSubpackName(pack), header.uuid().toString(), false, false) - ); - } - - return entries; - } - - private String getSubpackName(GeyserResourcePack pack) { - OptionHolder holder = options.get(pack.uuid()); - ResourcePackOption option; - if (holder == null) { - option = pack.options().getOrDefault(ResourcePackOption.Type.PRIORITY, GeyserSubpackOption.EMPTY); - } else { - option = options.get(pack.uuid()) - .getOrDefault(ResourcePackOption.Type.PRIORITY, pack, GeyserSubpackOption.EMPTY); - } - - return ((SubpackOption) option).subpackName(); + this.options = new Object2ObjectOpenHashMap<>(); } @Override public @NonNull List resourcePacks() { - return List.copyOf(packs.values()); + return packs.values().stream().map(ResourcePackHolder::pack).collect(Collectors.toUnmodifiableList()); } @Override public boolean register(@NonNull ResourcePack resourcePack) { - validate(resourcePack); return register(resourcePack, PriorityOption.NORMAL); } - private void registerOption(@NonNull ResourcePack resourcePack, @Nullable ResourcePackOption... options) { + @Override + public boolean register(@NonNull ResourcePack resourcePack, @Nullable ResourcePackOption... options) { + GeyserResourcePack pack = validate(resourcePack); + + UUID uuid = resourcePack.uuid(); + if (packs.containsKey(uuid)) { + return false; + } + + packs.put(uuid, ResourcePackHolder.of(pack)); + + // register options + registerOption(resourcePack, options); + return true; + } + + @Override + public void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption... options) { + Objects.requireNonNull(uuid); + Objects.requireNonNull(options); + ResourcePackHolder holder = packs.get(uuid); + if (holder == null) { + throw new IllegalArgumentException("Pack with uuid %s not registered yet!".formatted(uuid)); + } + + registerOption(holder.pack(), options); + } + + @Override + public Collection> options(@NonNull UUID uuid) { + Objects.requireNonNull(uuid); + ResourcePackHolder packHolder = packs.get(uuid); + if (packHolder == null) { + throw new IllegalArgumentException("ResourcePack with " + uuid + " not found, unable to provide options"); + } + + OptionHolder optionHolder = options.getOrDefault(uuid, new OptionHolder()); + return optionHolder.immutableValues(packHolder.optionHolder()); + } + + @Override + public boolean unregister(@NonNull UUID uuid) { + options.remove(uuid); + return packs.remove(uuid) != null; + } + + private void registerOption(@NonNull ResourcePack resourcePack, @Nullable ResourcePackOption... options) { if (options == null) { return; } @@ -153,52 +135,79 @@ public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksE holder.validateOptions(pack); } - @Override - public boolean register(@NonNull ResourcePack resourcePack, @Nullable ResourcePackOption... options) { - GeyserResourcePack pack = validate(resourcePack); + // Methods used internally for e.g. ordered packs, or resource pack entries - UUID uuid = resourcePack.uuid(); - if (packs.containsValue(pack) || packs.containsKey(uuid)) { - return false; + public List orderedPacks() { + TreeSet> sortedPacks = packs.values().stream() + // Map each ResourcePack to a pair of (GeyserResourcePack, Priority) + .map(holder -> new AbstractMap.SimpleEntry<>(holder.pack(), getPriority(holder.pack()))) + // Sort by priority in ascending order + .collect(Collectors.toCollection(() -> new TreeSet<>(Map.Entry.comparingByValue(Comparator.naturalOrder())))); + + return sortedPacks.stream() + .map(entry -> { + ResourcePackManifest.Header header = entry.getKey().manifest().header(); + return new ResourcePackStackPacket.Entry( + header.uuid().toString(), + header.version().toString(), + subpackName(entry.getKey()) + ); + }) + .toList(); + } + + public List infoPacketEntries() { + List entries = new ArrayList<>(); + + for (ResourcePackHolder holder : packs.values()) { + GeyserResourcePack pack = holder.pack(); + ResourcePackManifest.Header header = pack.manifest().header(); + entries.add(new ResourcePacksInfoPacket.Entry( + header.uuid().toString(), header.version().toString(), pack.codec().size(), pack.contentKey(), + subpackName(pack), header.uuid().toString(), false, false) + ); } - packs.put(uuid, pack); - - // register options - registerOption(resourcePack, options); - return true; + return entries; } - @Override - public void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption... options) { - Objects.requireNonNull(uuid); - Objects.requireNonNull(options); - GeyserResourcePack resourcePack = packs.get(uuid); - if (resourcePack == null) { - throw new IllegalArgumentException("Pack with uuid %s not registered yet!".formatted(uuid)); + public List cdnEntries() { + List entries = new ArrayList<>(); + + for (ResourcePackHolder holder : this.packs.values()) { + GeyserResourcePack pack = holder.pack(); + ResourcePackManifest.Header header = pack.manifest().header(); + if (pack.codec() instanceof UrlPackCodec urlPackCodec) { + entries.add(new ResourcePacksInfoPacket.CDNEntry( + header.uuid() + "_" + header.version(), urlPackCodec.url())); + } } - registerOption(resourcePack, options); + return entries; } - @Override - public Collection options(@NonNull UUID uuid) { - Objects.requireNonNull(uuid); - GeyserResourcePack pack = packs.get(uuid); - if (pack == null) { - throw new IllegalArgumentException("ResourcePack with " + uuid + " not found, unable to provide options"); - } + // Helper methods to get the options for a ResourcePack - OptionHolder holder = options.getOrDefault(uuid, new OptionHolder()); - return holder.immutableValues(pack); + private double getPriority(GeyserResourcePack pack) { + OptionHolder holder = options.get(pack.uuid()); + OptionHolder defaultHolder = packs.get(pack.uuid()).optionHolder(); + Objects.requireNonNull(defaultHolder); // should never be null + + return OptionHolder.getWithFallbacks(ResourcePackOption.Type.PRIORITY, + holder, defaultHolder, 5); } - @Override - public boolean unregister(@NonNull UUID uuid) { - options.remove(uuid); - return packs.remove(uuid) != null; + private String subpackName(GeyserResourcePack pack) { + OptionHolder holder = options.get(pack.uuid()); + OptionHolder defaultHolder = packs.get(pack.uuid()).optionHolder(); + Objects.requireNonNull(defaultHolder); // should never be null + + return OptionHolder.getWithFallbacks(ResourcePackOption.Type.SUBPACK, + holder, defaultHolder, ""); } + // Helper method to validate a pack + private GeyserResourcePack validate(@NonNull ResourcePack resourcePack) { Objects.requireNonNull(resourcePack); if (resourcePack instanceof GeyserResourcePack geyserResourcePack) { diff --git a/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java b/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java index cdda4fe4c..000557e4c 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java @@ -168,7 +168,7 @@ public class BlockInventoryHolder extends InventoryHolder { return; } } else { - GeyserImpl.getInstance().getLogger().warning("Tried to close a non-container inventory in a block inventory holder! "); + GeyserImpl.getInstance().getLogger().warning("Tried to close a non-container inventory in a block inventory optionHolder! "); if (GeyserImpl.getInstance().getLogger().isDebug()) { GeyserImpl.getInstance().getLogger().debug("Current inventory: " + inventory); GeyserImpl.getInstance().getLogger().debug("Open inventory: " + session.getOpenInventory()); diff --git a/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java b/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java index 3c0caa60c..89f6e226c 100644 --- a/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java +++ b/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java @@ -85,7 +85,7 @@ public record Enchantment(String identifier, return Set.copyOf(components); // Also ensures any empty sets are consolidated } - // TODO holder set util? + // TODO optionHolder set util? private static HolderSet readHolderSet(@Nullable Object holderSet, Function keyIdMapping) { if (holderSet == null) { return new HolderSet(new int[]{}); diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index 751981255..39ce47526 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -62,6 +62,7 @@ import org.geysermc.geyser.api.pack.ResourcePackManifest; import org.geysermc.geyser.api.pack.UrlPackCodec; import org.geysermc.geyser.event.type.SessionLoadResourcePacksEventImpl; import org.geysermc.geyser.pack.GeyserResourcePack; +import org.geysermc.geyser.pack.ResourcePackHolder; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.loader.ResourcePackLoader; @@ -78,6 +79,7 @@ import java.nio.channels.SeekableByteChannel; import java.util.ArrayDeque; import java.util.Deque; import java.util.OptionalInt; +import java.util.UUID; public class UpstreamPacketHandler extends LoggingPacketHandler { @@ -206,14 +208,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket(); resourcePacksInfo.getResourcePackInfos().addAll(this.resourcePackLoadEvent.infoPacketEntries()); + resourcePacksInfo.getCDNEntries().addAll(this.resourcePackLoadEvent.cdnEntries()); - // TODO add url pack entries - /* - if (pack.codec() instanceof UrlPackCodec urlPackCodec) { - resourcePacksInfo.getCDNEntries().add(new ResourcePacksInfoPacket.CDNEntry( - header.uuid() + "_" + header.version(), urlPackCodec.url())); - } - */ resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().getConfig().isForceResourcePacks()); session.sendUpstreamPacket(resourcePacksInfo); @@ -302,21 +298,22 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public PacketSignal handle(ResourcePackChunkRequestPacket packet) { - ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(packet.getPackId().toString()); + ResourcePackHolder holder = this.resourcePackLoadEvent.getPacks().get(packet.getPackId()); - if (pack == null) { + if (holder == null) { GeyserImpl.getInstance().getLogger().debug("Client %s tried to request pack id (%s) not sent to it!" .formatted(session.bedrockUsername(), packet.getPackId())); session.disconnect("disconnectionScreen.resourcePack"); return PacketSignal.HANDLED; } + ResourcePack pack = holder.pack(); ResourcePackChunkDataPacket data = new ResourcePackChunkDataPacket(); PackCodec codec = pack.codec(); // If a remote pack ends up here, that usually implies that a client was not able to download the pack if (codec instanceof UrlPackCodec urlPackCodec) { - ResourcePackLoader.testRemotePack(session, urlPackCodec, packet.getPackId().toString(), packet.getPackVersion()); + ResourcePackLoader.testRemotePack(session, urlPackCodec, packet.getPackId(), packet.getPackVersion()); } data.setChunkIndex(packet.getChunkIndex()); @@ -332,6 +329,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { channel.position(offset); channel.read(ByteBuffer.wrap(packData, 0, packData.length)); } catch (IOException e) { + session.disconnect("disconnectionScreen.resourcePack"); e.printStackTrace(); } @@ -358,14 +356,25 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { return; } - ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(packID[0]); - - if (pack == null) { - GeyserImpl.getInstance().getLogger().debug("Client %s tried to request invalid pack id %s!" - .formatted(session.bedrockUsername(), packID[0])); + UUID packId; + try { + packId = UUID.fromString(packID[0]); + } catch (IllegalArgumentException e) { + GeyserImpl.getInstance().getLogger().debug("Client %s tried to request pack with an invalid id (%s)" + .formatted(session.bedrockUsername(), id)); session.disconnect("disconnectionScreen.resourcePack"); return; } + + ResourcePackHolder holder = this.resourcePackLoadEvent.getPacks().get(packId); + if (holder == null) { + GeyserImpl.getInstance().getLogger().debug("Client %s tried to request pack id (%s) not sent to it!" + .formatted(session.bedrockUsername(), id)); + session.disconnect("disconnectionScreen.resourcePack"); + return; + } + + ResourcePack pack = holder.pack(); PackCodec codec = pack.codec(); ResourcePackManifest.Header header = pack.manifest().header(); diff --git a/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePack.java b/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePack.java index 4de140305..306bb9aa8 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePack.java +++ b/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePack.java @@ -29,17 +29,13 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.pack.PackCodec; import org.geysermc.geyser.api.pack.ResourcePack; import org.geysermc.geyser.api.pack.ResourcePackManifest; -import org.geysermc.geyser.api.pack.option.ResourcePackOption; -import org.geysermc.geyser.pack.option.OptionHolder; -import java.util.Collection; import java.util.Objects; public record GeyserResourcePack( @NonNull PackCodec codec, @NonNull ResourcePackManifest manifest, - @NonNull String contentKey, - @NonNull OptionHolder options + @NonNull String contentKey ) implements ResourcePack { /** @@ -47,15 +43,6 @@ public record GeyserResourcePack( */ public static final int CHUNK_SIZE = 102400; - @Override - public Collection defaultOptions() { - return options.immutableValues(); - } - - public GeyserResourcePack(PackCodec codec, ResourcePackManifest manifest, String contentKey) { - this(codec, manifest, contentKey, new OptionHolder()); - } - public static class Builder implements ResourcePack.Builder { public Builder(PackCodec codec, ResourcePackManifest manifest) { @@ -72,7 +59,6 @@ public record GeyserResourcePack( private final PackCodec codec; private final ResourcePackManifest manifest; private String contentKey = ""; - private final OptionHolder optionHolder = new OptionHolder(); @Override public ResourcePackManifest manifest() { @@ -89,27 +75,14 @@ public record GeyserResourcePack( return contentKey; } - @Override - public Collection defaultOptions() { - return optionHolder.immutableValues(); - } - public Builder contentKey(@NonNull String contentKey) { Objects.requireNonNull(contentKey); this.contentKey = contentKey; return this; } - public Builder defaultOptions(@NonNull ResourcePackOption... defaultOptions) { - Objects.requireNonNull(defaultOptions); - this.optionHolder.add(defaultOptions); - return this; - } - public GeyserResourcePack build() { - GeyserResourcePack pack = new GeyserResourcePack(codec, manifest, contentKey, optionHolder); - optionHolder.validateOptions(pack); - return pack; + return new GeyserResourcePack(codec, manifest, contentKey); } } } diff --git a/core/src/main/java/org/geysermc/geyser/pack/ResourcePackHolder.java b/core/src/main/java/org/geysermc/geyser/pack/ResourcePackHolder.java new file mode 100644 index 000000000..20463fab8 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/pack/ResourcePackHolder.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 GeyserMC. http://geysermc.org + * + * 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. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.pack; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.pack.option.OptionHolder; + +public record ResourcePackHolder( + @NonNull GeyserResourcePack pack, + @NonNull OptionHolder optionHolder +) { + + public static ResourcePackHolder of(GeyserResourcePack pack) { + return new ResourcePackHolder(pack, new OptionHolder()); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/pack/option/GeyserPriorityOption.java b/core/src/main/java/org/geysermc/geyser/pack/option/GeyserPriorityOption.java index a8cbc929a..f8a31fece 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/option/GeyserPriorityOption.java +++ b/core/src/main/java/org/geysermc/geyser/pack/option/GeyserPriorityOption.java @@ -44,6 +44,11 @@ public record GeyserPriorityOption(double priority) implements PriorityOption { return Type.PRIORITY; } + @Override + public @NonNull Double value() { + return priority; + } + @Override public void validate(@NonNull ResourcePack pack) { Objects.requireNonNull(pack); diff --git a/core/src/main/java/org/geysermc/geyser/pack/option/GeyserSubpackOption.java b/core/src/main/java/org/geysermc/geyser/pack/option/GeyserSubpackOption.java index 6b04d6d3e..fe8d75e2e 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/option/GeyserSubpackOption.java +++ b/core/src/main/java/org/geysermc/geyser/pack/option/GeyserSubpackOption.java @@ -38,16 +38,25 @@ import java.util.Objects; */ public record GeyserSubpackOption(String subpackName) implements SubpackOption { - public static final GeyserSubpackOption EMPTY = new GeyserSubpackOption(""); - @Override public @NonNull Type type() { return Type.SUBPACK; } + @Override + public @NonNull String value() { + return subpackName; + } + @Override public void validate(@NonNull ResourcePack pack) { Objects.requireNonNull(pack); + + // Allow empty subpack names - they're the same as "none" + if (subpackName.isEmpty()) { + return; + } + if (pack.manifest().subpacks().stream().noneMatch(subpack -> subpack.name().equals(subpackName))) { throw new IllegalArgumentException("No subpack with the name %s found!".formatted(subpackName)); } diff --git a/core/src/main/java/org/geysermc/geyser/pack/option/OptionHolder.java b/core/src/main/java/org/geysermc/geyser/pack/option/OptionHolder.java index 46fa2e313..d4645c333 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/option/OptionHolder.java +++ b/core/src/main/java/org/geysermc/geyser/pack/option/OptionHolder.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.pack.option; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.pack.ResourcePack; import org.geysermc.geyser.api.pack.option.PriorityOption; import org.geysermc.geyser.api.pack.option.ResourcePackOption; @@ -35,9 +37,9 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -public class OptionHolder extends HashMap { +public class OptionHolder extends HashMap> { - public void add(ResourcePackOption option) { + public void add(ResourcePackOption option) { if (super.containsKey(option.type())) { super.replace(option.type(), option); } else { @@ -45,29 +47,38 @@ public class OptionHolder extends HashMap... options) { + for (ResourcePackOption option : options) { add(option); } } - public ResourcePackOption getOrDefault(ResourcePackOption.Type type, - GeyserResourcePack pack, - ResourcePackOption defaultValue) { - ResourcePackOption option = super.get(type); + @SuppressWarnings("unchecked") + public static T getWithFallbacks(ResourcePackOption.@NonNull Type type, + @Nullable OptionHolder holder, + @NonNull OptionHolder defaultHolder, + @NonNull T defaultValue) { + ResourcePackOption option; + + // First: the optionHolder's option, if it exists + if (holder != null) { + option = holder.get(type); + if (option != null) { + return ((ResourcePackOption) option).value(); + } + } + + // Second: check the default optionHolder for the option, if it exists + option = defaultHolder.get(type); if (option != null) { - return option; - } - - ResourcePackOption packOption = pack.options().get(type); - if (packOption != null) { - return packOption; + return ((ResourcePackOption) option).value(); } + // Finally: Fallback to default return defaultValue; } - public void remove(ResourcePackOption option) { + public void remove(ResourcePackOption option) { super.remove(option.type()); } @@ -81,26 +92,26 @@ public class OptionHolder extends HashMap immutableValues() { + public Collection> immutableValues() { return Collections.unmodifiableCollection(values()); } /** - * @return the options of this option holder, with fallbacks to options of a {@link GeyserResourcePack} + * @return the options of this option optionHolder, with fallbacks to options of a {@link GeyserResourcePack} * if they're not already overridden here */ - public Collection immutableValues(GeyserResourcePack pack) { - if (pack.options().isEmpty()) { + public Collection> immutableValues(OptionHolder defaultValues) { + if (defaultValues.isEmpty()) { return immutableValues(); } // Create a map to hold the combined values - Map combinedOptions = new HashMap<>(this); + Map> combinedOptions = new HashMap<>(this); // Add options from the pack if not already overridden by this OptionHolder - pack.options().forEach(combinedOptions::putIfAbsent); + defaultValues.forEach(combinedOptions::putIfAbsent); // Return an immutable collection of the combined options return Collections.unmodifiableCollection(combinedOptions.values()); diff --git a/core/src/main/java/org/geysermc/geyser/pack/url/GeyserUrlPackCodec.java b/core/src/main/java/org/geysermc/geyser/pack/url/GeyserUrlPackCodec.java index f8b5ba7fb..ee7f48c90 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/url/GeyserUrlPackCodec.java +++ b/core/src/main/java/org/geysermc/geyser/pack/url/GeyserUrlPackCodec.java @@ -27,10 +27,9 @@ package org.geysermc.geyser.pack.url; import lombok.Getter; import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.pack.PathPackCodec; -import org.geysermc.geyser.api.pack.ResourcePack; import org.geysermc.geyser.api.pack.UrlPackCodec; +import org.geysermc.geyser.pack.GeyserResourcePack; import org.geysermc.geyser.registry.loader.ResourcePackLoader; import java.io.IOException; @@ -39,18 +38,12 @@ import java.util.Objects; public class GeyserUrlPackCodec extends UrlPackCodec { private final @NonNull String url; - private final @Nullable String contentKey; @Getter private PathPackCodec fallback; - public GeyserUrlPackCodec(String url) throws IllegalArgumentException { - this(url, null); - } - - public GeyserUrlPackCodec(@NonNull String url, @Nullable String contentKey) throws IllegalArgumentException { - Objects.requireNonNull(url, "url cannot be null"); + public GeyserUrlPackCodec(@NonNull String url) throws IllegalArgumentException { + Objects.requireNonNull(url); this.url = url; - this.contentKey = contentKey; } @Override @@ -66,14 +59,19 @@ public class GeyserUrlPackCodec extends UrlPackCodec { } @Override - public @NonNull SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws IOException { + public @NonNull SeekableByteChannel serialize() throws IOException { Objects.requireNonNull(fallback, "must call #create() before attempting to serialize!!"); - return fallback.serialize(resourcePack); + return fallback.serialize(); } @Override @NonNull - public ResourcePack create() { + public GeyserResourcePack create() { + return createBuilder().build(); + } + + @Override + protected GeyserResourcePack.@NonNull Builder createBuilder() { if (this.fallback == null) { try { ResourcePackLoader.downloadPack(url, false).whenComplete((pack, throwable) -> { @@ -84,7 +82,7 @@ public class GeyserUrlPackCodec extends UrlPackCodec { } }).join(); // Needed to ensure that we don't attempt to read a pack before downloading/checking it } catch (Exception e) { - throw new IllegalArgumentException("Failed to download pack from the url %s (reason: %s)!".formatted(url, e.getMessage())); + throw new IllegalArgumentException("Failed to download pack from the url %s (%s)!".formatted(url, e.getMessage())); } } @@ -95,9 +93,4 @@ public class GeyserUrlPackCodec extends UrlPackCodec { public @NonNull String url() { return this.url; } - - @Override - public @NonNull String contentKey() { - return this.contentKey != null ? contentKey : ""; - } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/Registries.java b/core/src/main/java/org/geysermc/geyser/registry/Registries.java index 1c30b422b..91547ff24 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -37,6 +37,7 @@ import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.pack.GeyserResourcePack; +import org.geysermc.geyser.pack.ResourcePackHolder; import org.geysermc.geyser.registry.loader.BiomeIdentifierRegistryLoader; import org.geysermc.geyser.registry.loader.BlockEntityRegistryLoader; import org.geysermc.geyser.registry.loader.ParticleTypesRegistryLoader; @@ -160,7 +161,7 @@ public final class Registries { /** * A mapped registry holding {@link GeyserResourcePack}'s with the pack uuid as keys. */ - public static final DeferredRegistry> RESOURCE_PACKS = DeferredRegistry.create(GeyserImpl.getInstance().packDirectory(), SimpleMappedRegistry::create, RegistryLoaders.RESOURCE_PACKS); + public static final DeferredRegistry> RESOURCE_PACKS = DeferredRegistry.create(GeyserImpl.getInstance().packDirectory(), SimpleMappedRegistry::create, RegistryLoaders.RESOURCE_PACKS); /** * A mapped registry holding sound identifiers to their corresponding {@link SoundMapping}. diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java index 21f329709..7e5aa7c7e 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java @@ -38,6 +38,7 @@ import org.geysermc.geyser.api.pack.UrlPackCodec; import org.geysermc.geyser.event.type.GeyserDefineResourcePacksEventImpl; import org.geysermc.geyser.pack.GeyserResourcePack; import org.geysermc.geyser.pack.GeyserResourcePackManifest; +import org.geysermc.geyser.pack.ResourcePackHolder; import org.geysermc.geyser.pack.SkullResourcePackManager; import org.geysermc.geyser.pack.path.GeyserPathPackCodec; import org.geysermc.geyser.pack.url.GeyserUrlPackCodec; @@ -67,7 +68,7 @@ import java.util.zip.ZipFile; /** * Loads {@link ResourcePack}s within a {@link Path} directory, firing the {@link GeyserDefineResourcePacksEventImpl}. */ -public class ResourcePackLoader implements RegistryLoader> { +public class ResourcePackLoader implements RegistryLoader> { /** * Used to keep track of remote resource packs that the client rejected. @@ -85,8 +86,8 @@ public class ResourcePackLoader implements RegistryLoader load(Path directory) { - Map packMap = new Object2ObjectOpenHashMap<>(); + public Map load(Path directory) { + Map packMap = new Object2ObjectOpenHashMap<>(); if (!Files.exists(directory)) { try { @@ -122,20 +123,22 @@ public class ResourcePackLoader implements RegistryLoader loadRemotePacks() { + private Map loadRemotePacks() { GeyserImpl instance = GeyserImpl.getInstance(); // Unable to make this a static variable, as the test would fail - final Path cachedCdnPacksDirectory = instance.getBootstrap().getConfigFolder().resolve("cache").resolve("remote_packs"); + final Path cachedDirectory = instance.getBootstrap().getConfigFolder().resolve("cache").resolve("remote_packs"); - if (!Files.exists(cachedCdnPacksDirectory)) { + if (!Files.exists(cachedDirectory)) { try { - Files.createDirectories(cachedCdnPacksDirectory); + Files.createDirectories(cachedDirectory); } catch (IOException e) { instance.getLogger().error("Could not create remote pack cache directory", e); return new Object2ObjectOpenHashMap<>(); } } - List remotePackUrls = instance.getConfig().getResourcePackUrls(); - Map packMap = new Object2ObjectOpenHashMap<>(); + //List remotePackUrls = instance.getConfig().getResourcePackUrls(); + List remotePackUrls = List.of(); + Map packMap = new Object2ObjectOpenHashMap<>(); for (String url : remotePackUrls) { try { GeyserUrlPackCodec codec = new GeyserUrlPackCodec(url); - ResourcePack pack = codec.create(); - packMap.put(pack.manifest().header().uuid().toString(), pack); + GeyserResourcePack pack = codec.create(); + packMap.put(pack.uuid(), ResourcePackHolder.of(pack)); } catch (Throwable e) { instance.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", url)); instance.getLogger().error(e.getMessage()); @@ -257,7 +261,7 @@ public class ResourcePackLoader implements RegistryLoader { - CACHED_FAILED_PACKS.invalidate(packId); + CACHED_FAILED_PACKS.invalidate(codec.url()); deleteFile(path); }, 5, TimeUnit.MINUTES); } catch (RejectedExecutionException exception) { @@ -369,7 +372,6 @@ public class ResourcePackLoader implements RegistryLoader