Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-11-20 15:00:11 +01:00
Merge remote resource packs, change the default pack option system
Dieser Commit ist enthalten in:
Ursprung
9279c70624
Commit
c13f9281d5
@ -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<ResourcePackOption> options(UUID uuid);
|
||||
public abstract Collection<ResourcePackOption<?>> options(@NonNull UUID uuid);
|
||||
|
||||
/**
|
||||
* Unregisters a resource pack from being sent to the client.
|
||||
|
@ -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<ResourcePack> 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<ResourcePack> 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<ResourcePackOption<?>> options(@NonNull UUID uuid);
|
||||
|
||||
/**
|
||||
* Unregisters a {@link ResourcePack} from being sent to clients.
|
||||
|
@ -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<Path> resourcePacks) implements Event {
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* These can be overridden in the {@link org.geysermc.geyser.api.event.bedrock.SessionLoadResourcePacksEvent}
|
||||
*
|
||||
* @return a collection of default {@link ResourcePackOption}s
|
||||
*/
|
||||
Collection<ResourcePackOption> 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<ResourcePackOption> defaultOptions();
|
||||
|
||||
/**
|
||||
* Sets default options for this resource pack.
|
||||
*
|
||||
* @return this builder
|
||||
*/
|
||||
Builder defaultOptions(ResourcePackOption... defaultOptions);
|
||||
|
||||
/**
|
||||
* @return the resource pack
|
||||
*/
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<Double> {
|
||||
|
||||
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
|
||||
*
|
||||
|
@ -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<T> {
|
||||
|
||||
/**
|
||||
* @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.
|
||||
|
@ -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<String> {
|
||||
|
||||
/**
|
||||
* 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, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -96,7 +96,8 @@ public interface GeyserConfiguration {
|
||||
|
||||
boolean isForceResourcePacks();
|
||||
|
||||
List<String> getResourcePackUrls();
|
||||
// TODO configurate - till then, api only
|
||||
//List<String> getResourcePackUrls();
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
boolean isXboxAchievementsEnabled();
|
||||
|
@ -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<UUID, ResourcePackHolder> packs;
|
||||
|
||||
private final Map<String, ResourcePack> packs;
|
||||
|
||||
public GeyserDefineResourcePacksEventImpl(Map<String, ResourcePack> packMap) {
|
||||
public GeyserDefineResourcePacksEventImpl(Map<UUID, ResourcePackHolder> packMap) {
|
||||
this.packs = packMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<ResourcePack> 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<ResourcePack> 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<ResourcePackOption<?>> 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<UUID, GeyserResourcePack> packs;
|
||||
private final Map<UUID, OptionHolder> options = new HashMap<>();
|
||||
private final Map<UUID, ResourcePackHolder> packs;
|
||||
private final Map<UUID, OptionHolder> options;
|
||||
|
||||
public SessionLoadResourcePacksEventImpl(GeyserSession session) {
|
||||
super(session);
|
||||
this.packs = new Object2ObjectLinkedOpenHashMap<>(Registries.RESOURCE_PACKS.get());
|
||||
}
|
||||
|
||||
public LinkedList<ResourcePackStackPacket.Entry> orderedPacks() {
|
||||
TreeSet<Map.Entry<GeyserResourcePack, Double>> 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<ResourcePacksInfoPacket.Entry> infoPacketEntries() {
|
||||
List<ResourcePacksInfoPacket.Entry> 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<ResourcePack> 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<ResourcePackOption<?>> 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<ResourcePackStackPacket.Entry> orderedPacks() {
|
||||
TreeSet<Map.Entry<GeyserResourcePack, Double>> 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<ResourcePacksInfoPacket.Entry> infoPacketEntries() {
|
||||
List<ResourcePacksInfoPacket.Entry> 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<ResourcePacksInfoPacket.CDNEntry> cdnEntries() {
|
||||
List<ResourcePacksInfoPacket.CDNEntry> 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<ResourcePackOption> 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) {
|
||||
|
@ -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());
|
||||
|
@ -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<Key, Integer> keyIdMapping) {
|
||||
if (holderSet == null) {
|
||||
return new HolderSet(new int[]{});
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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<ResourcePackOption> 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<ResourcePackOption> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
39
core/src/main/java/org/geysermc/geyser/pack/ResourcePackHolder.java
Normale Datei
39
core/src/main/java/org/geysermc/geyser/pack/ResourcePackHolder.java
Normale Datei
@ -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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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<ResourcePackOption.Type, ResourcePackOption> {
|
||||
public class OptionHolder extends HashMap<ResourcePackOption.Type, ResourcePackOption<?>> {
|
||||
|
||||
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<ResourcePackOption.Type, ResourcePackO
|
||||
}
|
||||
}
|
||||
|
||||
public void add(ResourcePackOption... options) {
|
||||
for (ResourcePackOption option : options) {
|
||||
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);
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> 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<T>) 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<T>) 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<ResourcePackOption.Type, ResourcePackO
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the options of this option holder in an immutable collection
|
||||
* @return the options of this option optionHolder in an immutable collection
|
||||
*/
|
||||
public Collection<ResourcePackOption> immutableValues() {
|
||||
public Collection<ResourcePackOption<?>> 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<ResourcePackOption> immutableValues(GeyserResourcePack pack) {
|
||||
if (pack.options().isEmpty()) {
|
||||
public Collection<ResourcePackOption<?>> immutableValues(OptionHolder defaultValues) {
|
||||
if (defaultValues.isEmpty()) {
|
||||
return immutableValues();
|
||||
}
|
||||
|
||||
// Create a map to hold the combined values
|
||||
Map<ResourcePackOption.Type, ResourcePackOption> combinedOptions = new HashMap<>(this);
|
||||
Map<ResourcePackOption.Type, ResourcePackOption<?>> 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());
|
||||
|
@ -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 : "";
|
||||
}
|
||||
}
|
||||
|
@ -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<Map<UUID, GeyserResourcePack>> RESOURCE_PACKS = DeferredRegistry.create(GeyserImpl.getInstance().packDirectory(), SimpleMappedRegistry::create, RegistryLoaders.RESOURCE_PACKS);
|
||||
public static final DeferredRegistry<Map<UUID, ResourcePackHolder>> RESOURCE_PACKS = DeferredRegistry.create(GeyserImpl.getInstance().packDirectory(), SimpleMappedRegistry::create, RegistryLoaders.RESOURCE_PACKS);
|
||||
|
||||
/**
|
||||
* A mapped registry holding sound identifiers to their corresponding {@link SoundMapping}.
|
||||
|
@ -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<Path, Map<UUID, GeyserResourcePack>> {
|
||||
public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, ResourcePackHolder>> {
|
||||
|
||||
/**
|
||||
* Used to keep track of remote resource packs that the client rejected.
|
||||
@ -85,8 +86,8 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Geyser
|
||||
* Loop through the packs directory and locate valid resource pack files
|
||||
*/
|
||||
@Override
|
||||
public Map<UUID, GeyserResourcePack> load(Path directory) {
|
||||
Map<UUID, GeyserResourcePack> packMap = new Object2ObjectOpenHashMap<>();
|
||||
public Map<UUID, ResourcePackHolder> load(Path directory) {
|
||||
Map<UUID, ResourcePackHolder> packMap = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
if (!Files.exists(directory)) {
|
||||
try {
|
||||
@ -122,20 +123,22 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Geyser
|
||||
for (Path path : event.resourcePacks()) {
|
||||
try {
|
||||
GeyserResourcePack pack = readPack(path).build();
|
||||
packMap.put(pack.uuid(), pack);
|
||||
packMap.put(pack.uuid(), ResourcePackHolder.of(pack));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Load all remote resource packs before firing the new event
|
||||
packMap.putAll(loadRemotePacks());
|
||||
|
||||
GeyserDefineResourcePacksEventImpl defineEvent = new GeyserDefineResourcePacksEventImpl(packMap);
|
||||
GeyserImpl.getInstance().eventBus().fire(defineEvent);
|
||||
return defineEvent.getPacks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a resource pack at the given file. Also searches for a file in the same directory, with the same name
|
||||
* Reads a resource pack builder at the given file. Also searches for a file in the same directory, with the same name
|
||||
* but suffixed by ".key", containing the content key. If such file does not exist, no content key is stored.
|
||||
*
|
||||
* @param path the file to read from, in ZIP format
|
||||
@ -171,10 +174,10 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Geyser
|
||||
* @return a {@link GeyserResourcePack} representation
|
||||
* @throws IllegalArgumentException if there was an error reading the pack.
|
||||
*/
|
||||
public static GeyserResourcePack readPack(GeyserUrlPackCodec codec) throws IllegalArgumentException {
|
||||
public static GeyserResourcePack.Builder readPack(GeyserUrlPackCodec codec) throws IllegalArgumentException {
|
||||
Path path = codec.getFallback().path();
|
||||
ResourcePackManifest manifest = readManifest(path, codec.url());
|
||||
return new GeyserResourcePack(codec, manifest, codec.contentKey());
|
||||
return new GeyserResourcePack.Builder(codec, manifest);
|
||||
}
|
||||
|
||||
private static ResourcePackManifest readManifest(Path path, String packLocation) throws IllegalArgumentException {
|
||||
@ -213,28 +216,29 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Geyser
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, ResourcePack> loadRemotePacks() {
|
||||
private Map<UUID, ResourcePackHolder> 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<String> remotePackUrls = instance.getConfig().getResourcePackUrls();
|
||||
Map<String, ResourcePack> packMap = new Object2ObjectOpenHashMap<>();
|
||||
//List<String> remotePackUrls = instance.getConfig().getResourcePackUrls();
|
||||
List<String> remotePackUrls = List.of();
|
||||
Map<UUID, ResourcePackHolder> 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<Path, Map<UUID, Geyser
|
||||
*
|
||||
* @param codec the codec of the resource pack that wasn't successfully downloaded by a Bedrock client.
|
||||
*/
|
||||
public static void testRemotePack(GeyserSession session, UrlPackCodec codec, String packId, String packVersion) {
|
||||
public static void testRemotePack(GeyserSession session, UrlPackCodec codec, UUID packId, String packVersion) {
|
||||
if (CACHED_FAILED_PACKS.getIfPresent(codec.url()) == null) {
|
||||
String url = codec.url();
|
||||
CACHED_FAILED_PACKS.put(url, codec);
|
||||
@ -282,9 +286,8 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Geyser
|
||||
return; // Already warned about
|
||||
}
|
||||
|
||||
ResourcePack newPack = ResourcePackLoader.readPack(pathPackCodec.path());
|
||||
UUID newUUID = newPack.manifest().header().uuid();
|
||||
if (newUUID.toString().equals(packId)) {
|
||||
GeyserResourcePack newPack = readPack(pathPackCodec.path()).build();
|
||||
if (newPack.uuid().equals(packId)) {
|
||||
if (packVersion.equals(newPack.manifest().header().version().toString())) {
|
||||
GeyserImpl.getInstance().getLogger().info("No version or pack change detected: Was the resource pack server down?");
|
||||
} else {
|
||||
@ -298,14 +301,14 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Geyser
|
||||
// This should be safe to do as we're not directly using registries to read packs.
|
||||
// Instead, they're cached per-session in the SessionLoadResourcePacks event
|
||||
Registries.RESOURCE_PACKS.get().remove(packId);
|
||||
Registries.RESOURCE_PACKS.get().put(newUUID.toString(), newPack);
|
||||
Registries.RESOURCE_PACKS.get().put(newPack.uuid(), ResourcePackHolder.of(newPack));
|
||||
|
||||
if (codec instanceof GeyserUrlPackCodec geyserUrlPackCodec && geyserUrlPackCodec.getFallback() != null) {
|
||||
// Other implementations could, in theory, not have a fallback
|
||||
if (codec instanceof GeyserUrlPackCodec geyserUrlPackCodec
|
||||
&& geyserUrlPackCodec.getFallback() != null) {
|
||||
Path path = geyserUrlPackCodec.getFallback().path();
|
||||
try {
|
||||
GeyserImpl.getInstance().getScheduledThread().schedule(() -> {
|
||||
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<Path, Map<UUID, Geyser
|
||||
public static void clear() {
|
||||
Registries.RESOURCE_PACKS.get().clear();
|
||||
CACHED_FAILED_PACKS.invalidateAll();
|
||||
|
||||
}
|
||||
|
||||
public static void cleanupRemotePacks() {
|
||||
@ -380,10 +382,10 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Geyser
|
||||
|
||||
int count = 0;
|
||||
final long expireTime = (((long) 1000 * 60 * 60)); // one hour
|
||||
for (File imageFile : Objects.requireNonNull(cacheFolder.listFiles())) {
|
||||
if (imageFile.lastModified() < System.currentTimeMillis() - expireTime) {
|
||||
for (File cachedPack : Objects.requireNonNull(cacheFolder.listFiles())) {
|
||||
if (cachedPack.lastModified() < System.currentTimeMillis() - expireTime) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
imageFile.delete();
|
||||
cachedPack.delete();
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ public abstract class AbstractBlockInventoryTranslator extends BaseInventoryTran
|
||||
|
||||
/**
|
||||
* @param size the amount of slots that the inventory adds alongside the base inventory slots
|
||||
* @param holder the custom block holder
|
||||
* @param holder the custom block optionHolder
|
||||
* @param updater updater
|
||||
*/
|
||||
public AbstractBlockInventoryTranslator(int size, InventoryHolder holder, InventoryUpdater updater) {
|
||||
|
@ -167,14 +167,6 @@ above-bedrock-nether-building: false
|
||||
# want to download the resource packs.
|
||||
force-resource-packs: true
|
||||
|
||||
# A list of links to send to the client to download resource packs from.
|
||||
# These must be direct links to the resource pack, not a link to a page containing the resource pack.
|
||||
# If you enter a link here, Geyser will download the resource pack once to check if it's in a valid format.
|
||||
# See https://wiki.geysermc.org/geyser/packs for more info.
|
||||
resource-pack-urls:
|
||||
# GeyserOptionalPack
|
||||
- "https://download.geysermc.org/v2/projects/geyseroptionalpack/versions/latest/builds/latest/downloads/geyseroptionalpack"
|
||||
|
||||
# Allows Xbox achievements to be unlocked.
|
||||
xbox-achievements-enabled: false
|
||||
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren