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 d5a8aea87..fbb3b6609 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 @@ -26,6 +26,7 @@ package org.geysermc.geyser.api.event.bedrock; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.connection.ConnectionEvent; import org.geysermc.geyser.api.pack.ResourcePack; @@ -64,11 +65,29 @@ public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent { * specific options. * * @param resourcePack a resource pack that will be sent to the client. - * @param resourcePackOptions {@link ResourcePackOption}'s that specify how clients load the pack + * @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, ResourcePackOption... resourcePackOptions); + public abstract boolean register(@NonNull ResourcePack resourcePack, @Nullable ResourcePackOption... options); + + /** + * Sets {@link ResourcePackOption}'s for a resource pack + * + * @param resourcePack the resource pack 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 registerOptions(@NonNull ResourcePack resourcePack, @NonNull ResourcePackOption... options); + + /** + * Sets {@link ResourcePackOption}'s for a resource pack + * + * @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 registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption... options); /** * Returns the subpack options set for a specific resource pack. @@ -76,9 +95,7 @@ public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent { * @param resourcePack the resourcePack for which the options are set * @return a list of {@link ResourcePackOption} */ - Collection options(ResourcePack resourcePack) { - return options(resourcePack.manifest().header().uuid()); - } + public abstract Collection options(ResourcePack resourcePack); /** * Returns the subpack options set for a specific resource pack uuid. 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 29aa340ff..f246f7c63 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 @@ -30,6 +30,7 @@ import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.pack.option.ResourcePackOption; import java.util.Collection; +import java.util.UUID; /** * Represents a resource pack sent to Bedrock clients @@ -63,6 +64,14 @@ public interface ResourcePack { @NonNull String contentKey(); + /** + * @return the resource pack uuid. Shortcut for getting the UUID from the header. + */ + @NonNull + default UUID uuid() { + 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, 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 a7f576145..33cb23332 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 @@ -28,6 +28,7 @@ package org.geysermc.geyser.event.type; import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; import lombok.Getter; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.packet.ResourcePackStackPacket; import org.cloudburstmc.protocol.bedrock.packet.ResourcePacksInfoPacket; import org.geysermc.geyser.api.event.bedrock.SessionLoadResourcePacksEvent; @@ -36,20 +37,21 @@ import org.geysermc.geyser.api.pack.ResourcePackManifest; 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.option.OptionHolder; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.session.GeyserSession; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; +import java.util.Objects; import java.util.TreeSet; import java.util.UUID; import java.util.stream.Collectors; @@ -57,22 +59,19 @@ 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 = new HashMap<>(); public SessionLoadResourcePacksEventImpl(GeyserSession session) { super(session); this.packs = new Object2ObjectLinkedOpenHashMap<>(Registries.RESOURCE_PACKS.get()); - this.packs.values().forEach( - pack -> options.put(pack.manifest().header().uuid(), pack.defaultOptions()) - ); } public LinkedList orderedPacks() { - TreeSet> sortedPacks = packs.values().stream() + 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 descending order + // 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 @@ -82,42 +81,52 @@ public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksE return new ResourcePackStackPacket.Entry( header.uuid().toString(), header.version().toString(), - getSubpackName(header.uuid()) + getSubpackName(entry.getKey()) ); }) .collect(Collectors.toCollection(LinkedList::new)); } // Helper method to get the priority of a ResourcePack - private int getPriority(ResourcePack pack) { - return options.get(pack.manifest().header().uuid()).stream() - .filter(option -> option instanceof PriorityOption) - .mapToInt(option -> ((PriorityOption) option).priority()) - .max() - .orElse(PriorityOption.NORMAL.priority()); - } + private int getPriority(GeyserResourcePack pack) { + ResourcePackOption option; + OptionHolder holder = options.get(pack.uuid()); + if (holder == null) { + 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 (ResourcePack pack : packs.values()) { + 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(header.uuid()), header.uuid().toString(), false, false) + getSubpackName(pack), header.uuid().toString(), false, false) ); } return entries; } - private String getSubpackName(UUID uuid) { - return options.get(uuid).stream() - .filter(option -> option instanceof SubpackOption) - .map(option -> ((SubpackOption) option).subpackName()) - .findFirst() - .orElse(""); // Return an empty string if none is found + 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(); } @Override @@ -127,35 +136,73 @@ public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksE @Override public boolean register(@NonNull ResourcePack resourcePack) { + validate(resourcePack); return register(resourcePack, PriorityOption.NORMAL); } + public void registerOption(@NonNull ResourcePack resourcePack, @NonNull ResourcePackOption option) { + GeyserResourcePack pack = validate(resourcePack); + + OptionHolder holder = this.options.computeIfAbsent(pack.uuid(), $ -> new OptionHolder()); + holder.add(option); + holder.validateOptions(pack); + } + @Override - public boolean register(@NonNull ResourcePack resourcePack, ResourcePackOption... resourcePackOptions) { + public boolean register(@NonNull ResourcePack resourcePack, @Nullable ResourcePackOption... options) { + GeyserResourcePack pack = validate(resourcePack); - // Validate options, check for duplicates - Set types = new HashSet<>(); - for (ResourcePackOption option : resourcePackOptions) { - option.validate(resourcePack); - if (!types.add(option.type())) { - throw new IllegalArgumentException("Duplicate resource pack option " + option + "!"); - } - } - types.clear(); - - UUID uuid = resourcePack.manifest().header().uuid(); - if (packs.containsValue(resourcePack) || packs.containsKey(uuid)) { + UUID uuid = resourcePack.uuid(); + if (packs.containsValue(pack) || packs.containsKey(uuid)) { return false; } - packs.put(uuid, resourcePack); - options.put(uuid, List.of(resourcePackOptions)); + packs.put(uuid, pack); + + // register options + OptionHolder holder = new OptionHolder(); + holder.add(options); + holder.validateOptions(pack); + this.options.put(uuid, holder); + return true; } @Override - public Collection options(UUID uuid) { - return Collections.unmodifiableCollection(options.get(uuid)); + public void registerOptions(@NonNull ResourcePack resourcePack, @NonNull ResourcePackOption... options) { + validate(resourcePack); + registerOptions(resourcePack.uuid()); + } + + @Override + public void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption... options) { + Objects.requireNonNull(uuid); + GeyserResourcePack resourcePack = packs.get(uuid); + if (resourcePack == null) { + throw new IllegalArgumentException("Pack with uuid %s not registered yet!".formatted(uuid)); + } + + OptionHolder holder = this.options.computeIfAbsent(uuid, $ -> new OptionHolder()); + holder.add(options); + holder.validateOptions(resourcePack); + } + + @Override + public Collection options(@NonNull ResourcePack resourcePack) { + validate(resourcePack); + return options(resourcePack.uuid()); + } + + @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"); + } + + OptionHolder holder = options.getOrDefault(uuid, new OptionHolder()); + return holder.immutableValues(pack); } @Override @@ -163,4 +210,14 @@ public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksE options.remove(uuid); return packs.remove(uuid) != null; } + + 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/pack/GeyserResourcePack.java b/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePack.java index dbbe9179d..6e7a35628 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePack.java +++ b/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePack.java @@ -29,23 +29,17 @@ 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.PriorityOption; import org.geysermc.geyser.api.pack.option.ResourcePackOption; +import org.geysermc.geyser.pack.option.OptionHolder; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; import java.util.Objects; -import java.util.Set; public record GeyserResourcePack( PackCodec codec, ResourcePackManifest manifest, String contentKey, - Collection defaultOptions + OptionHolder options ) implements ResourcePack { /** @@ -53,8 +47,13 @@ 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 ArrayList<>(List.of(PriorityOption.NORMAL))); + this(codec, manifest, contentKey, new OptionHolder()); } public static class Builder implements ResourcePack.Builder { @@ -73,7 +72,7 @@ public record GeyserResourcePack( private final PackCodec codec; private final ResourcePackManifest manifest; private String contentKey = ""; - private final Collection defaultOptions = new ArrayList<>(List.of(PriorityOption.NORMAL)); + private final OptionHolder optionHolder = new OptionHolder(); @Override public ResourcePackManifest manifest() { @@ -92,7 +91,7 @@ public record GeyserResourcePack( @Override public Collection defaultOptions() { - return Collections.unmodifiableCollection(defaultOptions); + return optionHolder.immutableValues(); } public Builder contentKey(@NonNull String contentKey) { @@ -101,23 +100,15 @@ public record GeyserResourcePack( return this; } - public Builder defaultOptions(ResourcePackOption... defaultOptions) { - this.defaultOptions.addAll(Arrays.stream(defaultOptions).toList()); + public Builder defaultOptions(@NonNull ResourcePackOption... defaultOptions) { + Objects.requireNonNull(defaultOptions); + this.optionHolder.add(defaultOptions); return this; } public GeyserResourcePack build() { - // Check for duplicates - Set types = new HashSet<>(); - for (ResourcePackOption option : defaultOptions) { - if (!types.add(option.type())) { - throw new IllegalArgumentException("Duplicate resource pack option " + option + "!"); - } - } - types.clear(); - - GeyserResourcePack pack = new GeyserResourcePack(codec, manifest, contentKey, defaultOptions); - defaultOptions.forEach(option -> option.validate(pack)); + GeyserResourcePack pack = new GeyserResourcePack(codec, manifest, contentKey, optionHolder); + optionHolder.validateOptions(pack); return 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 7ea211481..6b04d6d3e 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,6 +38,8 @@ 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; 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 new file mode 100644 index 000000000..46fa2e313 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/pack/option/OptionHolder.java @@ -0,0 +1,108 @@ +/* + * 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.option; + +import org.geysermc.geyser.api.pack.ResourcePack; +import org.geysermc.geyser.api.pack.option.PriorityOption; +import org.geysermc.geyser.api.pack.option.ResourcePackOption; +import org.geysermc.geyser.pack.GeyserResourcePack; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class OptionHolder extends HashMap { + + public void add(ResourcePackOption option) { + if (super.containsKey(option.type())) { + super.replace(option.type(), option); + } else { + super.put(option.type(), option); + } + } + + public void add(ResourcePackOption... options) { + for (ResourcePackOption option : options) { + add(option); + } + } + + public ResourcePackOption getOrDefault(ResourcePackOption.Type type, + GeyserResourcePack pack, + ResourcePackOption defaultValue) { + ResourcePackOption option = super.get(type); + if (option != null) { + return option; + } + + ResourcePackOption packOption = pack.options().get(type); + if (packOption != null) { + return packOption; + } + + return defaultValue; + } + + public void remove(ResourcePackOption option) { + super.remove(option.type()); + } + + public OptionHolder() { + super(); + add(PriorityOption.NORMAL); + } + + public void validateOptions(ResourcePack pack) { + values().forEach(option -> option.validate(pack)); + } + + /** + * @return the options of this option holder in an immutable collection + */ + public Collection immutableValues() { + return Collections.unmodifiableCollection(values()); + } + + /** + * @return the options of this option holder, with fallbacks to options of a {@link GeyserResourcePack} + * if they're not already overridden here + */ + public Collection immutableValues(GeyserResourcePack pack) { + if (pack.options().isEmpty()) { + return immutableValues(); + } + + // Create a map to hold the combined values + Map combinedOptions = new HashMap<>(this); + + // Add options from the pack if not already overridden by this OptionHolder + pack.options().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/registry/Registries.java b/core/src/main/java/org/geysermc/geyser/registry/Registries.java index 95d2f9f28..1c30b422b 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -33,14 +33,22 @@ import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.PotionMixData; import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.api.pack.ResourcePack; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.item.type.Item; -import org.geysermc.geyser.registry.loader.*; +import org.geysermc.geyser.pack.GeyserResourcePack; +import org.geysermc.geyser.registry.loader.BiomeIdentifierRegistryLoader; +import org.geysermc.geyser.registry.loader.BlockEntityRegistryLoader; +import org.geysermc.geyser.registry.loader.ParticleTypesRegistryLoader; +import org.geysermc.geyser.registry.loader.PotionMixRegistryLoader; +import org.geysermc.geyser.registry.loader.ProviderRegistryLoader; +import org.geysermc.geyser.registry.loader.RecipeRegistryLoader; +import org.geysermc.geyser.registry.loader.RegistryLoaders; +import org.geysermc.geyser.registry.loader.SoundEventsRegistryLoader; +import org.geysermc.geyser.registry.loader.SoundRegistryLoader; +import org.geysermc.geyser.registry.loader.SoundTranslatorRegistryLoader; import org.geysermc.geyser.registry.populator.ItemRegistryPopulator; import org.geysermc.geyser.registry.populator.PacketRegistryPopulator; -import org.geysermc.geyser.registry.loader.RecipeRegistryLoader; import org.geysermc.geyser.registry.provider.ProviderSupplier; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ParticleMapping; @@ -56,7 +64,14 @@ import org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEvent; import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType; import org.geysermc.mcprotocollib.protocol.data.game.recipe.RecipeType; -import java.util.*; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; /** * Holds all the common registries in Geyser. @@ -143,9 +158,9 @@ public final class Registries { public static final SimpleMappedRegistry> RECIPES = SimpleMappedRegistry.create("mappings/recipes.nbt", RecipeRegistryLoader::new); /** - * A mapped registry holding {@link ResourcePack}'s with the pack uuid as keys. + * 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 7df215464..1c688c895 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 @@ -55,7 +55,7 @@ import java.util.zip.ZipFile; /** * Loads {@link ResourcePack}s within a {@link Path} directory, firing the {@link GeyserLoadResourcePacksEvent}. */ -public class ResourcePackLoader implements RegistryLoader> { +public class ResourcePackLoader implements RegistryLoader> { static final PathMatcher PACK_MATCHER = FileSystems.getDefault().getPathMatcher("glob:**.{zip,mcpack}"); @@ -65,8 +65,8 @@ public class ResourcePackLoader implements RegistryLoader load(Path directory) { - Map packMap = new HashMap<>(); + public Map load(Path directory) { + Map packMap = new HashMap<>(); if (!Files.exists(directory)) { try { @@ -101,7 +101,7 @@ public class ResourcePackLoader implements RegistryLoader