Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-11-20 15:00:11 +01:00
attempt at implementing resource pack order specifying
Dieser Commit ist enthalten in:
Ursprung
c316d09754
Commit
a868ced1a7
@ -27,6 +27,9 @@ 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;
|
||||
|
||||
/**
|
||||
* Represents a resource pack sent to Bedrock clients
|
||||
@ -63,12 +66,15 @@ public interface ResourcePack {
|
||||
String contentKey();
|
||||
|
||||
/**
|
||||
* The default subpack to tell Bedrock clients to load. Lack of a subpack to load is represented by an empty string.
|
||||
* 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 the subpack name, or an empty string if not set.
|
||||
* @return a collection of default {@link ResourcePackOption}s
|
||||
*/
|
||||
@NonNull
|
||||
String defaultSubpackName();
|
||||
Collection<ResourcePackOption> defaultOptions();
|
||||
|
||||
/**
|
||||
* Creates a resource pack with the given {@link PackCodec}.
|
||||
@ -98,18 +104,44 @@ public interface ResourcePack {
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* @return the {@link ResourcePackManifest} of this resource pack
|
||||
*/
|
||||
ResourcePackManifest manifest();
|
||||
|
||||
/**
|
||||
* @return the {@link PackCodec} of this resource pack
|
||||
*/
|
||||
PackCodec codec();
|
||||
|
||||
/**
|
||||
* @return the current content key, or an empty string if not set
|
||||
*/
|
||||
String contentKey();
|
||||
|
||||
String defaultSubpackName();
|
||||
|
||||
/**
|
||||
* Sets a content key for this resource pack.
|
||||
*
|
||||
* @param contentKey the content key
|
||||
* @return this builder
|
||||
*/
|
||||
Builder contentKey(@NonNull String contentKey);
|
||||
|
||||
Builder defaultSubpackName(@NonNull String subpackName);
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
ResourcePack build();
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,8 @@ import org.geysermc.geyser.api.GeyserApi;
|
||||
|
||||
/**
|
||||
* Allows specifying a pack priority that decides the order on how packs are sent to the client.
|
||||
* Multiple resource packs can override each other. The higher the priority, the
|
||||
* 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 {
|
||||
|
||||
@ -37,8 +38,19 @@ public interface PriorityOption extends ResourcePackOption {
|
||||
PriorityOption NORMAL = PriorityOption.priority(5);
|
||||
PriorityOption LOW = PriorityOption.priority(0);
|
||||
|
||||
/**
|
||||
* The priority of the resource pack
|
||||
*
|
||||
* @return priority
|
||||
*/
|
||||
int priority();
|
||||
|
||||
/**
|
||||
* Constructs a priority option based on a value between 0 and 10
|
||||
*
|
||||
* @param priority an integer that is above 0, but smaller than 10
|
||||
* @return the priority option
|
||||
*/
|
||||
static PriorityOption priority(int priority) {
|
||||
if (priority < 0 || priority > 10) {
|
||||
throw new IllegalArgumentException("Priority must be between 0 and 10 inclusive!");
|
||||
|
@ -26,15 +26,21 @@
|
||||
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.cloudburstmc.protocol.bedrock.packet.ResourcePackStackPacket;
|
||||
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.option.PriorityOption;
|
||||
import org.geysermc.geyser.api.pack.option.ResourcePackOption;
|
||||
import org.geysermc.geyser.api.pack.option.SubpackOption;
|
||||
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.HashMap;
|
||||
@ -42,28 +48,72 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksEvent {
|
||||
|
||||
@Getter
|
||||
private final Map<String, ResourcePack> packs;
|
||||
private final Map<String, Collection<ResourcePackOption>> options;
|
||||
private final Map<String, Collection<ResourcePackOption>> options = new HashMap<>();
|
||||
|
||||
public SessionLoadResourcePacksEventImpl(GeyserSession session) {
|
||||
super(session);
|
||||
this.packs = new Object2ObjectLinkedOpenHashMap<>(Registries.RESOURCE_PACKS.get());
|
||||
this.options = new HashMap<>();
|
||||
}
|
||||
|
||||
public @NonNull Map<String, ResourcePack> getPacks() {
|
||||
return packs;
|
||||
this.packs.values().forEach(
|
||||
pack -> options.put(pack.manifest().header().uuid().toString(), pack.defaultOptions())
|
||||
);
|
||||
}
|
||||
|
||||
public LinkedList<ResourcePackStackPacket.Entry> orderedPacks() {
|
||||
// TODO sort by priority here
|
||||
|
||||
return new LinkedList<>();
|
||||
return 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 (higher priority first)
|
||||
.sorted((entry1, entry2) -> Integer.compare(entry2.getValue(), entry1.getValue()))
|
||||
// Extract the ResourcePack from the sorted entries
|
||||
.map(entry -> {
|
||||
ResourcePack pack = entry.getKey();
|
||||
ResourcePackManifest.Header header = pack.manifest().header();
|
||||
return new ResourcePackStackPacket.Entry(header.uuid().toString(), header.version().toString(), getSubpackName(header.uuid()));
|
||||
})
|
||||
// Collect to a LinkedList
|
||||
.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().toString()).stream()
|
||||
// Filter to find the PriorityOption
|
||||
.filter(option -> option instanceof PriorityOption)
|
||||
// Map to the priority value
|
||||
.mapToInt(option -> ((PriorityOption) option).priority())
|
||||
// Get the highest priority (or a default value, if none found)
|
||||
.max().orElse(PriorityOption.NORMAL.priority());
|
||||
}
|
||||
|
||||
public List<ResourcePacksInfoPacket.Entry> infoPacketEntries() {
|
||||
List<ResourcePacksInfoPacket.Entry> entries = new ArrayList<>();
|
||||
|
||||
for (ResourcePack 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)
|
||||
);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private String getSubpackName(UUID uuid) {
|
||||
return options.get(uuid.toString()).stream()
|
||||
.filter(option -> option instanceof SubpackOption)
|
||||
.map(option -> ((SubpackOption) option).subpackName())
|
||||
.findFirst()
|
||||
.orElse(""); // Return an empty string if none is found
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @NonNull List<ResourcePack> resourcePacks() {
|
||||
return List.copyOf(packs.values());
|
||||
@ -92,9 +142,8 @@ public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksE
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourcePackOption> options(UUID resourcePack) {
|
||||
Collection<ResourcePackOption> packOptions = options.get(resourcePack.toString());
|
||||
return packOptions == null ? List.of() : Collections.unmodifiableCollection(packOptions);
|
||||
public Collection<ResourcePackOption> options(UUID uuid) {
|
||||
return Collections.unmodifiableCollection(options.get(uuid.toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -203,12 +203,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
this.geyser.eventBus().fire(this.resourcePackLoadEvent);
|
||||
|
||||
ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
|
||||
for (ResourcePack pack : this.resourcePackLoadEvent.resourcePacks()) {
|
||||
ResourcePackManifest.Header header = pack.manifest().header();
|
||||
resourcePacksInfo.getResourcePackInfos().add(new ResourcePacksInfoPacket.Entry(
|
||||
header.uuid().toString(), header.version().toString(), pack.codec().size(), pack.contentKey(),
|
||||
pack.defaultSubpackName(), header.uuid().toString(), false, false));
|
||||
}
|
||||
resourcePacksInfo.getResourcePackInfos().addAll(this.resourcePackLoadEvent.infoPacketEntries());
|
||||
resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().getConfig().isForceResourcePacks());
|
||||
session.sendUpstreamPacket(resourcePacksInfo);
|
||||
|
||||
@ -235,7 +230,6 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
break;
|
||||
|
||||
case HAVE_ALL_PACKS:
|
||||
// TODO apply pack order here
|
||||
ResourcePackStackPacket stackPacket = new ResourcePackStackPacket();
|
||||
stackPacket.setExperimentsPreviouslyToggled(false);
|
||||
stackPacket.setForcedToAccept(false); // Leaving this as false allows the player to choose to download or not
|
||||
|
@ -25,16 +25,25 @@
|
||||
|
||||
package org.geysermc.geyser.pack;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public record GeyserResourcePack(
|
||||
PackCodec codec,
|
||||
ResourcePackManifest manifest,
|
||||
String contentKey,
|
||||
String defaultSubpackName
|
||||
Collection<ResourcePackOption> defaultOptions
|
||||
) implements ResourcePack {
|
||||
|
||||
/**
|
||||
@ -43,10 +52,9 @@ public record GeyserResourcePack(
|
||||
public static final int CHUNK_SIZE = 102400;
|
||||
|
||||
public GeyserResourcePack(PackCodec codec, ResourcePackManifest manifest, String contentKey) {
|
||||
this(codec, manifest, contentKey, "");
|
||||
this(codec, manifest, contentKey, new ArrayList<>(List.of(PriorityOption.NORMAL)));
|
||||
}
|
||||
|
||||
|
||||
public static class Builder implements ResourcePack.Builder {
|
||||
|
||||
public Builder(PackCodec codec, ResourcePackManifest manifest) {
|
||||
@ -62,8 +70,8 @@ public record GeyserResourcePack(
|
||||
|
||||
private final PackCodec codec;
|
||||
private final ResourcePackManifest manifest;
|
||||
private String contentKey;
|
||||
private String defaultSubpackName;
|
||||
private String contentKey = "";
|
||||
private final Collection<ResourcePackOption> defaultOptions = new ArrayList<>(List.of(PriorityOption.NORMAL));
|
||||
|
||||
@Override
|
||||
public ResourcePackManifest manifest() {
|
||||
@ -77,38 +85,29 @@ public record GeyserResourcePack(
|
||||
|
||||
@Override
|
||||
public String contentKey() {
|
||||
return this.contentKey == null ? "" : this.contentKey;
|
||||
return contentKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultSubpackName() {
|
||||
return this.defaultSubpackName == null ? "" : this.defaultSubpackName;
|
||||
public Collection<ResourcePackOption> defaultOptions() {
|
||||
return Collections.unmodifiableCollection(defaultOptions);
|
||||
}
|
||||
|
||||
public Builder contentKey(@Nullable String contentKey) {
|
||||
public Builder contentKey(@NonNull String contentKey) {
|
||||
Objects.requireNonNull(contentKey);
|
||||
this.contentKey = contentKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaultSubpackName(@Nullable String subpackName) {
|
||||
if (manifest.subpacks().stream().anyMatch(subpack -> subpack.name().equals(subpackName))) {
|
||||
this.defaultSubpackName = subpackName;
|
||||
} else {
|
||||
throw new IllegalArgumentException("A subpack with the name '" + subpackName + "' does not exist!");
|
||||
}
|
||||
public Builder defaultOptions(ResourcePackOption... defaultOptions) {
|
||||
this.defaultOptions.addAll(Arrays.stream(defaultOptions).toList());
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeyserResourcePack build() {
|
||||
if (contentKey == null) {
|
||||
contentKey = "";
|
||||
}
|
||||
if (defaultSubpackName == null) {
|
||||
defaultSubpackName = "";
|
||||
}
|
||||
|
||||
return new GeyserResourcePack(codec, manifest, contentKey, defaultSubpackName);
|
||||
GeyserResourcePack pack = new GeyserResourcePack(codec, manifest, contentKey, defaultOptions);
|
||||
defaultOptions.forEach(option -> option.validate(pack));
|
||||
return pack;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren