mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-04-17 19:12:14 +02:00
Merge branch 'master' into creative-item-categories
This commit is contained in:
commit
7c029886cd
87 changed files with 24193 additions and 1182 deletions
README.mdgradle.properties
api/src/main/java/org/geysermc/geyser/api
event
bedrock
lifecycle
pack
bootstrap
bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord
mod/src/main/java/org/geysermc/geyser/platform/mod
spigot/src/main/java/org/geysermc/geyser/platform/spigot
standalone/src/main/java/org/geysermc/geyser/platform/standalone
velocity/src/main/java/org/geysermc/geyser/platform/velocity
viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy
build-logic/src/main/kotlin
core/src
main
java/org/geysermc/geyser
GeyserImpl.javaGeyserLogger.java
entity
event/type
inventory/holder
network
pack
registry
scoreboard
session
skin
translator
inventory/chest
protocol
bedrock
java
util
resources
test/java/org/geysermc/geyser
registry/loader
scoreboard/network
gradle
|
@ -15,7 +15,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
|
|||
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
|
||||
|
||||
## Supported Versions
|
||||
Geyser is currently supporting Minecraft Bedrock 1.21.40 - 1.21.62 and Minecraft Java 1.21.4. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
|
||||
Geyser is currently supporting Minecraft Bedrock 1.21.40 - 1.21.70 and Minecraft Java 1.21.4. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
|
||||
|
||||
## Setting Up
|
||||
Take a look [here](https://geysermc.org/wiki/geyser/setup/) for how to set up Geyser.
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
|||
|
||||
/**
|
||||
* Called when Geyser session connected to a Java remote server and is in a play-ready state.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public final class SessionJoinEvent extends ConnectionEvent {
|
||||
public SessionJoinEvent(@NonNull GeyserConnection connection) {
|
||||
|
|
|
@ -26,15 +26,20 @@
|
|||
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;
|
||||
import org.geysermc.geyser.api.pack.exception.ResourcePackException;
|
||||
import org.geysermc.geyser.api.pack.option.ResourcePackOption;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Called when Geyser initializes a session for a new Bedrock client and is in the process of sending resource packs.
|
||||
* Called when Geyser initializes a session for a new Bedrock client and is in the process of sending {@link ResourcePack}'s.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent {
|
||||
public SessionLoadResourcePacksEvent(@NonNull GeyserConnection connection) {
|
||||
|
@ -42,26 +47,70 @@ public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets an unmodifiable list of {@link ResourcePack}s that will be sent to the client.
|
||||
* Gets the {@link ResourcePack}'s that will be sent to this {@link GeyserConnection}.
|
||||
* To remove packs, use {@link #unregister(UUID)}, as the list returned
|
||||
* by this method is unmodifiable.
|
||||
*
|
||||
* @return an unmodifiable list of resource packs that will be sent to the client.
|
||||
* @return an unmodifiable list of {@link ResourcePack}'s
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract @NonNull List<ResourcePack> resourcePacks();
|
||||
|
||||
/**
|
||||
* Registers a {@link ResourcePack} to be sent to the client.
|
||||
*
|
||||
* @param resourcePack a resource pack that will be sent to the client.
|
||||
* @return true if the resource pack was added successfully,
|
||||
* or false if already present
|
||||
* @deprecated Use {{@link #register(ResourcePack, ResourcePackOption[])}} instead
|
||||
*/
|
||||
public abstract boolean register(@NonNull ResourcePack resourcePack);
|
||||
@Deprecated
|
||||
public abstract boolean register(@NonNull ResourcePack pack);
|
||||
|
||||
/**
|
||||
* Unregisters a resource pack from being sent to the client.
|
||||
* Registers a {@link ResourcePack} to be sent to the client, optionally alongside
|
||||
* specific {@link ResourcePackOption}'s specifying how it will be applied by the client.
|
||||
*
|
||||
* @param uuid the UUID of the resource pack
|
||||
* @return true whether the resource pack was removed from the list of resource packs.
|
||||
* @param pack the {@link ResourcePack} that will be sent to the client
|
||||
* @param options {@link ResourcePackOption}'s that specify how the client loads the pack
|
||||
* @throws ResourcePackException if an issue occurred during pack registration
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract void register(@NonNull ResourcePack pack, @Nullable ResourcePackOption<?>... options);
|
||||
|
||||
/**
|
||||
* Sets {@link ResourcePackOption}'s for a {@link ResourcePack}.
|
||||
* This method can also be used to override options for resource packs already registered in the
|
||||
* {@link org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent}.
|
||||
*
|
||||
* @param uuid the uuid of the resource pack to register the options for
|
||||
* @param options the {@link ResourcePackOption}'s to register for the resource pack
|
||||
* @throws ResourcePackException if an issue occurred during {@link ResourcePackOption} registration
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption<?>... options);
|
||||
|
||||
/**
|
||||
* Returns a collection of {@link ResourcePackOption}'s for a registered {@link ResourcePack}.
|
||||
* The collection returned here is not modifiable.
|
||||
*
|
||||
* @param uuid the {@link ResourcePack} for which the options are set
|
||||
* @return a collection of {@link ResourcePackOption}'s
|
||||
* @throws ResourcePackException if the pack was not registered
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract Collection<ResourcePackOption<?>> options(@NonNull UUID uuid);
|
||||
|
||||
/**
|
||||
* Returns the current {@link ResourcePackOption}, or null, for a given {@link ResourcePackOption.Type}.
|
||||
*
|
||||
* @param uuid the {@link ResourcePack} for which to query this option type
|
||||
* @param type the {@link ResourcePackOption.Type} of the option to query
|
||||
* @throws ResourcePackException if the queried option is invalid or not present on the resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract @Nullable ResourcePackOption<?> option(@NonNull UUID uuid, ResourcePackOption.@NonNull Type type);
|
||||
|
||||
/**
|
||||
* Unregisters a {@link ResourcePack} from the list of packs sent to this {@link GeyserConnection}.
|
||||
*
|
||||
* @param uuid the UUID of the {@link ResourcePack} to be removed
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract boolean unregister(@NonNull UUID uuid);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2025 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.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.exception.ResourcePackException;
|
||||
import org.geysermc.geyser.api.pack.option.ResourcePackOption;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Called when {@link ResourcePack}'s are loaded within Geyser.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract class GeyserDefineResourcePacksEvent implements Event {
|
||||
|
||||
/**
|
||||
* Gets the {@link ResourcePack}'s that will be sent to connecting Bedrock clients.
|
||||
* To remove packs, use {@link #unregister(UUID)}, as the list returned
|
||||
* by this method is unmodifiable.
|
||||
*
|
||||
* @return an unmodifiable list of {@link ResourcePack}'s
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract @NonNull List<ResourcePack> resourcePacks();
|
||||
|
||||
/**
|
||||
* Registers a {@link ResourcePack} to be sent to the client, optionally alongside
|
||||
* {@link ResourcePackOption}'s specifying how it will be applied on clients.
|
||||
*
|
||||
* @param pack a resource pack that will be sent to the client
|
||||
* @param options {@link ResourcePackOption}'s that specify how clients load the pack
|
||||
* @throws ResourcePackException if an issue occurred during pack registration
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract void register(@NonNull ResourcePack pack, @Nullable ResourcePackOption<?>... options);
|
||||
|
||||
/**
|
||||
* Sets {@link ResourcePackOption}'s for a {@link ResourcePack}.
|
||||
*
|
||||
* @param uuid the uuid of the resource pack to register the options for
|
||||
* @param options the {@link ResourcePackOption}'s to register for the resource pack
|
||||
* @throws ResourcePackException if an issue occurred during {@link ResourcePackOption} registration
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption<?>... options);
|
||||
|
||||
/**
|
||||
* Returns a collection of {@link ResourcePackOption}'s for a registered {@link ResourcePack}.
|
||||
* The collection returned here is not modifiable.
|
||||
*
|
||||
* @param uuid the uuid of the {@link ResourcePack} for which the options are set
|
||||
* @return a collection of {@link ResourcePackOption}'s
|
||||
* @throws ResourcePackException if the pack was not registered
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract Collection<ResourcePackOption<?>> options(@NonNull UUID uuid);
|
||||
|
||||
/**
|
||||
* Returns the current option, or null, for a given {@link ResourcePackOption.Type}.
|
||||
*
|
||||
* @param uuid the {@link ResourcePack} for which to query this option type
|
||||
* @param type the {@link ResourcePackOption.Type} of the option to query
|
||||
* @throws ResourcePackException if the queried option is invalid or not present on the resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract @Nullable ResourcePackOption<?> option(@NonNull UUID uuid, ResourcePackOption.@NonNull Type type);
|
||||
|
||||
/**
|
||||
* Unregisters a {@link ResourcePack} from the list of packs sent to connecting Bedrock clients.
|
||||
*
|
||||
* @param uuid the UUID of the {@link ResourcePack} to be removed
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract void unregister(@NonNull UUID uuid);
|
||||
}
|
|
@ -32,9 +32,8 @@ import java.nio.file.Path;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Called when resource packs are loaded within Geyser.
|
||||
*
|
||||
* @param resourcePacks a mutable list of the currently listed resource packs
|
||||
* @deprecated Use the {@link GeyserDefineResourcePacksEvent} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public record GeyserLoadResourcePacksEvent(@NonNull List<Path> resourcePacks) implements Event {
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.geysermc.geyser.api.extension.ExtensionManager;
|
|||
|
||||
/**
|
||||
* Called when Geyser is about to reload. Primarily aimed at extensions, so they can decide on their own what to reload.
|
||||
* After this event is fired, some lifecycle events can be fired again - such as the {@link GeyserLoadResourcePacksEvent}.
|
||||
* After this event is fired, some lifecycle events can be fired again - such as the {@link GeyserDefineResourcePacksEvent}.
|
||||
*
|
||||
* @param extensionManager the extension manager
|
||||
* @param eventBus the event bus
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.nio.file.Path;
|
|||
/**
|
||||
* Represents a pack codec that can be used
|
||||
* to provide resource packs to clients.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract class PackCodec {
|
||||
|
||||
|
@ -42,6 +43,7 @@ public abstract class PackCodec {
|
|||
* Gets the sha256 hash of the resource pack.
|
||||
*
|
||||
* @return the hash of the resource pack
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract byte @NonNull [] sha256();
|
||||
|
||||
|
@ -49,34 +51,66 @@ public abstract class PackCodec {
|
|||
* Gets the resource pack size.
|
||||
*
|
||||
* @return the resource pack file size
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract long size();
|
||||
|
||||
/**
|
||||
* Serializes the given resource pack into a byte buffer.
|
||||
* @deprecated use {@link #serialize()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@NonNull
|
||||
public SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws IOException {
|
||||
return serialize();
|
||||
};
|
||||
|
||||
/**
|
||||
* Serializes the given codec into a byte buffer.
|
||||
*
|
||||
* @param resourcePack the resource pack to serialize
|
||||
* @return the serialized resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
public abstract SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws IOException;
|
||||
public abstract SeekableByteChannel serialize() throws IOException;
|
||||
|
||||
/**
|
||||
* Creates a new resource pack from this codec.
|
||||
*
|
||||
* @return the new resource pack
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
protected abstract ResourcePack create();
|
||||
|
||||
/**
|
||||
* Creates a new resource pack builder from this codec.
|
||||
*
|
||||
* @return the new resource pack builder
|
||||
* @since 2.6.2
|
||||
*/
|
||||
protected abstract ResourcePack.@NonNull Builder createBuilder();
|
||||
|
||||
/**
|
||||
* Creates a new pack provider from the given path.
|
||||
*
|
||||
* @param path the path to create the pack provider from
|
||||
* @return the new pack provider
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
public static PackCodec path(@NonNull Path path) {
|
||||
return GeyserApi.api().provider(PathPackCodec.class, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new pack provider from the given url.
|
||||
*
|
||||
* @param url the url to create the pack provider from
|
||||
* @return the new pack provider
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
public static PackCodec url(@NonNull String url) {
|
||||
return GeyserApi.api().provider(UrlPackCodec.class, url);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import java.nio.file.Path;
|
|||
/**
|
||||
* Represents a pack codec that creates a resource
|
||||
* pack from a path on the filesystem.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract class PathPackCodec extends PackCodec {
|
||||
|
||||
|
@ -39,7 +40,8 @@ public abstract class PathPackCodec extends PackCodec {
|
|||
* Gets the path of the resource pack.
|
||||
*
|
||||
* @return the path of the resource pack
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
public abstract Path path();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,12 +26,17 @@
|
|||
package org.geysermc.geyser.api.pack;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.common.returnsreceiver.qual.This;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a resource pack sent to Bedrock clients
|
||||
* <p>
|
||||
* This representation of a resource pack only contains what
|
||||
* Geyser requires to send it to the client.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public interface ResourcePack {
|
||||
|
||||
|
@ -39,6 +44,7 @@ public interface ResourcePack {
|
|||
* The {@link PackCodec codec} for this pack.
|
||||
*
|
||||
* @return the codec for this pack
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
PackCodec codec();
|
||||
|
@ -47,6 +53,7 @@ public interface ResourcePack {
|
|||
* Gets the resource pack manifest.
|
||||
*
|
||||
* @return the resource pack manifest
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
ResourcePackManifest manifest();
|
||||
|
@ -55,18 +62,83 @@ public interface ResourcePack {
|
|||
* Gets the content key of the resource pack. Lack of a content key is represented by an empty String.
|
||||
*
|
||||
* @return the content key of the resource pack
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
String contentKey();
|
||||
|
||||
/**
|
||||
* Shortcut for getting the UUID from the {@link ResourcePackManifest}.
|
||||
*
|
||||
* @return the resource pack uuid
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
default UUID uuid() {
|
||||
return manifest().header().uuid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a resource pack with the given {@link PackCodec}.
|
||||
*
|
||||
* @param codec the pack codec
|
||||
* @return the resource pack
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
static ResourcePack create(@NonNull PackCodec codec) {
|
||||
return codec.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Builder} for a resource pack.
|
||||
* It can be used to set a content key.
|
||||
*
|
||||
* @param codec the {@link PackCodec} to base the builder on
|
||||
* @return a {@link Builder} to build a resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
static Builder builder(@NonNull PackCodec codec) {
|
||||
return GeyserApi.api().provider(Builder.class, codec);
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for a resource pack. It allows providing a content key manually.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* @return the {@link ResourcePackManifest} of this resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
ResourcePackManifest manifest();
|
||||
|
||||
/**
|
||||
* @return the {@link PackCodec} of this resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
PackCodec codec();
|
||||
|
||||
/**
|
||||
* @return the current content key, or an empty string if not set
|
||||
* @since 2.6.2
|
||||
*/
|
||||
String contentKey();
|
||||
|
||||
/**
|
||||
* Sets a content key for this resource pack.
|
||||
*
|
||||
* @param contentKey the content key
|
||||
* @return this builder
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@This Builder contentKey(@NonNull String contentKey);
|
||||
|
||||
/**
|
||||
* @return the resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
ResourcePack build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,55 +26,99 @@
|
|||
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.event.bedrock.SessionLoadResourcePacksEvent;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent;
|
||||
import org.geysermc.geyser.api.pack.option.SubpackOption;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a resource pack manifest.
|
||||
* Represents a Bedrock edition resource pack manifest (manifest.json).
|
||||
* All resource packs are required to have such a file as it identifies the resource pack.
|
||||
* See <a href="https://learn.microsoft.com/en-us/minecraft/creator/reference/content/addonsreference/examples/addonmanifest?view=minecraft-bedrock-stable">
|
||||
* Microsoft's docs for more info</a>.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public interface ResourcePackManifest {
|
||||
|
||||
/**
|
||||
* Gets the format version of the resource pack.
|
||||
* <p>
|
||||
* "1" is used for skin packs,
|
||||
* "2" is used for resource and behavior packs, and world templates.
|
||||
*
|
||||
* @return the format version
|
||||
* @since 2.1.1
|
||||
*/
|
||||
int formatVersion();
|
||||
|
||||
/**
|
||||
* Gets the header of the resource pack.
|
||||
* Gets the {@link Header} of the resource pack.
|
||||
*
|
||||
* @return the header
|
||||
* @return the {@link Header}
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
Header header();
|
||||
|
||||
/**
|
||||
* Gets the modules of the resource pack.
|
||||
* Gets the {@link Module}'s of the resource pack.
|
||||
*
|
||||
* @return the modules
|
||||
* @return a collection of modules
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
Collection<? extends Module> modules();
|
||||
|
||||
/**
|
||||
* Gets the dependencies of the resource pack.
|
||||
* Gets the {@link Dependency}'s of the resource pack.
|
||||
*
|
||||
* @return the dependencies
|
||||
* @return a collection of dependencies
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
Collection<? extends Dependency> dependencies();
|
||||
|
||||
/**
|
||||
* Gets the {@link Subpack}'s of the resource pack.
|
||||
* See <a href="https://learn.microsoft.com/en-us/minecraft/creator/documents/utilizingsubpacks">Microsoft's docs on subpacks
|
||||
* </a> for more information.
|
||||
*
|
||||
* @return a collection of subpacks
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
Collection<? extends Subpack> subpacks();
|
||||
|
||||
/**
|
||||
* Gets the {@link Setting}'s of the resource pack.
|
||||
* These are shown to Bedrock client's in the resource pack settings menu (<a href="https://learn.microsoft.com/en-us/minecraft/creator/documents/media/utilizingsubpacks/subpackgif.gif?view=minecraft-bedrock-stable">see here</a>)
|
||||
* to inform users about what the resource pack and sub-packs include.
|
||||
*
|
||||
* @return a collection of settings
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
Collection<? extends Setting> settings();
|
||||
|
||||
/**
|
||||
* Represents the header of a resource pack.
|
||||
* It contains the main information about the resource pack, such as
|
||||
* the name, description, or uuid.
|
||||
* See <a href="https://learn.microsoft.com/en-us/minecraft/creator/reference/content/addonsreference/examples/addonmanifest?view=minecraft-bedrock-stable#header">
|
||||
* Microsoft's docs for further details on headers.</a>
|
||||
* @since 2.1.1
|
||||
*/
|
||||
interface Header {
|
||||
|
||||
/**
|
||||
* Gets the UUID of the resource pack.
|
||||
* Gets the UUID of the resource pack. It is a unique identifier that differentiates this resource pack from any other resource pack.
|
||||
* Bedrock clients will cache resource packs, and download resource packs when the uuid is new (or the version changes).
|
||||
*
|
||||
* @return the UUID
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
UUID uuid();
|
||||
|
@ -83,6 +127,7 @@ public interface ResourcePackManifest {
|
|||
* Gets the version of the resource pack.
|
||||
*
|
||||
* @return the version
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
Version version();
|
||||
|
@ -91,6 +136,7 @@ public interface ResourcePackManifest {
|
|||
* Gets the name of the resource pack.
|
||||
*
|
||||
* @return the name
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
String name();
|
||||
|
@ -99,6 +145,7 @@ public interface ResourcePackManifest {
|
|||
* Gets the description of the resource pack.
|
||||
*
|
||||
* @return the description
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
String description();
|
||||
|
@ -107,6 +154,7 @@ public interface ResourcePackManifest {
|
|||
* Gets the minimum supported Minecraft version of the resource pack.
|
||||
*
|
||||
* @return the minimum supported Minecraft version
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
Version minimumSupportedMinecraftVersion();
|
||||
|
@ -114,21 +162,29 @@ public interface ResourcePackManifest {
|
|||
|
||||
/**
|
||||
* Represents a module of a resource pack.
|
||||
* It contains information about the content type that is
|
||||
* offered by this resource pack.
|
||||
* See <a href="https://learn.microsoft.com/en-us/minecraft/creator/reference/content/addonsreference/examples/addonmanifest?view=minecraft-bedrock-stable#modules">
|
||||
* Microsoft's docs for further details on modules.</a>
|
||||
* @since 2.1.1
|
||||
*/
|
||||
interface Module {
|
||||
|
||||
/**
|
||||
* Gets the UUID of the module.
|
||||
* This should usually be different from the UUID in the {@link Header}.
|
||||
*
|
||||
* @return the UUID
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
UUID uuid();
|
||||
|
||||
/**
|
||||
* Gets the version of the module.
|
||||
* Gets the {@link Version} of the module.
|
||||
*
|
||||
* @return the version
|
||||
* @return the {@link Version}
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
Version version();
|
||||
|
@ -137,6 +193,7 @@ public interface ResourcePackManifest {
|
|||
* Gets the type of the module.
|
||||
*
|
||||
* @return the type
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
String type();
|
||||
|
@ -145,6 +202,7 @@ public interface ResourcePackManifest {
|
|||
* Gets the description of the module.
|
||||
*
|
||||
* @return the description
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
String description();
|
||||
|
@ -152,28 +210,102 @@ public interface ResourcePackManifest {
|
|||
|
||||
/**
|
||||
* Represents a dependency of a resource pack.
|
||||
* These are references to other resource packs that must be
|
||||
* present in order for this resource pack to apply.
|
||||
* See <a href="https://learn.microsoft.com/en-us/minecraft/creator/reference/content/addonsreference/examples/addonmanifest?view=minecraft-bedrock-stable#dependencies">
|
||||
* Microsoft's docs for further details on dependencies.</a>
|
||||
* @since 2.1.1
|
||||
*/
|
||||
interface Dependency {
|
||||
|
||||
/**
|
||||
* Gets the UUID of the dependency.
|
||||
* Gets the UUID of the resource pack dependency.
|
||||
*
|
||||
* @return the uuid
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
UUID uuid();
|
||||
|
||||
/**
|
||||
* Gets the version of the dependency.
|
||||
* Gets the {@link Version} of the dependency.
|
||||
*
|
||||
* @return the version
|
||||
* @return the {@link Version}
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
Version version();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a subpack of a resource pack. These are often used for "variants" of the resource pack,
|
||||
* such as lesser details, or additional features either to be determined by player's taste or adapted to the player device's performance.
|
||||
* See <a href="https://learn.microsoft.com/en-us/minecraft/creator/documents/utilizingsubpacks">Micoroft's docs</a> for more information.
|
||||
*/
|
||||
interface Subpack {
|
||||
|
||||
/**
|
||||
* Gets the folder name where this sub-pack is placed in.
|
||||
*
|
||||
* @return the folder name
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
String folderName();
|
||||
|
||||
/**
|
||||
* Gets the name of this subpack. Required for each subpack to be valid.
|
||||
* To make a Bedrock client load any subpack, register the resource pack
|
||||
* in the {@link SessionLoadResourcePacksEvent} or {@link GeyserDefineResourcePacksEvent} and specify a
|
||||
* {@link SubpackOption} with the name of the subpack to load.
|
||||
*
|
||||
* @return the subpack name
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Gets the memory tier of this Subpack, representing how much RAM a device must have to run it.
|
||||
* Each memory tier requires 0.25 GB of RAM. For example, a memory tier of 0 is no requirement,
|
||||
* and a memory tier of 4 requires 1GB of RAM.
|
||||
*
|
||||
* @return the memory tier
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@Nullable
|
||||
Float memoryTier();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a setting that is shown client-side that describe what a pack does.
|
||||
* Multiple setting entries are shown in separate paragraphs.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
interface Setting {
|
||||
|
||||
/**
|
||||
* The type of the setting. Usually just "label".
|
||||
*
|
||||
* @return the type
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
String type();
|
||||
|
||||
/**
|
||||
* The text shown for the setting.
|
||||
*
|
||||
* @return the text content
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
String text();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a version of a resource pack.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
interface Version {
|
||||
|
||||
|
@ -181,6 +313,7 @@ public interface ResourcePackManifest {
|
|||
* Gets the major version.
|
||||
*
|
||||
* @return the major version
|
||||
* @since 2.1.1
|
||||
*/
|
||||
int major();
|
||||
|
||||
|
@ -188,6 +321,7 @@ public interface ResourcePackManifest {
|
|||
* Gets the minor version.
|
||||
*
|
||||
* @return the minor version
|
||||
* @since 2.1.1
|
||||
*/
|
||||
int minor();
|
||||
|
||||
|
@ -195,6 +329,7 @@ public interface ResourcePackManifest {
|
|||
* Gets the patch version.
|
||||
*
|
||||
* @return the patch version
|
||||
* @since 2.1.1
|
||||
*/
|
||||
int patch();
|
||||
|
||||
|
@ -202,6 +337,7 @@ public interface ResourcePackManifest {
|
|||
* Gets the version formatted as a String.
|
||||
*
|
||||
* @return the version string
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull String toString();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 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.api.pack;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Represents a pack codec that creates a resource
|
||||
* pack from a URL.
|
||||
* <p>
|
||||
* Due to Bedrock limitations, the URL must:
|
||||
* <ul>
|
||||
* <li>be a direct download link to a .zip or .mcpack resource pack</li>
|
||||
* <li>use the application type `application/zip` and set a correct content length</li>
|
||||
* </ul>
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract class UrlPackCodec extends PackCodec {
|
||||
|
||||
/**
|
||||
* Gets the URL to the resource pack location.
|
||||
*
|
||||
* @return the URL of the resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
public abstract String url();
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2025 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.api.pack.exception;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* Used to indicate an exception that occurred while handling resource pack registration,
|
||||
* or during resource pack option validation.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public class ResourcePackException extends IllegalArgumentException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The {@link Cause} of this exception.
|
||||
*/
|
||||
private final Cause cause;
|
||||
|
||||
/**
|
||||
* @param cause the cause of this exception
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public ResourcePackException(Cause cause) {
|
||||
super(cause.message());
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cause the cause of this exception
|
||||
* @param message an additional, more in-depth message about the issue.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public ResourcePackException(Cause cause, String message) {
|
||||
super(message);
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cause of this exception
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public Cause cause() {
|
||||
return cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents different causes with explanatory messages stating which issue occurred.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public enum Cause {
|
||||
DUPLICATE("A resource pack with this UUID was already registered!"),
|
||||
INVALID_PACK("This resource pack is not a valid Bedrock edition resource pack!"),
|
||||
INVALID_PACK_OPTION("Attempted to register an invalid resource pack option!"),
|
||||
PACK_NOT_FOUND("No resource pack was found!"),
|
||||
UNKNOWN_IMPLEMENTATION("Use the resource pack codecs to create resource packs.");
|
||||
|
||||
private final String message;
|
||||
|
||||
/**
|
||||
* @return the message of this cause
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public String message() {
|
||||
return message;
|
||||
}
|
||||
|
||||
Cause(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.api.pack.option;
|
||||
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
|
||||
/**
|
||||
* Allows specifying a pack priority that decides the order on how packs are sent to the client.
|
||||
* If two resource packs modify the same texture - for example if one removes the pumpkin overlay and
|
||||
* the other is just making it translucent, one of the packs will override the other.
|
||||
* Specifically, the pack with the higher priority will override the pack changes of the lower priority.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public interface PriorityOption extends ResourcePackOption<Integer> {
|
||||
|
||||
PriorityOption HIGHEST = PriorityOption.priority(100);
|
||||
PriorityOption HIGH = PriorityOption.priority(50);
|
||||
PriorityOption NORMAL = PriorityOption.priority(0);
|
||||
PriorityOption LOW = PriorityOption.priority(-50);
|
||||
PriorityOption LOWEST = PriorityOption.priority(-100);
|
||||
|
||||
/**
|
||||
* Constructs a priority option based on a value between 0 and 10.
|
||||
* The higher the number, the higher will this pack appear in the resource pack stack.
|
||||
*
|
||||
* @param priority an integer that is above 0, but smaller than 10
|
||||
* @return the priority option
|
||||
* @since 2.6.2
|
||||
*/
|
||||
static PriorityOption priority(int priority) {
|
||||
if (priority < -100 || priority > 100) {
|
||||
throw new IllegalArgumentException("Priority must be between 0 and 10 inclusive!");
|
||||
}
|
||||
return GeyserApi.api().provider(PriorityOption.class, priority);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.api.pack.option;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.api.pack.exception.ResourcePackException;
|
||||
|
||||
/**
|
||||
* Represents a resource pack option that can be used to specify how a resource
|
||||
* pack is sent to Bedrock clients.
|
||||
* <p>
|
||||
* Not all options can be applied to all resource packs. For example, you cannot specify
|
||||
* a specific subpack to be loaded on resource packs that do not have subpacks.
|
||||
* To see which limitations apply to specific resource pack options, check the javadocs
|
||||
* or see the {@link #validate(ResourcePack)} method.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public interface ResourcePackOption<T> {
|
||||
|
||||
/**
|
||||
* @return the option type
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull Type type();
|
||||
|
||||
/**
|
||||
* @return the value of the option
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull T value();
|
||||
|
||||
/**
|
||||
* Used to validate a specific options for a pack.
|
||||
* Some options are not applicable to some packs.
|
||||
*
|
||||
* @param pack the resource pack to validate the option for
|
||||
* @throws ResourcePackException with the {@link ResourcePackException.Cause#INVALID_PACK_OPTION} cause
|
||||
* @since 2.6.2
|
||||
*/
|
||||
void validate(@NonNull ResourcePack pack);
|
||||
|
||||
/**
|
||||
* Represents the different types of resource pack options.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
enum Type {
|
||||
SUBPACK,
|
||||
PRIORITY,
|
||||
FALLBACK
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.api.pack.option;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
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()}.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public interface SubpackOption extends ResourcePackOption<String> {
|
||||
|
||||
/**
|
||||
* Creates a subpack option based on a {@link ResourcePackManifest.Subpack}.
|
||||
*
|
||||
* @param subpack the chosen subpack
|
||||
* @return a subpack option specifying that subpack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
static SubpackOption subpack(ResourcePackManifest.@NonNull Subpack subpack) {
|
||||
return named(subpack.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a subpack option based on a subpack name.
|
||||
*
|
||||
* @param subpackName the name of the subpack
|
||||
* @return a subpack option specifying a subpack with that name
|
||||
* @since 2.6.2
|
||||
*/
|
||||
static SubpackOption named(@NonNull String subpackName) {
|
||||
return GeyserApi.api().provider(SubpackOption.class, subpackName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a subpack option with no subpack specified.
|
||||
*
|
||||
* @return a subpack option specifying no subpack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
static SubpackOption empty() {
|
||||
return GeyserApi.api().provider(SubpackOption.class, "");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.api.pack.option;
|
||||
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
import org.geysermc.geyser.api.pack.PathPackCodec;
|
||||
import org.geysermc.geyser.api.pack.UrlPackCodec;
|
||||
|
||||
/**
|
||||
* Can be used for resource packs created with the {@link UrlPackCodec}.
|
||||
* When a Bedrock client is unable to download a resource pack from a URL, Geyser will, by default,
|
||||
* serve the resource pack over raknet (as packs are served with the {@link PathPackCodec}).
|
||||
* This option can be used to disable that behavior, and disconnect the player instead.
|
||||
* By default, the {@link UrlFallbackOption#TRUE} option is set.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public interface UrlFallbackOption extends ResourcePackOption<Boolean> {
|
||||
|
||||
UrlFallbackOption TRUE = fallback(true);
|
||||
UrlFallbackOption FALSE = fallback(false);
|
||||
|
||||
/**
|
||||
* Whether to fall back to serving packs over the raknet connection
|
||||
*
|
||||
* @param fallback whether to fall back
|
||||
* @return a UrlFallbackOption with the specified behavior
|
||||
* @since 2.6.2
|
||||
*/
|
||||
static UrlFallbackOption fallback(boolean fallback) {
|
||||
return GeyserApi.api().provider(UrlFallbackOption.class, fallback);
|
||||
}
|
||||
|
||||
}
|
|
@ -75,4 +75,11 @@ public class GeyserBungeeLogger implements GeyserLogger {
|
|||
info(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Object... arguments) {
|
||||
if (debug) {
|
||||
info(String.format(message, arguments));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,13 @@ public class GeyserModLogger implements GeyserLogger {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Object... arguments) {
|
||||
if (debug) {
|
||||
logger.info(message, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
|
|
|
@ -75,4 +75,11 @@ public class GeyserSpigotLogger implements GeyserLogger {
|
|||
info(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Object... arguments) {
|
||||
if (debug) {
|
||||
info(String.format(message, arguments));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,6 +115,11 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey
|
|||
log.debug(ChatColor.GRAY + message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Object... arguments) {
|
||||
log.debug(ChatColor.GRAY + message, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
Configurator.setLevel(log.getName(), debug ? Level.DEBUG : Level.INFO);
|
||||
|
|
|
@ -73,4 +73,11 @@ public class GeyserVelocityLogger implements GeyserLogger {
|
|||
info(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Object... arguments) {
|
||||
if (debug) {
|
||||
logger.info(message, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,13 @@ public class GeyserViaProxyLogger implements GeyserLogger, GeyserCommandSource {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Object... arguments) {
|
||||
if (this.debug) {
|
||||
this.debug(String.format(message, arguments));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
|
|
|
@ -62,11 +62,12 @@ repositories {
|
|||
name = "viaversion"
|
||||
}
|
||||
|
||||
// For Adventure snapshots
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
|
||||
// Jitpack for e.g. MCPL
|
||||
maven("https://jitpack.io") {
|
||||
content { includeGroupByRegex("com\\.github\\..*") }
|
||||
}
|
||||
|
||||
// For Adventure snapshots
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ import org.geysermc.geyser.network.GameProtocol;
|
|||
import org.geysermc.geyser.network.netty.GeyserServer;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.registry.loader.ResourcePackLoader;
|
||||
import org.geysermc.geyser.registry.provider.ProviderSupplier;
|
||||
import org.geysermc.geyser.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
@ -677,9 +678,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||
runIfNonNull(newsHandler, NewsHandler::shutdown);
|
||||
runIfNonNull(erosionUnixListener, UnixSocketClientListener::close);
|
||||
|
||||
if (Registries.RESOURCE_PACKS.loaded()) {
|
||||
Registries.RESOURCE_PACKS.get().clear();
|
||||
}
|
||||
ResourcePackLoader.clear();
|
||||
|
||||
this.setEnabled(false);
|
||||
}
|
||||
|
|
|
@ -103,6 +103,15 @@ public interface GeyserLogger extends GeyserCommandSource {
|
|||
debug(String.valueOf(object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs and formats a message to console if debug mode is enabled,
|
||||
* with the provided arguments.
|
||||
*
|
||||
* @param message the message to log
|
||||
* @param arguments the arguments to replace in the message
|
||||
*/
|
||||
void debug(String message, Object... arguments);
|
||||
|
||||
/**
|
||||
* Sets if the logger should print debug messages
|
||||
*
|
||||
|
|
|
@ -28,7 +28,7 @@ package org.geysermc.geyser.entity;
|
|||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.factory.EntityFactory;
|
||||
import org.geysermc.geyser.entity.properties.GeyserEntityProperties;
|
||||
import org.geysermc.geyser.entity.properties.VanillaEntityProperties;
|
||||
import org.geysermc.geyser.entity.type.AbstractArrowEntity;
|
||||
import org.geysermc.geyser.entity.type.AbstractWindChargeEntity;
|
||||
import org.geysermc.geyser.entity.type.AreaEffectCloudEntity;
|
||||
|
@ -462,6 +462,7 @@ public final class EntityDefinitions {
|
|||
EGG = EntityDefinition.inherited(ThrowableItemEntity::new, throwableItemBase)
|
||||
.type(EntityType.EGG)
|
||||
.heightAndWidth(0.25f)
|
||||
.properties(VanillaEntityProperties.CLIMATE_VARIANT)
|
||||
.build();
|
||||
ENDER_PEARL = EntityDefinition.inherited(ThrowableItemEntity::new, throwableItemBase)
|
||||
.type(EntityType.ENDER_PEARL)
|
||||
|
@ -685,15 +686,7 @@ public final class EntityDefinitions {
|
|||
.addTranslator(MetadataTypes.BOOLEAN, CreakingEntity::setActive)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, CreakingEntity::setIsTearingDown)
|
||||
.addTranslator(MetadataTypes.OPTIONAL_POSITION, CreakingEntity::setHomePos)
|
||||
.properties(new GeyserEntityProperties.Builder()
|
||||
.addEnum(CreakingEntity.CREAKING_STATE,
|
||||
"neutral",
|
||||
"hostile_observed",
|
||||
"hostile_unobserved",
|
||||
"twitching",
|
||||
"crumbling")
|
||||
.addInt(CreakingEntity.CREAKING_SWAYING_TICKS, 0, 6)
|
||||
.build())
|
||||
.properties(VanillaEntityProperties.CREAKING)
|
||||
.build();
|
||||
CREEPER = EntityDefinition.inherited(CreeperEntity::new, mobEntityBase)
|
||||
.type(EntityType.CREEPER)
|
||||
|
@ -946,15 +939,7 @@ public final class EntityDefinitions {
|
|||
ARMADILLO = EntityDefinition.inherited(ArmadilloEntity::new, ageableEntityBase)
|
||||
.type(EntityType.ARMADILLO)
|
||||
.height(0.65f).width(0.7f)
|
||||
.properties(new GeyserEntityProperties.Builder()
|
||||
.addEnum(
|
||||
"minecraft:armadillo_state",
|
||||
"unrolled",
|
||||
"rolled_up",
|
||||
"rolled_up_peeking",
|
||||
"rolled_up_relaxing",
|
||||
"rolled_up_unrolling")
|
||||
.build())
|
||||
.properties(VanillaEntityProperties.ARMADILLO)
|
||||
.addTranslator(MetadataTypes.ARMADILLO_STATE, ArmadilloEntity::setArmadilloState)
|
||||
.build();
|
||||
AXOLOTL = EntityDefinition.inherited(AxolotlEntity::new, ageableEntityBase)
|
||||
|
@ -967,19 +952,19 @@ public final class EntityDefinitions {
|
|||
BEE = EntityDefinition.inherited(BeeEntity::new, ageableEntityBase)
|
||||
.type(EntityType.BEE)
|
||||
.heightAndWidth(0.6f)
|
||||
.properties(new GeyserEntityProperties.Builder()
|
||||
.addBoolean("minecraft:has_nectar")
|
||||
.build())
|
||||
.properties(VanillaEntityProperties.BEE)
|
||||
.addTranslator(MetadataTypes.BYTE, BeeEntity::setBeeFlags)
|
||||
.addTranslator(MetadataTypes.INT, BeeEntity::setAngerTime)
|
||||
.build();
|
||||
CHICKEN = EntityDefinition.inherited(ChickenEntity::new, ageableEntityBase)
|
||||
.type(EntityType.CHICKEN)
|
||||
.height(0.7f).width(0.4f)
|
||||
.properties(VanillaEntityProperties.CLIMATE_VARIANT)
|
||||
.build();
|
||||
COW = EntityDefinition.inherited(CowEntity::new, ageableEntityBase)
|
||||
.type(EntityType.COW)
|
||||
.height(1.4f).width(0.9f)
|
||||
.properties(VanillaEntityProperties.CLIMATE_VARIANT)
|
||||
.build();
|
||||
FOX = EntityDefinition.inherited(FoxEntity::new, ageableEntityBase)
|
||||
.type(EntityType.FOX)
|
||||
|
@ -1030,6 +1015,7 @@ public final class EntityDefinitions {
|
|||
PIG = EntityDefinition.inherited(PigEntity::new, ageableEntityBase)
|
||||
.type(EntityType.PIG)
|
||||
.heightAndWidth(0.9f)
|
||||
.properties(VanillaEntityProperties.CLIMATE_VARIANT)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, (pigEntity, entityMetadata) -> pigEntity.setFlag(EntityFlag.SADDLED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.addTranslator(MetadataTypes.INT, PigEntity::setBoost)
|
||||
.build();
|
||||
|
@ -1176,6 +1162,7 @@ public final class EntityDefinitions {
|
|||
WOLF = EntityDefinition.inherited(WolfEntity::new, tameableEntityBase)
|
||||
.type(EntityType.WOLF)
|
||||
.height(0.85f).width(0.6f)
|
||||
.properties(VanillaEntityProperties.WOLF_SOUND_VARIANT)
|
||||
// "Begging" on wiki.vg, "Interested" in Nukkit - the tilt of the head
|
||||
.addTranslator(MetadataTypes.BOOLEAN, (wolfEntity, entityMetadata) -> wolfEntity.setFlag(EntityFlag.INTERESTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.addTranslator(MetadataTypes.INT, WolfEntity::setCollarColor)
|
||||
|
|
|
@ -162,4 +162,4 @@ public class GeyserEntityProperties {
|
|||
return new GeyserEntityProperties(properties, propertyIndices);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2025 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.entity.properties;
|
||||
|
||||
import org.geysermc.geyser.entity.type.living.monster.CreakingEntity;
|
||||
|
||||
public class VanillaEntityProperties {
|
||||
|
||||
public static final String CLIMATE_VARIANT_ID = "minecraft:climate_variant";
|
||||
|
||||
public static final GeyserEntityProperties ARMADILLO = new GeyserEntityProperties.Builder()
|
||||
.addEnum("minecraft:armadillo_state",
|
||||
"unrolled",
|
||||
"rolled_up",
|
||||
"rolled_up_peeking",
|
||||
"rolled_up_relaxing",
|
||||
"rolled_up_unrolling")
|
||||
.build();
|
||||
|
||||
public static final GeyserEntityProperties BEE = new GeyserEntityProperties.Builder()
|
||||
.addBoolean("minecraft:has_nectar")
|
||||
.build();
|
||||
|
||||
public static final GeyserEntityProperties CLIMATE_VARIANT = new GeyserEntityProperties.Builder()
|
||||
.addEnum(CLIMATE_VARIANT_ID,
|
||||
"temperate",
|
||||
"warm",
|
||||
"cold")
|
||||
.build();
|
||||
|
||||
public static final GeyserEntityProperties CREAKING = new GeyserEntityProperties.Builder()
|
||||
.addEnum(CreakingEntity.CREAKING_STATE,
|
||||
"neutral",
|
||||
"hostile_observed",
|
||||
"hostile_unobserved",
|
||||
"twitching",
|
||||
"crumbling")
|
||||
.addInt(CreakingEntity.CREAKING_SWAYING_TICKS, 0, 6)
|
||||
.build();
|
||||
|
||||
public static final GeyserEntityProperties WOLF_SOUND_VARIANT = new GeyserEntityProperties.Builder()
|
||||
.addEnum("minecraft:sound_variant",
|
||||
"default",
|
||||
"big",
|
||||
"cute",
|
||||
"grumpy",
|
||||
"mad",
|
||||
"puglin",
|
||||
"sad")
|
||||
.build();
|
||||
}
|
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.entity.type;
|
|||
import lombok.Getter;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
|
@ -85,7 +86,16 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
|
|||
|
||||
// Required to be able to move on land 1.16.200+ or apply gravity not in the water 1.16.100+
|
||||
dirtyMetadata.put(EntityDataTypes.IS_BUOYANT, true);
|
||||
dirtyMetadata.put(EntityDataTypes.BUOYANCY_DATA, BUOYANCY_DATA);
|
||||
dirtyMetadata.put(EntityDataTypes.BUOYANCY_DATA, BUOYANCY_DATA);;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeMetadata() {
|
||||
super.initializeMetadata();
|
||||
if (GameProtocol.is1_21_70orHigher(session)) {
|
||||
// Without this flag you cant stand on boats
|
||||
setFlag(EntityFlag.COLLIDABLE, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -200,6 +200,7 @@ public class Entity implements GeyserEntity {
|
|||
addAdditionalSpawnData(addEntityPacket);
|
||||
|
||||
valid = true;
|
||||
|
||||
session.sendUpstreamPacket(addEntityPacket);
|
||||
|
||||
flagsDirty = false;
|
||||
|
@ -372,6 +373,10 @@ public class Entity implements GeyserEntity {
|
|||
flagsDirty = false;
|
||||
}
|
||||
dirtyMetadata.apply(entityDataPacket.getMetadata());
|
||||
if (propertyManager != null && propertyManager.hasProperties()) {
|
||||
propertyManager.applyIntProperties(entityDataPacket.getProperties().getIntProperties());
|
||||
propertyManager.applyFloatProperties(entityDataPacket.getProperties().getFloatProperties());
|
||||
}
|
||||
session.sendUpstreamPacket(entityDataPacket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,10 +28,13 @@ package org.geysermc.geyser.entity.type;
|
|||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.properties.VanillaEntityProperties;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -53,6 +56,14 @@ public class ThrowableItemEntity extends ThrowableEntity {
|
|||
age = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSpawnData(AddEntityPacket addEntityPacket) {
|
||||
if (definition.entityType() == EntityType.EGG) {
|
||||
propertyManager.add(VanillaEntityProperties.CLIMATE_VARIANT_ID, "temperate");
|
||||
propertyManager.applyIntProperties(addEntityPacket.getProperties().getIntProperties());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeMetadata() {
|
||||
super.initializeMetadata();
|
||||
|
|
|
@ -27,7 +27,9 @@ package org.geysermc.geyser.entity.type.living.animal;
|
|||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.properties.VanillaEntityProperties;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
|
@ -41,6 +43,12 @@ public class ChickenEntity extends AnimalEntity {
|
|||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSpawnData(AddEntityPacket addEntityPacket) {
|
||||
propertyManager.add(VanillaEntityProperties.CLIMATE_VARIANT_ID, "temperate");
|
||||
propertyManager.applyIntProperties(addEntityPacket.getProperties().getIntProperties());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Tag<Item> getFoodTag() {
|
||||
|
|
|
@ -30,7 +30,9 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.properties.VanillaEntityProperties;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
|
@ -48,6 +50,12 @@ public class CowEntity extends AnimalEntity {
|
|||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSpawnData(AddEntityPacket addEntityPacket) {
|
||||
propertyManager.add(VanillaEntityProperties.CLIMATE_VARIANT_ID, "temperate");
|
||||
propertyManager.applyIntProperties(addEntityPacket.getProperties().getIntProperties());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.entity.type.living.animal;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
|
@ -52,6 +53,11 @@ public class MooshroomEntity extends CowEntity {
|
|||
dirtyMetadata.put(EntityDataTypes.VARIANT, isBrown ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSpawnData(AddEntityPacket addEntityPacket) {
|
||||
// There are no variants for mooshroom cows, so far
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
|
|
|
@ -31,7 +31,9 @@ import org.cloudburstmc.math.vector.Vector2f;
|
|||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.properties.VanillaEntityProperties;
|
||||
import org.geysermc.geyser.entity.type.Tickable;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
import org.geysermc.geyser.entity.vehicle.BoostableVehicleComponent;
|
||||
|
@ -58,6 +60,12 @@ public class PigEntity extends AnimalEntity implements Tickable, ClientVehicle {
|
|||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSpawnData(AddEntityPacket addEntityPacket) {
|
||||
propertyManager.add(VanillaEntityProperties.CLIMATE_VARIANT_ID, "temperate");
|
||||
propertyManager.applyIntProperties(addEntityPacket.getProperties().getIntProperties());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Tag<Item> getFoodTag() {
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
|
@ -67,6 +68,12 @@ public class WolfEntity extends TameableEntity {
|
|||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSpawnData(AddEntityPacket addEntityPacket) {
|
||||
propertyManager.add("minecraft:sound_variant", "default");
|
||||
propertyManager.applyIntProperties(addEntityPacket.getProperties().getIntProperties());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTameableFlags(ByteEntityMetadata entityMetadata) {
|
||||
super.setTameableFlags(entityMetadata);
|
||||
|
|
|
@ -30,7 +30,7 @@ import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
|||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
|
@ -57,7 +57,7 @@ public class EndermanEntity extends MonsterEntity {
|
|||
//TODO see if Bedrock controls this differently
|
||||
// Java Edition this controls which ambient sound is used
|
||||
if (entityMetadata.getPrimitiveValue()) {
|
||||
LevelSoundEvent2Packet packet = new LevelSoundEvent2Packet();
|
||||
LevelSoundEventPacket packet = new LevelSoundEventPacket();
|
||||
packet.setSound(SoundEvent.STARE);
|
||||
packet.setPosition(this.position);
|
||||
packet.setExtraData(-1);
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 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.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.exception.ResourcePackException;
|
||||
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;
|
||||
|
||||
public GeyserDefineResourcePacksEventImpl(Map<UUID, ResourcePackHolder> packMap) {
|
||||
this.packs = packMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<ResourcePack> resourcePacks() {
|
||||
return packs.values().stream().map(ResourcePackHolder::resourcePack).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(@NonNull ResourcePack resourcePack, @Nullable ResourcePackOption<?>... options) {
|
||||
Objects.requireNonNull(resourcePack, "resource pack must not be null!");
|
||||
if (!(resourcePack instanceof GeyserResourcePack pack)) {
|
||||
throw new ResourcePackException(ResourcePackException.Cause.UNKNOWN_IMPLEMENTATION);
|
||||
}
|
||||
|
||||
UUID uuid = resourcePack.uuid();
|
||||
if (packs.containsKey(uuid)) {
|
||||
throw new ResourcePackException(ResourcePackException.Cause.DUPLICATE);
|
||||
}
|
||||
|
||||
ResourcePackHolder holder = ResourcePackHolder.of(pack);
|
||||
attemptRegisterOptions(holder, options);
|
||||
packs.put(uuid, holder);
|
||||
}
|
||||
|
||||
@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 ResourcePackException(ResourcePackException.Cause.PACK_NOT_FOUND);
|
||||
}
|
||||
|
||||
attemptRegisterOptions(holder, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourcePackOption<?>> options(@NonNull UUID uuid) {
|
||||
Objects.requireNonNull(uuid);
|
||||
ResourcePackHolder packHolder = packs.get(uuid);
|
||||
if (packHolder == null) {
|
||||
throw new ResourcePackException(ResourcePackException.Cause.PACK_NOT_FOUND);
|
||||
}
|
||||
|
||||
return packHolder.optionHolder().immutableValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ResourcePackOption<?> option(@NonNull UUID uuid, ResourcePackOption.@NonNull Type type) {
|
||||
Objects.requireNonNull(uuid);
|
||||
Objects.requireNonNull(type);
|
||||
|
||||
ResourcePackHolder packHolder = packs.get(uuid);
|
||||
if (packHolder == null) {
|
||||
throw new ResourcePackException(ResourcePackException.Cause.PACK_NOT_FOUND);
|
||||
}
|
||||
|
||||
return packHolder.optionHolder().get(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(@NonNull UUID uuid) {
|
||||
packs.remove(uuid);
|
||||
}
|
||||
|
||||
private void attemptRegisterOptions(@NonNull ResourcePackHolder holder, @Nullable ResourcePackOption<?>... options) {
|
||||
if (options == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
holder.optionHolder().validateAndAdd(holder.pack(), options);
|
||||
}
|
||||
}
|
|
@ -25,45 +25,200 @@
|
|||
|
||||
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;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackStackPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ResourcePacksInfoPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
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.exception.ResourcePackException;
|
||||
import org.geysermc.geyser.api.pack.option.PriorityOption;
|
||||
import org.geysermc.geyser.api.pack.option.ResourcePackOption;
|
||||
import org.geysermc.geyser.pack.GeyserResourcePack;
|
||||
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;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksEvent {
|
||||
|
||||
private final Map<UUID, ResourcePack> packs;
|
||||
/**
|
||||
* The packs for this Session. A {@link ResourcePackHolder} may contain resource pack options registered
|
||||
* during the {@link org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent}.
|
||||
*/
|
||||
@Getter
|
||||
private final Map<UUID, ResourcePackHolder> packs;
|
||||
|
||||
public SessionLoadResourcePacksEventImpl(GeyserSession session, Map<UUID, ResourcePack> packMap) {
|
||||
/**
|
||||
* The additional, per-session options for the resource packs of this session.
|
||||
* These options are prioritized over the "default" options registered
|
||||
* in the {@link org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent}
|
||||
*/
|
||||
private final Map<UUID, OptionHolder> sessionPackOptionOverrides;
|
||||
|
||||
public SessionLoadResourcePacksEventImpl(GeyserSession session) {
|
||||
super(session);
|
||||
this.packs = packMap;
|
||||
}
|
||||
|
||||
public @NonNull Map<UUID, ResourcePack> getPacks() {
|
||||
return packs;
|
||||
this.packs = new Object2ObjectLinkedOpenHashMap<>(Registries.RESOURCE_PACKS.get());
|
||||
this.sessionPackOptionOverrides = new Object2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<ResourcePack> resourcePacks() {
|
||||
return List.copyOf(packs.values());
|
||||
return packs.values().stream().map(ResourcePackHolder::resourcePack).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean register(@NonNull ResourcePack resourcePack) {
|
||||
UUID packID = resourcePack.manifest().header().uuid();
|
||||
if (packs.containsValue(resourcePack) || packs.containsKey(packID)) {
|
||||
try {
|
||||
register(resourcePack, PriorityOption.NORMAL);
|
||||
} catch (ResourcePackException e) {
|
||||
GeyserImpl.getInstance().getLogger().error("An exception occurred while registering resource pack: " + e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
packs.put(resourcePack.manifest().header().uuid(), resourcePack);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(@NonNull ResourcePack resourcePack, @Nullable ResourcePackOption<?>... options) {
|
||||
Objects.requireNonNull(resourcePack);
|
||||
if (!(resourcePack instanceof GeyserResourcePack pack)) {
|
||||
throw new ResourcePackException(ResourcePackException.Cause.UNKNOWN_IMPLEMENTATION);
|
||||
}
|
||||
|
||||
UUID uuid = resourcePack.uuid();
|
||||
if (packs.containsKey(uuid)) {
|
||||
throw new ResourcePackException(ResourcePackException.Cause.DUPLICATE);
|
||||
}
|
||||
|
||||
attemptRegisterOptions(pack, options);
|
||||
packs.put(uuid, ResourcePackHolder.of(pack));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption<?>... options) {
|
||||
Objects.requireNonNull(uuid, "uuid cannot be null");
|
||||
Objects.requireNonNull(options, "options cannot be null");
|
||||
ResourcePackHolder holder = packs.get(uuid);
|
||||
if (holder == null) {
|
||||
throw new ResourcePackException(ResourcePackException.Cause.PACK_NOT_FOUND);
|
||||
}
|
||||
|
||||
attemptRegisterOptions(holder.pack(), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourcePackOption<?>> options(@NonNull UUID uuid) {
|
||||
Objects.requireNonNull(uuid);
|
||||
ResourcePackHolder packHolder = packs.get(uuid);
|
||||
if (packHolder == null) {
|
||||
throw new ResourcePackException(ResourcePackException.Cause.PACK_NOT_FOUND);
|
||||
}
|
||||
|
||||
OptionHolder optionHolder = sessionPackOptionOverrides.get(uuid);
|
||||
if (optionHolder == null) {
|
||||
// No need to create a new session option holder
|
||||
return packHolder.optionHolder().immutableValues();
|
||||
}
|
||||
|
||||
return optionHolder.immutableValues(packHolder.optionHolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ResourcePackOption<?> option(@NonNull UUID uuid, ResourcePackOption.@NonNull Type type) {
|
||||
Objects.requireNonNull(uuid);
|
||||
Objects.requireNonNull(type);
|
||||
|
||||
ResourcePackHolder packHolder = packs.get(uuid);
|
||||
if (packHolder == null) {
|
||||
throw new ResourcePackException(ResourcePackException.Cause.PACK_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Nullable OptionHolder additionalOptions = sessionPackOptionOverrides.get(uuid);
|
||||
OptionHolder defaultHolder = packHolder.optionHolder();
|
||||
Objects.requireNonNull(defaultHolder); // should never be null
|
||||
|
||||
return OptionHolder.optionByType(type, additionalOptions, defaultHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregister(@NonNull UUID uuid) {
|
||||
sessionPackOptionOverrides.remove(uuid);
|
||||
return packs.remove(uuid) != null;
|
||||
}
|
||||
|
||||
private void attemptRegisterOptions(@NonNull GeyserResourcePack pack, @Nullable ResourcePackOption<?>... options) {
|
||||
if (options == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
OptionHolder holder = this.sessionPackOptionOverrides.computeIfAbsent(pack.uuid(), $ -> new OptionHolder());
|
||||
holder.validateAndAdd(pack, options);
|
||||
}
|
||||
|
||||
// Methods used internally for e.g. ordered packs, or resource pack entries
|
||||
|
||||
public List<ResourcePackStackPacket.Entry> orderedPacks() {
|
||||
return packs.values().stream()
|
||||
// Map each ResourcePack to a pair of (GeyserResourcePack, Priority)
|
||||
.map(holder -> new AbstractMap.SimpleEntry<>(holder.pack(), priority(holder.pack())))
|
||||
// Sort by priority in descending order
|
||||
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
|
||||
// Map the sorted entries to ResourcePackStackPacket.Entry
|
||||
.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(), header.version().toString(), pack.codec().size(), pack.contentKey(),
|
||||
subpackName(pack), header.uuid().toString(), false, false, false, subpackName(pack))
|
||||
);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
// Helper methods to get the options for a ResourcePack
|
||||
|
||||
public <T> T value(UUID uuid, ResourcePackOption.Type type, T defaultValue) {
|
||||
OptionHolder holder = sessionPackOptionOverrides.get(uuid);
|
||||
OptionHolder defaultHolder = packs.get(uuid).optionHolder();
|
||||
Objects.requireNonNull(defaultHolder); // should never be null
|
||||
|
||||
return OptionHolder.valueOrFallback(type, holder, defaultHolder, defaultValue);
|
||||
}
|
||||
|
||||
private double priority(GeyserResourcePack pack) {
|
||||
return value(pack.uuid(), ResourcePackOption.Type.PRIORITY, PriorityOption.NORMAL.value());
|
||||
}
|
||||
|
||||
private String subpackName(GeyserResourcePack pack) {
|
||||
return value(pack.uuid(), ResourcePackOption.Type.SUBPACK, "");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,19 +168,20 @@ public class BlockInventoryHolder extends InventoryHolder {
|
|||
return;
|
||||
}
|
||||
|
||||
// Bedrock broke inventory closing. I wish i was kidding.
|
||||
// "type" is explicitly passed to keep track of which inventory types can be closed without
|
||||
// ""workarounds"". yippie.
|
||||
// Further, Lecterns cannot be closed with any of the two methods below.
|
||||
|
||||
// Lecterns are special, and cannot be closed with any of the two methods below.
|
||||
if (container.isUsingRealBlock() && !(container instanceof LecternContainer)) {
|
||||
// No need to reset a block since we didn't change any blocks
|
||||
// But send a container close packet because we aren't destroying the original.
|
||||
ContainerClosePacket packet = new ContainerClosePacket();
|
||||
packet.setId((byte) inventory.getBedrockId());
|
||||
packet.setServerInitiated(true);
|
||||
packet.setType(type != null ? type : containerType);
|
||||
session.sendUpstreamPacket(packet);
|
||||
|
||||
// Here comes the ugly part. This is a manual check to filter specific containers
|
||||
// that won't close anymore with "just" a ContainerClosePacket.
|
||||
if (type != null) {
|
||||
// No need to reset a block since we didn't change any blocks
|
||||
// But send a container close packet because we aren't destroying the original.
|
||||
ContainerClosePacket packet = new ContainerClosePacket();
|
||||
packet.setId((byte) inventory.getBedrockId());
|
||||
packet.setServerInitiated(true);
|
||||
packet.setType(type);
|
||||
session.sendUpstreamPacket(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
|||
import org.cloudburstmc.protocol.bedrock.codec.v748.Bedrock_v748;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v766.Bedrock_v766;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v776.Bedrock_v776;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v786.Bedrock_v786;
|
||||
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec;
|
||||
|
@ -48,8 +49,8 @@ public final class GameProtocol {
|
|||
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
||||
* release of the game that Geyser supports.
|
||||
*/
|
||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v776.CODEC.toBuilder()
|
||||
.minecraftVersion("1.21.60")
|
||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v786.CODEC.toBuilder()
|
||||
.minecraftVersion("1.21.70")
|
||||
.build());
|
||||
|
||||
/**
|
||||
|
@ -70,6 +71,9 @@ public final class GameProtocol {
|
|||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v766.CODEC.toBuilder()
|
||||
.minecraftVersion("1.21.50 - 1.21.51")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v776.CODEC.toBuilder()
|
||||
.minecraftVersion("1.21.60 - 1.21.62")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
|
||||
}
|
||||
|
||||
|
@ -97,6 +101,10 @@ public final class GameProtocol {
|
|||
return protocolVersion < 776;
|
||||
}
|
||||
|
||||
public static boolean is1_21_70orHigher(GeyserSession session) {
|
||||
return session.protocolVersion() >= Bedrock_v786.CODEC.getProtocolVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link PacketCodec} for Minecraft: Java Edition.
|
||||
*
|
||||
|
|
|
@ -51,7 +51,7 @@ public class InvalidPacketHandler extends ChannelInboundHandlerAdapter {
|
|||
|
||||
if (!(rootCause instanceof IllegalArgumentException)) {
|
||||
// Kick users that cause exceptions
|
||||
logger.warning("Exception caught in session of" + session.bedrockUsername() + ": " + rootCause.getMessage());
|
||||
logger.warning("Exception caught in session of " + session.bedrockUsername() + ": " + rootCause.getMessage());
|
||||
session.disconnect("An internal error occurred!");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -906,4 +906,14 @@ public class LoggingPacketHandler implements BedrockPacketHandler {
|
|||
public PacketSignal handle(ServerboundDiagnosticsPacket packet) {
|
||||
return defaultHandler(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketSignal handle(UpdateClientOptionsPacket packet) {
|
||||
return defaultHandler(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketSignal handle(PlayerUpdateEntityOverridesPacket packet) {
|
||||
return defaultHandler(packet);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,10 +58,14 @@ import org.geysermc.geyser.api.network.AuthType;
|
|||
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.UrlPackCodec;
|
||||
import org.geysermc.geyser.api.pack.option.ResourcePackOption;
|
||||
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;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
@ -74,7 +78,6 @@ import java.nio.ByteBuffer;
|
|||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -199,17 +202,12 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
|
||||
geyser.getSessionManager().addPendingSession(session);
|
||||
|
||||
this.resourcePackLoadEvent = new SessionLoadResourcePacksEventImpl(session, new HashMap<>(Registries.RESOURCE_PACKS.get()));
|
||||
this.resourcePackLoadEvent = new SessionLoadResourcePacksEventImpl(session);
|
||||
this.geyser.eventBus().fire(this.resourcePackLoadEvent);
|
||||
|
||||
ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
|
||||
for (ResourcePack pack : this.resourcePackLoadEvent.resourcePacks()) {
|
||||
PackCodec codec = pack.codec();
|
||||
ResourcePackManifest.Header header = pack.manifest().header();
|
||||
resourcePacksInfo.getResourcePackInfos().add(new ResourcePacksInfoPacket.Entry(
|
||||
header.uuid(), header.version().toString(), codec.size(), pack.contentKey(),
|
||||
"", header.uuid().toString(), false, false, false, ""));
|
||||
}
|
||||
resourcePacksInfo.getResourcePackInfos().addAll(this.resourcePackLoadEvent.infoPacketEntries());
|
||||
|
||||
resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().getConfig().isForceResourcePacks());
|
||||
resourcePacksInfo.setWorldTemplateId(UUID.randomUUID());
|
||||
resourcePacksInfo.setWorldTemplateVersion("*");
|
||||
|
@ -222,7 +220,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
@Override
|
||||
public PacketSignal handle(ResourcePackClientResponsePacket packet) {
|
||||
switch (packet.getStatus()) {
|
||||
case COMPLETED:
|
||||
case COMPLETED -> {
|
||||
if (geyser.getConfig().getRemote().authType() != AuthType.ONLINE) {
|
||||
session.authenticate(session.getAuthData().name());
|
||||
} else if (!couldLoginUserByName(session.getAuthData().name())) {
|
||||
|
@ -230,30 +228,21 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
session.connect();
|
||||
}
|
||||
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.connect", session.getAuthData().name()));
|
||||
break;
|
||||
|
||||
case SEND_PACKS:
|
||||
}
|
||||
case SEND_PACKS -> {
|
||||
packsToSend.addAll(packet.getPackIds());
|
||||
sendPackDataInfo(packsToSend.pop());
|
||||
break;
|
||||
|
||||
case HAVE_ALL_PACKS:
|
||||
}
|
||||
case HAVE_ALL_PACKS -> {
|
||||
ResourcePackStackPacket stackPacket = new ResourcePackStackPacket();
|
||||
stackPacket.setExperimentsPreviouslyToggled(false);
|
||||
stackPacket.setForcedToAccept(false); // Leaving this as false allows the player to choose to download or not
|
||||
stackPacket.setGameVersion(session.getClientData().getGameVersion());
|
||||
|
||||
for (ResourcePack pack : this.resourcePackLoadEvent.resourcePacks()) {
|
||||
ResourcePackManifest.Header header = pack.manifest().header();
|
||||
stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.uuid().toString(), header.version().toString(), ""));
|
||||
}
|
||||
stackPacket.getResourcePacks().addAll(this.resourcePackLoadEvent.orderedPacks());
|
||||
|
||||
session.sendUpstreamPacket(stackPacket);
|
||||
break;
|
||||
|
||||
default:
|
||||
session.disconnect("disconnectionScreen.resourcePack");
|
||||
break;
|
||||
}
|
||||
default -> session.disconnect("disconnectionScreen.resourcePack");
|
||||
}
|
||||
|
||||
return PacketSignal.HANDLED;
|
||||
|
@ -302,10 +291,29 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
|
||||
@Override
|
||||
public PacketSignal handle(ResourcePackChunkRequestPacket packet) {
|
||||
ResourcePackHolder holder = this.resourcePackLoadEvent.getPacks().get(packet.getPackId());
|
||||
|
||||
if (holder == null) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Client {0} tried to request pack id {1} not sent to it!",
|
||||
session.bedrockUsername(), packet.getPackId());
|
||||
session.disconnect("disconnectionScreen.resourcePack");
|
||||
return PacketSignal.HANDLED;
|
||||
}
|
||||
|
||||
ResourcePack pack = holder.pack();
|
||||
ResourcePackChunkDataPacket data = new ResourcePackChunkDataPacket();
|
||||
ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(packet.getPackId());
|
||||
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(), packet.getPackVersion());
|
||||
|
||||
if (!resourcePackLoadEvent.value(pack.uuid(), ResourcePackOption.Type.FALLBACK, true)) {
|
||||
session.disconnect("Unable to provide downloaded resource pack. Contact an administrator!");
|
||||
return PacketSignal.HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
data.setChunkIndex(packet.getChunkIndex());
|
||||
data.setProgress((long) packet.getChunkIndex() * GeyserResourcePack.CHUNK_SIZE);
|
||||
data.setPackVersion(packet.getPackVersion());
|
||||
|
@ -315,10 +323,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
long remainingSize = codec.size() - offset;
|
||||
byte[] packData = new byte[(int) MathUtils.constrain(remainingSize, 0, GeyserResourcePack.CHUNK_SIZE)];
|
||||
|
||||
try (SeekableByteChannel channel = codec.serialize(pack)) {
|
||||
try (SeekableByteChannel channel = codec.serialize()) {
|
||||
channel.position(offset);
|
||||
channel.read(ByteBuffer.wrap(packData, 0, packData.length));
|
||||
} catch (IOException e) {
|
||||
session.disconnect("disconnectionScreen.resourcePack");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
@ -337,8 +346,33 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
private void sendPackDataInfo(String id) {
|
||||
ResourcePackDataInfoPacket data = new ResourcePackDataInfoPacket();
|
||||
String[] packID = id.split("_");
|
||||
UUID uuid = UUID.fromString(packID[0]);
|
||||
ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(uuid);
|
||||
|
||||
if (packID.length < 2) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Client {0} tried to request invalid pack id {1}!",
|
||||
session.bedrockUsername(), packID);
|
||||
session.disconnect("disconnectionScreen.resourcePack");
|
||||
return;
|
||||
}
|
||||
|
||||
UUID packId;
|
||||
try {
|
||||
packId = UUID.fromString(packID[0]);
|
||||
} catch (IllegalArgumentException e) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Client {0} tried to request pack with an invalid id {1})",
|
||||
session.bedrockUsername(), id);
|
||||
session.disconnect("disconnectionScreen.resourcePack");
|
||||
return;
|
||||
}
|
||||
|
||||
ResourcePackHolder holder = this.resourcePackLoadEvent.getPacks().get(packId);
|
||||
if (holder == null) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Client {0} tried to request pack id {1} not sent to it!",
|
||||
session.bedrockUsername(), id);
|
||||
session.disconnect("disconnectionScreen.resourcePack");
|
||||
return;
|
||||
}
|
||||
|
||||
ResourcePack pack = holder.pack();
|
||||
PackCodec codec = pack.codec();
|
||||
ResourcePackManifest.Header header = pack.manifest().header();
|
||||
|
||||
|
|
|
@ -25,14 +25,64 @@
|
|||
|
||||
package org.geysermc.geyser.pack;
|
||||
|
||||
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;
|
||||
|
||||
public record GeyserResourcePack(PackCodec codec, ResourcePackManifest manifest, String contentKey) implements ResourcePack {
|
||||
import java.util.Objects;
|
||||
|
||||
public record GeyserResourcePack(
|
||||
@NonNull PackCodec codec,
|
||||
@NonNull ResourcePackManifest manifest,
|
||||
@NonNull String contentKey
|
||||
) implements ResourcePack {
|
||||
|
||||
/**
|
||||
* The size of each chunk to use when sending the resource packs to clients in bytes
|
||||
*/
|
||||
public static final int CHUNK_SIZE = 102400;
|
||||
|
||||
public static class Builder implements ResourcePack.Builder {
|
||||
|
||||
public Builder(PackCodec codec, ResourcePackManifest manifest) {
|
||||
this.codec = codec;
|
||||
this.manifest = manifest;
|
||||
}
|
||||
|
||||
public Builder(PackCodec codec, ResourcePackManifest manifest, String contentKey) {
|
||||
this.codec = codec;
|
||||
this.manifest = manifest;
|
||||
this.contentKey = contentKey;
|
||||
}
|
||||
|
||||
private final PackCodec codec;
|
||||
private final ResourcePackManifest manifest;
|
||||
private String contentKey = "";
|
||||
|
||||
@Override
|
||||
public ResourcePackManifest manifest() {
|
||||
return manifest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackCodec codec() {
|
||||
return codec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String contentKey() {
|
||||
return contentKey;
|
||||
}
|
||||
|
||||
public Builder contentKey(@NonNull String contentKey) {
|
||||
Objects.requireNonNull(contentKey);
|
||||
this.contentKey = contentKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeyserResourcePack build() {
|
||||
return new GeyserResourcePack(codec, manifest, contentKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,30 @@ import org.geysermc.geyser.api.pack.ResourcePackManifest;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
public record GeyserResourcePackManifest(@JsonProperty("format_version") int formatVersion, Header header, Collection<Module> modules, Collection<Dependency> dependencies) implements ResourcePackManifest {
|
||||
public record GeyserResourcePackManifest(
|
||||
@JsonProperty("format_version") int formatVersion,
|
||||
Header header,
|
||||
Collection<Module> modules,
|
||||
Collection<Dependency> dependencies,
|
||||
Collection<Subpack> subpacks,
|
||||
Collection<Setting> settings
|
||||
) implements ResourcePackManifest {
|
||||
public GeyserResourcePackManifest(int formatVersion,
|
||||
Header header,
|
||||
Collection<Module> modules,
|
||||
Collection<Dependency> dependencies,
|
||||
Collection<Subpack> subpacks,
|
||||
Collection<Setting> settings) {
|
||||
this.formatVersion = formatVersion;
|
||||
this.header = header;
|
||||
this.modules = ensureNonNull(modules);
|
||||
this.dependencies = ensureNonNull(dependencies);
|
||||
this.subpacks = ensureNonNull(subpacks);
|
||||
this.settings = ensureNonNull(settings);
|
||||
}
|
||||
|
||||
public record Header(UUID uuid, Version version, String name, String description, @JsonProperty("min_engine_version") Version minimumSupportedMinecraftVersion) implements ResourcePackManifest.Header { }
|
||||
|
||||
|
@ -45,6 +66,15 @@ public record GeyserResourcePackManifest(@JsonProperty("format_version") int for
|
|||
|
||||
public record Dependency(UUID uuid, Version version) implements ResourcePackManifest.Dependency { }
|
||||
|
||||
public record Subpack(@JsonProperty("folder_name") String folderName, String name, @JsonProperty("memory_tier") Float memoryTier) implements ResourcePackManifest.Subpack { }
|
||||
|
||||
public record Setting(String type, String text) implements ResourcePackManifest.Setting { }
|
||||
|
||||
static <T> Collection<T> ensureNonNull(Collection<T> collection) {
|
||||
if (collection == null) return Collections.emptyList();
|
||||
return Collections.unmodifiableCollection(collection);
|
||||
}
|
||||
|
||||
@JsonDeserialize(using = Version.VersionDeserializer.class)
|
||||
public record Version(int major, int minor, int patch) implements ResourcePackManifest.Version {
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.api.pack.option.PriorityOption;
|
||||
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(PriorityOption.NORMAL));
|
||||
}
|
||||
|
||||
public ResourcePack resourcePack() {
|
||||
return this.pack;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.api.pack.exception.ResourcePackException;
|
||||
import org.geysermc.geyser.api.pack.option.PriorityOption;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record GeyserPriorityOption(int priority) implements PriorityOption {
|
||||
|
||||
@Override
|
||||
public @NonNull Type type() {
|
||||
return Type.PRIORITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Integer value() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(@NonNull ResourcePack pack) {
|
||||
Objects.requireNonNull(pack);
|
||||
if (priority < -100 || priority > 100) {
|
||||
throw new ResourcePackException(ResourcePackException.Cause.INVALID_PACK_OPTION,
|
||||
"Priority must be between -100 and 100 inclusive!");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.api.pack.ResourcePackManifest;
|
||||
import org.geysermc.geyser.api.pack.exception.ResourcePackException;
|
||||
import org.geysermc.geyser.api.pack.option.SubpackOption;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 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 record GeyserSubpackOption(String subpackName) implements SubpackOption {
|
||||
|
||||
@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 ResourcePackException(ResourcePackException.Cause.INVALID_PACK_OPTION,
|
||||
"No subpack with the name %s found!".formatted(subpackName));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.api.pack.UrlPackCodec;
|
||||
import org.geysermc.geyser.api.pack.exception.ResourcePackException;
|
||||
import org.geysermc.geyser.api.pack.option.UrlFallbackOption;
|
||||
|
||||
public record GeyserUrlFallbackOption(Boolean enabled) implements UrlFallbackOption {
|
||||
|
||||
@Override
|
||||
public @NonNull Type type() {
|
||||
return Type.FALLBACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Boolean value() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(@NonNull ResourcePack pack) {
|
||||
if (!(pack.codec() instanceof UrlPackCodec)) {
|
||||
throw new ResourcePackException(ResourcePackException.Cause.INVALID_PACK_OPTION,
|
||||
"The UrlFallbackOption cannot be set on resource packs not created using the url pack codec!");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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.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;
|
||||
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<ResourcePackOption.Type, ResourcePackOption<?>> {
|
||||
|
||||
public OptionHolder() {
|
||||
super();
|
||||
}
|
||||
|
||||
// Used when adding resource packs initially to ensure that a priority option is always set
|
||||
// It is however NOT used for session-options, as then the "normal" prio might override
|
||||
// the resource pack option
|
||||
public OptionHolder(PriorityOption option) {
|
||||
super();
|
||||
put(option.type(), option);
|
||||
}
|
||||
|
||||
public void validateAndAdd(ResourcePack pack, ResourcePackOption<?>... options) {
|
||||
for (ResourcePackOption<?> option : options) {
|
||||
// Validate before adding
|
||||
option.validate(pack);
|
||||
|
||||
// Ensure that we do not have duplicate types.
|
||||
if (super.containsKey(option.type())) {
|
||||
super.replace(option.type(), option);
|
||||
} else {
|
||||
super.put(option.type(), option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T valueOrFallback(ResourcePackOption.@NonNull Type type,
|
||||
@Nullable OptionHolder sessionPackOptions,
|
||||
@NonNull OptionHolder resourcePackOptions,
|
||||
@NonNull T defaultValue) {
|
||||
ResourcePackOption<?> option;
|
||||
|
||||
// First: the session's options, if they exist
|
||||
if (sessionPackOptions != null) {
|
||||
option = sessionPackOptions.get(type);
|
||||
if (option != null) {
|
||||
return (T) option.value();
|
||||
}
|
||||
}
|
||||
|
||||
// Second: check the resource pack options
|
||||
option = resourcePackOptions.get(type);
|
||||
if (option != null) {
|
||||
return (T) option.value();
|
||||
}
|
||||
|
||||
// Finally: return default
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static @Nullable ResourcePackOption<?> optionByType(ResourcePackOption.@NonNull Type type,
|
||||
@Nullable OptionHolder sessionPackOptions,
|
||||
@NonNull OptionHolder resourcePackOptions) {
|
||||
|
||||
// First: the session-specific options, if these exist
|
||||
if (sessionPackOptions != null) {
|
||||
ResourcePackOption<?> option = sessionPackOptions.get(type);
|
||||
if (option != null) {
|
||||
return option;
|
||||
}
|
||||
}
|
||||
|
||||
// Second: check the default holder for the option, if it exists;
|
||||
// Or return null if the option isn't set.
|
||||
return resourcePackOptions.get(type);
|
||||
}
|
||||
|
||||
public void remove(ResourcePackOption<?> option) {
|
||||
super.remove(option.type());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the options of this holder in an immutable collection
|
||||
*/
|
||||
public Collection<ResourcePackOption<?>> 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<ResourcePackOption<?>> immutableValues(OptionHolder defaultValues) {
|
||||
// Create a map to hold the combined values
|
||||
Map<ResourcePackOption.Type, ResourcePackOption<?>> combinedOptions = new HashMap<>(this);
|
||||
|
||||
// Add options from the pack if not already overridden by this OptionHolder
|
||||
defaultValues.forEach(combinedOptions::putIfAbsent);
|
||||
|
||||
// Return an immutable collection of the combined options
|
||||
return Collections.unmodifiableCollection(combinedOptions.values());
|
||||
}
|
||||
}
|
|
@ -79,15 +79,20 @@ public class GeyserPathPackCodec extends PathPackCodec {
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NonNull SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws IOException {
|
||||
public @NonNull SeekableByteChannel serialize() throws IOException {
|
||||
return FileChannel.open(this.path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull ResourcePack create() {
|
||||
protected ResourcePack.@NonNull Builder createBuilder() {
|
||||
return ResourcePackLoader.readPack(this.path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull ResourcePack create() {
|
||||
return createBuilder().build();
|
||||
}
|
||||
|
||||
private void checkLastModified() {
|
||||
try {
|
||||
FileTime lastModified = Files.getLastModifiedTime(this.path);
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 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.url;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.pack.PathPackCodec;
|
||||
import org.geysermc.geyser.api.pack.UrlPackCodec;
|
||||
import org.geysermc.geyser.pack.GeyserResourcePack;
|
||||
import org.geysermc.geyser.registry.loader.ResourcePackLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.util.Objects;
|
||||
|
||||
public class GeyserUrlPackCodec extends UrlPackCodec {
|
||||
private final @NonNull String url;
|
||||
@Getter
|
||||
private PathPackCodec fallback;
|
||||
|
||||
public GeyserUrlPackCodec(@NonNull String url) throws IllegalArgumentException {
|
||||
Objects.requireNonNull(url);
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte @NonNull [] sha256() {
|
||||
Objects.requireNonNull(fallback, "must call #create() before attempting to get the sha256!");
|
||||
return fallback.sha256();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() {
|
||||
Objects.requireNonNull(fallback, "must call #create() before attempting to get the size!");
|
||||
return fallback.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull SeekableByteChannel serialize() throws IOException {
|
||||
Objects.requireNonNull(fallback, "must call #create() before attempting to serialize!!");
|
||||
return fallback.serialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public GeyserResourcePack create() {
|
||||
return createBuilder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GeyserResourcePack.@NonNull Builder createBuilder() {
|
||||
if (this.fallback == null) {
|
||||
try {
|
||||
ResourcePackLoader.downloadPack(url, false).whenComplete((pack, throwable) -> {
|
||||
if (throwable != null) {
|
||||
throw new IllegalArgumentException(throwable);
|
||||
} else if (pack != null) {
|
||||
this.fallback = pack;
|
||||
}
|
||||
}).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 (%s)!".formatted(url, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
return ResourcePackLoader.readPack(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String url() {
|
||||
return this.url;
|
||||
}
|
||||
}
|
|
@ -34,10 +34,10 @@ 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.pack.ResourcePackHolder;
|
||||
import org.geysermc.geyser.registry.loader.BiomeIdentifierRegistryLoader;
|
||||
import org.geysermc.geyser.registry.loader.BlockEntityRegistryLoader;
|
||||
import org.geysermc.geyser.registry.loader.ParticleTypesRegistryLoader;
|
||||
|
@ -166,9 +166,9 @@ public final class Registries {
|
|||
//public static final SimpleMappedDeferredRegistry<RecipeType, List<GeyserRecipe>> RECIPES = SimpleMappedDeferredRegistry.create("mappings/recipes.nbt", RecipeRegistryLoader::new);
|
||||
|
||||
/**
|
||||
* A mapped registry holding {@link ResourcePack}'s with the pack uuid as keys.
|
||||
* A mapped registry holding {@link ResourcePackHolder}'s with the pack uuid as keys.
|
||||
*/
|
||||
public static final SimpleMappedDeferredRegistry<UUID, ResourcePack> RESOURCE_PACKS = SimpleMappedDeferredRegistry.create(GeyserImpl.getInstance().packDirectory(), RegistryLoaders.RESOURCE_PACKS);
|
||||
public static final SimpleMappedDeferredRegistry<UUID, ResourcePackHolder> RESOURCE_PACKS = SimpleMappedDeferredRegistry.create(GeyserImpl.getInstance().packDirectory(), RegistryLoaders.RESOURCE_PACKS);
|
||||
|
||||
/**
|
||||
* A versioned registry holding most Bedrock tags, with the Java item list (sorted) being the key, and the tag name as the value.
|
||||
|
|
|
@ -40,10 +40,14 @@ import org.geysermc.geyser.api.item.custom.CustomItemData;
|
|||
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
||||
import org.geysermc.geyser.api.pack.PathPackCodec;
|
||||
import org.geysermc.geyser.impl.camera.GeyserCameraFade;
|
||||
import org.geysermc.geyser.impl.camera.GeyserCameraPosition;
|
||||
import org.geysermc.geyser.api.pack.UrlPackCodec;
|
||||
import org.geysermc.geyser.api.pack.option.PriorityOption;
|
||||
import org.geysermc.geyser.api.pack.option.SubpackOption;
|
||||
import org.geysermc.geyser.api.pack.option.UrlFallbackOption;
|
||||
import org.geysermc.geyser.event.GeyserEventRegistrar;
|
||||
import org.geysermc.geyser.extension.command.GeyserExtensionCommand;
|
||||
import org.geysermc.geyser.impl.camera.GeyserCameraFade;
|
||||
import org.geysermc.geyser.impl.camera.GeyserCameraPosition;
|
||||
import org.geysermc.geyser.item.GeyserCustomItemData;
|
||||
import org.geysermc.geyser.item.GeyserCustomItemOptions;
|
||||
import org.geysermc.geyser.item.GeyserNonVanillaCustomItemData;
|
||||
|
@ -53,7 +57,11 @@ import org.geysermc.geyser.level.block.GeyserGeometryComponent;
|
|||
import org.geysermc.geyser.level.block.GeyserJavaBlockState;
|
||||
import org.geysermc.geyser.level.block.GeyserMaterialInstance;
|
||||
import org.geysermc.geyser.level.block.GeyserNonVanillaCustomBlockData;
|
||||
import org.geysermc.geyser.pack.option.GeyserPriorityOption;
|
||||
import org.geysermc.geyser.pack.option.GeyserSubpackOption;
|
||||
import org.geysermc.geyser.pack.option.GeyserUrlFallbackOption;
|
||||
import org.geysermc.geyser.pack.path.GeyserPathPackCodec;
|
||||
import org.geysermc.geyser.pack.url.GeyserUrlPackCodec;
|
||||
import org.geysermc.geyser.registry.provider.ProviderSupplier;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
@ -66,9 +74,10 @@ public class ProviderRegistryLoader implements RegistryLoader<Map<Class<?>, Prov
|
|||
|
||||
@Override
|
||||
public Map<Class<?>, ProviderSupplier> load(Map<Class<?>, ProviderSupplier> providers) {
|
||||
// misc
|
||||
// commands
|
||||
providers.put(Command.Builder.class, args -> new GeyserExtensionCommand.Builder<>((Extension) args[0]));
|
||||
|
||||
// custom blocks
|
||||
providers.put(CustomBlockComponents.Builder.class, args -> new GeyserCustomBlockComponents.Builder());
|
||||
providers.put(CustomBlockData.Builder.class, args -> new GeyserCustomBlockData.Builder());
|
||||
providers.put(JavaBlockState.Builder.class, args -> new GeyserJavaBlockState.Builder());
|
||||
|
@ -76,8 +85,15 @@ public class ProviderRegistryLoader implements RegistryLoader<Map<Class<?>, Prov
|
|||
providers.put(MaterialInstance.Builder.class, args -> new GeyserMaterialInstance.Builder());
|
||||
providers.put(GeometryComponent.Builder.class, args -> new GeyserGeometryComponent.Builder());
|
||||
|
||||
// misc
|
||||
providers.put(EventRegistrar.class, args -> new GeyserEventRegistrar(args[0]));
|
||||
|
||||
// packs
|
||||
providers.put(PathPackCodec.class, args -> new GeyserPathPackCodec((Path) args[0]));
|
||||
providers.put(UrlPackCodec.class, args -> new GeyserUrlPackCodec((String) args[0]));
|
||||
providers.put(PriorityOption.class, args -> new GeyserPriorityOption((int) args[0]));
|
||||
providers.put(SubpackOption.class, args -> new GeyserSubpackOption((String) args[0]));
|
||||
providers.put(UrlFallbackOption.class, args -> new GeyserUrlFallbackOption((Boolean) args[0]));
|
||||
|
||||
// items
|
||||
providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.Builder());
|
||||
|
|
|
@ -25,16 +25,30 @@
|
|||
|
||||
package org.geysermc.geyser.registry.loader;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent;
|
||||
import org.geysermc.geyser.api.pack.PathPackCodec;
|
||||
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.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;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
import org.geysermc.geyser.util.WebUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystems;
|
||||
|
@ -42,10 +56,13 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -53,9 +70,17 @@ import java.util.zip.ZipEntry;
|
|||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Loads {@link ResourcePack}s within a {@link Path} directory, firing the {@link GeyserLoadResourcePacksEvent}.
|
||||
* Loads {@link ResourcePack}s within a {@link Path} directory, firing the {@link GeyserDefineResourcePacksEventImpl}.
|
||||
*/
|
||||
public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, ResourcePack>> {
|
||||
public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, ResourcePackHolder>> {
|
||||
|
||||
/**
|
||||
* Used to keep track of remote resource packs that the client rejected.
|
||||
* If a client rejects such a pack, it falls back to the old method, and Geyser serves a cached variant.
|
||||
*/
|
||||
private static final Cache<String, UrlPackCodec> CACHED_FAILED_PACKS = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(1, TimeUnit.HOURS)
|
||||
.build();
|
||||
|
||||
static final PathMatcher PACK_MATCHER = FileSystems.getDefault().getPathMatcher("glob:**.{zip,mcpack}");
|
||||
|
||||
|
@ -65,8 +90,8 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Resour
|
|||
* Loop through the packs directory and locate valid resource pack files
|
||||
*/
|
||||
@Override
|
||||
public Map<UUID, ResourcePack> load(Path directory) {
|
||||
Map<UUID, ResourcePack> packMap = new HashMap<>();
|
||||
public Map<UUID, ResourcePackHolder> load(Path directory) {
|
||||
Map<UUID, ResourcePackHolder> packMap = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
if (!Files.exists(directory)) {
|
||||
try {
|
||||
|
@ -77,7 +102,7 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Resour
|
|||
}
|
||||
|
||||
List<Path> resourcePacks;
|
||||
try (Stream<Path> stream = Files.walk(directory)) {
|
||||
try (Stream<Path> stream = Files.list(directory)) {
|
||||
resourcePacks = stream.filter(PACK_MATCHER::matches)
|
||||
.collect(Collectors.toCollection(ArrayList::new)); // toList() does not guarantee mutability
|
||||
} catch (Exception e) {
|
||||
|
@ -95,33 +120,76 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Resour
|
|||
resourcePacks.add(skullResourcePack);
|
||||
}
|
||||
|
||||
//noinspection deprecation - we know
|
||||
GeyserLoadResourcePacksEvent event = new GeyserLoadResourcePacksEvent(resourcePacks);
|
||||
GeyserImpl.getInstance().eventBus().fire(event);
|
||||
|
||||
for (Path path : event.resourcePacks()) {
|
||||
try {
|
||||
GeyserResourcePack pack = readPack(path);
|
||||
packMap.put(pack.manifest().header().uuid(), pack);
|
||||
GeyserResourcePack pack = readPack(path).build();
|
||||
packMap.put(pack.uuid(), ResourcePackHolder.of(pack));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return packMap;
|
||||
|
||||
// Load all remote resource packs from the config before firing the new event
|
||||
// TODO configurate
|
||||
//packMap.putAll(loadRemotePacks());
|
||||
|
||||
GeyserDefineResourcePacksEventImpl defineEvent = new GeyserDefineResourcePacksEventImpl(packMap);
|
||||
GeyserImpl.getInstance().eventBus().fire(defineEvent);
|
||||
|
||||
// After loading the new resource packs: let's clean up the old url packs
|
||||
cleanupRemotePacks();
|
||||
|
||||
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
|
||||
* @return a {@link ResourcePack} representation
|
||||
* @return a {@link ResourcePack.Builder} representation
|
||||
* @throws IllegalArgumentException if the pack manifest was invalid or there was any processing exception
|
||||
*/
|
||||
public static GeyserResourcePack readPack(Path path) throws IllegalArgumentException {
|
||||
if (!path.getFileName().toString().endsWith(".mcpack") && !path.getFileName().toString().endsWith(".zip")) {
|
||||
public static GeyserResourcePack.Builder readPack(Path path) throws IllegalArgumentException {
|
||||
if (!PACK_MATCHER.matches(path)) {
|
||||
throw new IllegalArgumentException("Resource pack " + path.getFileName() + " must be a .zip or .mcpack file!");
|
||||
}
|
||||
|
||||
ResourcePackManifest manifest = readManifest(path, path.getFileName().toString());
|
||||
String contentKey;
|
||||
|
||||
try {
|
||||
// Check if a file exists with the same name as the resource pack suffixed by .key,
|
||||
// and set this as content key. (e.g. test.zip, key file would be test.zip.key)
|
||||
Path keyFile = path.resolveSibling(path.getFileName().toString() + ".key");
|
||||
contentKey = Files.exists(keyFile) ? Files.readString(keyFile, StandardCharsets.UTF_8) : "";
|
||||
} catch (IOException e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Failed to read content key for resource pack " + path.getFileName(), e);
|
||||
contentKey = "";
|
||||
}
|
||||
|
||||
return new GeyserResourcePack.Builder(new GeyserPathPackCodec(path), manifest, contentKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a Resource pack from a URL codec, and returns a resource pack. Unlike {@link ResourcePackLoader#readPack(Path)}
|
||||
* this method reads content keys differently.
|
||||
*
|
||||
* @param codec the URL pack codec with the url to download the pack from
|
||||
* @return a {@link GeyserResourcePack} representation
|
||||
* @throws IllegalArgumentException if there was an error reading the pack.
|
||||
*/
|
||||
public static GeyserResourcePack.Builder readPack(GeyserUrlPackCodec codec) throws IllegalArgumentException {
|
||||
Path path = codec.getFallback().path();
|
||||
ResourcePackManifest manifest = readManifest(path, codec.url());
|
||||
return new GeyserResourcePack.Builder(codec, manifest);
|
||||
}
|
||||
|
||||
private static ResourcePackManifest readManifest(Path path, String packLocation) throws IllegalArgumentException {
|
||||
AtomicReference<GeyserResourcePackManifest> manifestReference = new AtomicReference<>();
|
||||
|
||||
try (ZipFile zip = new ZipFile(path.toFile());
|
||||
|
@ -129,7 +197,7 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Resour
|
|||
stream.forEach(x -> {
|
||||
String name = x.getName();
|
||||
if (SHOW_RESOURCE_PACK_LENGTH_WARNING && name.length() >= 80) {
|
||||
GeyserImpl.getInstance().getLogger().warning("The resource pack " + path.getFileName()
|
||||
GeyserImpl.getInstance().getLogger().warning("The resource pack " + packLocation
|
||||
+ " has a file in it that meets or exceeds 80 characters in its path (" + name
|
||||
+ ", " + name.length() + " characters long). This will cause problems on some Bedrock platforms." +
|
||||
" Please rename it to be shorter, or reduce the amount of folders needed to get to the file.");
|
||||
|
@ -148,17 +216,190 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Resour
|
|||
|
||||
GeyserResourcePackManifest manifest = manifestReference.get();
|
||||
if (manifest == null) {
|
||||
throw new IllegalArgumentException(path.getFileName() + " does not contain a valid pack_manifest.json or manifest.json");
|
||||
throw new IllegalArgumentException(packLocation + " does not contain a valid pack_manifest.json or manifest.json");
|
||||
}
|
||||
|
||||
// Check if a file exists with the same name as the resource pack suffixed by .key,
|
||||
// and set this as content key. (e.g. test.zip, key file would be test.zip.key)
|
||||
Path keyFile = path.resolveSibling(path.getFileName().toString() + ".key");
|
||||
String contentKey = Files.exists(keyFile) ? Files.readString(keyFile, StandardCharsets.UTF_8) : "";
|
||||
|
||||
return new GeyserResourcePack(new GeyserPathPackCodec(path), manifest, contentKey);
|
||||
return manifest;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", path.getFileName()), e);
|
||||
throw new IllegalArgumentException(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", packLocation), e);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<UUID, ResourcePackHolder> loadRemotePacks() {
|
||||
GeyserImpl instance = GeyserImpl.getInstance();
|
||||
// Unable to make this a static variable, as the test would fail
|
||||
final Path cachedDirectory = instance.getBootstrap().getConfigFolder().resolve("cache").resolve("remote_packs");
|
||||
|
||||
if (!Files.exists(cachedDirectory)) {
|
||||
try {
|
||||
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();
|
||||
List<String> remotePackUrls = List.of();
|
||||
Map<UUID, ResourcePackHolder> packMap = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
for (String url : remotePackUrls) {
|
||||
try {
|
||||
GeyserUrlPackCodec codec = new GeyserUrlPackCodec(url);
|
||||
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());
|
||||
if (instance.getLogger().isDebug()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return packMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when a Bedrock client requests a Bedrock resource pack from the server when it should be downloading it
|
||||
* from a remote provider. Since this would be called each time a Bedrock client requests a piece of the Bedrock pack,
|
||||
* this uses a cache to ensure we aren't re-checking a dozen times.
|
||||
*
|
||||
* @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, UUID packId, String packVersion) {
|
||||
if (CACHED_FAILED_PACKS.getIfPresent(codec.url()) == null) {
|
||||
String url = codec.url();
|
||||
CACHED_FAILED_PACKS.put(url, codec);
|
||||
GeyserImpl.getInstance().getLogger().warning(
|
||||
"Bedrock client (%s, playing on %s) was not able to download the resource pack at %s. Checking for changes now:"
|
||||
.formatted(session.bedrockUsername(), session.getClientData().getDeviceOs().name(), codec.url())
|
||||
);
|
||||
|
||||
downloadPack(codec.url(), true).whenComplete((pathPackCodec, e) -> {
|
||||
if (e != null) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", url), e);
|
||||
if (GeyserImpl.getInstance().getLogger().isDebug()) {
|
||||
e.printStackTrace();
|
||||
if (pathPackCodec != null) {
|
||||
deleteFile(pathPackCodec.path());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (pathPackCodec == null) {
|
||||
return; // Already warned about
|
||||
}
|
||||
|
||||
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 {
|
||||
GeyserImpl.getInstance().getLogger().info("Detected a new resource pack version (%s, old version %s) for pack at %s!"
|
||||
.formatted(packVersion, newPack.manifest().header().version().toString(), url));
|
||||
}
|
||||
} else {
|
||||
GeyserImpl.getInstance().getLogger().info("Detected a new resource pack at the url %s!".formatted(url));
|
||||
}
|
||||
|
||||
// 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(newPack.uuid(), ResourcePackHolder.of(newPack));
|
||||
|
||||
if (codec instanceof GeyserUrlPackCodec geyserUrlPackCodec
|
||||
&& geyserUrlPackCodec.getFallback() != null) {
|
||||
Path path = geyserUrlPackCodec.getFallback().path();
|
||||
try {
|
||||
GeyserImpl.getInstance().getScheduledThread().schedule(() -> {
|
||||
CACHED_FAILED_PACKS.invalidate(codec.url());
|
||||
deleteFile(path);
|
||||
}, 5, TimeUnit.MINUTES);
|
||||
} catch (RejectedExecutionException exception) {
|
||||
// No scheduling here, probably because we're shutting down?
|
||||
deleteFile(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void deleteFile(Path path) {
|
||||
if (path.toFile().exists()) {
|
||||
try {
|
||||
Files.delete(path);
|
||||
} catch (IOException e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Unable to delete old pack! " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static CompletableFuture<@Nullable PathPackCodec> downloadPack(String url, boolean testing) throws IllegalArgumentException {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Path path = WebUtils.downloadRemotePack(url, testing);
|
||||
|
||||
// Already warned about these above
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if the pack is a .zip or .mcpack file
|
||||
if (!PACK_MATCHER.matches(path)) {
|
||||
throw new IllegalArgumentException("Invalid pack format from url %s! Not a .zip or .mcpack file.".formatted(url));
|
||||
}
|
||||
|
||||
try {
|
||||
try (ZipFile zip = new ZipFile(path.toFile())) {
|
||||
if (zip.stream().noneMatch(x -> x.getName().contains("manifest.json"))) {
|
||||
throw new IllegalArgumentException("The pack at the url " + url + " does not contain a manifest file!");
|
||||
}
|
||||
|
||||
// Check if a "manifest.json" or "pack_manifest.json" file is located directly in the zip... does not work otherwise.
|
||||
// (something like MyZip.zip/manifest.json) will not, but will if it's a subfolder (MyPack.zip/MyPack/manifest.json)
|
||||
if (zip.getEntry("manifest.json") != null || zip.getEntry("pack_manifest.json") != null) {
|
||||
if (GeyserImpl.getInstance().getLogger().isDebug()) {
|
||||
GeyserImpl.getInstance().getLogger().info("The remote resource pack from " + url + " contains a manifest.json file at the root of the zip file. " +
|
||||
"This may not work for remote packs, and could cause Bedrock clients to fall back to request the pack from the server. " +
|
||||
"Please put the pack file in a subfolder, and provide that zip in the URL.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", url), e);
|
||||
}
|
||||
|
||||
return new GeyserPathPackCodec(path);
|
||||
});
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
if (Registries.RESOURCE_PACKS.loaded()) {
|
||||
Registries.RESOURCE_PACKS.get().clear();
|
||||
}
|
||||
CACHED_FAILED_PACKS.invalidateAll();
|
||||
}
|
||||
|
||||
public static void cleanupRemotePacks() {
|
||||
File cacheFolder = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("remote_packs").toFile();
|
||||
if (!cacheFolder.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
final long expireTime = (((long) 1000 * 60 * 60)); // one hour
|
||||
for (File cachedPack : Objects.requireNonNull(cacheFolder.listFiles())) {
|
||||
if (cachedPack.lastModified() < System.currentTimeMillis() - expireTime) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
cachedPack.delete();
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
GeyserImpl.getInstance().getLogger().debug(String.format("Removed %d cached resource pack files as they are no longer in use!", count));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.cloudburstmc.nbt.NbtUtils;
|
|||
import org.cloudburstmc.protocol.bedrock.codec.v748.Bedrock_v748;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v766.Bedrock_v766;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v776.Bedrock_v776;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v786.Bedrock_v786;
|
||||
import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
|
@ -117,41 +118,10 @@ public final class BlockRegistryPopulator {
|
|||
private static void registerBedrockBlocks() {
|
||||
var blockMappers = ImmutableMap.<ObjectIntPair<String>, Remapper>builder()
|
||||
.put(ObjectIntPair.of("1_21_40", Bedrock_v748.CODEC.getProtocolVersion()), Conversion766_748::remapBlock)
|
||||
.put(ObjectIntPair.of("1_21_50", Bedrock_v766.CODEC.getProtocolVersion()), tag -> tag) // TODO: Finish me
|
||||
.put(ObjectIntPair.of("1_21_60", Bedrock_v776.CODEC.getProtocolVersion()), tag -> {
|
||||
final String name = tag.getString("name");
|
||||
if (name.equals("minecraft:creaking_heart") && tag.getCompound("states").containsKey("active")) {
|
||||
NbtMapBuilder builder = tag.getCompound("states").toBuilder();
|
||||
builder.remove("active");
|
||||
builder.putString("creaking_heart_state", "awake");
|
||||
NbtMap states = builder.build();
|
||||
return tag.toBuilder().putCompound("states", states).build();
|
||||
}
|
||||
if ((name.endsWith("_door") || name.endsWith("fence_gate")) && tag.getCompound("states").containsKey("direction")) {
|
||||
NbtMapBuilder builder = tag.getCompound("states").toBuilder();
|
||||
Integer directionCardinality = (Integer) builder.remove("direction");
|
||||
switch (directionCardinality) {
|
||||
case 0:
|
||||
builder.putString("minecraft:cardinal_direction", "south");
|
||||
break;
|
||||
case 1:
|
||||
builder.putString("minecraft:cardinal_direction", "west");
|
||||
break;
|
||||
case 2:
|
||||
builder.putString( "minecraft:cardinal_direction" , "north");
|
||||
break;
|
||||
case 3:
|
||||
builder.putString("minecraft:cardinal_direction", "east");
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Invalid direction: " + directionCardinality);
|
||||
}
|
||||
NbtMap states = builder.build();
|
||||
return tag.toBuilder().putCompound("states", states).build();
|
||||
}
|
||||
return tag;
|
||||
})
|
||||
.build();
|
||||
.put(ObjectIntPair.of("1_21_50", Bedrock_v766.CODEC.getProtocolVersion()), Conversion776_766::remapBlock)
|
||||
.put(ObjectIntPair.of("1_21_60", Bedrock_v776.CODEC.getProtocolVersion()), tag -> tag)
|
||||
.put(ObjectIntPair.of("1_21_70", Bedrock_v786.CODEC.getProtocolVersion()), tag -> tag)
|
||||
.build();
|
||||
|
||||
// We can keep this strong as nothing should be garbage collected
|
||||
// Safe to intern since Cloudburst NBT is immutable
|
||||
|
|
|
@ -85,6 +85,10 @@ public class Conversion766_748 {
|
|||
}
|
||||
|
||||
static NbtMap remapBlock(NbtMap tag) {
|
||||
|
||||
// First: Downgrade from 1.21.60 -> 1.21.50
|
||||
tag = Conversion776_766.remapBlock(tag);
|
||||
|
||||
String name = tag.getString("name").replace("minecraft:", "");
|
||||
if (PALE_WOODEN_BLOCKS.contains(name)) {
|
||||
return withName(tag, name.replace("pale_oak", "birch"));
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2025 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.registry.populator;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
|
||||
public class Conversion776_766 {
|
||||
|
||||
public static NbtMap remapBlock(NbtMap tag) {
|
||||
final String name = tag.getString("name");
|
||||
|
||||
if (name.equals("minecraft:creaking_heart")) {
|
||||
NbtMapBuilder builder = tag.getCompound("states").toBuilder();
|
||||
String value = (String) builder.remove("creaking_heart_state");
|
||||
builder.putBoolean("active", value.equals("awake"));
|
||||
|
||||
return tag.toBuilder().putCompound("states", builder.build()).build();
|
||||
}
|
||||
|
||||
if (name.endsWith("_door") || name.endsWith("fence_gate")) {
|
||||
NbtMapBuilder builder = tag.getCompound("states").toBuilder();
|
||||
String cardinalDirection = (String) builder.remove("minecraft:cardinal_direction");
|
||||
switch (cardinalDirection) {
|
||||
case "south" -> builder.putInt("direction", 0);
|
||||
case "west" -> builder.putInt("direction", 1);
|
||||
case "east" -> builder.putInt("direction", 3);
|
||||
case "north" -> builder.putInt("direction", 2);
|
||||
default -> throw new AssertionError("Invalid direction: " + cardinalDirection);
|
||||
}
|
||||
NbtMap states = builder.build();
|
||||
return tag.toBuilder().putCompound("states", states).build();
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
}
|
|
@ -48,6 +48,7 @@ import org.cloudburstmc.nbt.NbtUtils;
|
|||
import org.cloudburstmc.protocol.bedrock.codec.v748.Bedrock_v748;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v766.Bedrock_v766;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v776.Bedrock_v776;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v786.Bedrock_v786;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.SimpleItemDefinition;
|
||||
|
@ -152,6 +153,7 @@ public class ItemRegistryPopulator {
|
|||
paletteVersions.add(new PaletteVersion("1_21_40", Bedrock_v748.CODEC.getProtocolVersion(), itemFallbacks, (item, mapping) -> mapping));
|
||||
paletteVersions.add(new PaletteVersion("1_21_50", Bedrock_v766.CODEC.getProtocolVersion()));
|
||||
paletteVersions.add(new PaletteVersion("1_21_60", Bedrock_v776.CODEC.getProtocolVersion()));
|
||||
paletteVersions.add(new PaletteVersion("1_21_70", Bedrock_v786.CODEC.getProtocolVersion()));
|
||||
|
||||
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
||||
|
||||
|
|
|
@ -25,23 +25,7 @@
|
|||
|
||||
package org.geysermc.geyser.scoreboard;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.UpdateType.REMOVE;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
@ -57,12 +41,28 @@ import org.geysermc.geyser.scoreboard.display.slot.DisplaySlot;
|
|||
import org.geysermc.geyser.scoreboard.display.slot.PlayerlistDisplaySlot;
|
||||
import org.geysermc.geyser.scoreboard.display.slot.SidebarDisplaySlot;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.UpdateType.REMOVE;
|
||||
|
||||
/**
|
||||
* Here follows some information about how scoreboards work in Java Edition, that is related to the workings of this
|
||||
* class:
|
||||
|
|
|
@ -61,13 +61,15 @@ public final class SidebarDisplayScore extends DisplayScore {
|
|||
markUpdated();
|
||||
|
||||
String finalName = reference.name();
|
||||
String displayName = reference.displayName();
|
||||
|
||||
String displayName = reference.displayName();
|
||||
if (displayName != null) {
|
||||
finalName = displayName;
|
||||
} else if (team != null) {
|
||||
}
|
||||
|
||||
if (team != null) {
|
||||
this.lastTeamUpdate = team.lastUpdate();
|
||||
finalName = team.displayName(reference.name());
|
||||
finalName = team.displayName(finalName);
|
||||
}
|
||||
|
||||
NumberFormat numberFormat = reference.numberFormat();
|
||||
|
|
|
@ -91,7 +91,7 @@ import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket;
|
|||
import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ItemComponentPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetCommandsEnabledPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetTimePacket;
|
||||
|
@ -675,6 +675,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
@Setter
|
||||
private int stepTicks = 0;
|
||||
|
||||
/*
|
||||
* Stores the number of attempts to open virtual inventories.
|
||||
* Capped at 3, and isn't used in ideal circumstances.
|
||||
* Used to resolve https://github.com/GeyserMC/Geyser/issues/5426
|
||||
*/
|
||||
@Setter
|
||||
private int containerOpenAttempts;
|
||||
|
||||
|
||||
public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop tickEventLoop) {
|
||||
this.geyser = geyser;
|
||||
|
@ -1933,7 +1941,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
}
|
||||
|
||||
public void playSoundEvent(SoundEvent sound, Vector3f position) {
|
||||
LevelSoundEvent2Packet packet = new LevelSoundEvent2Packet();
|
||||
LevelSoundEventPacket packet = new LevelSoundEventPacket();
|
||||
packet.setPosition(position);
|
||||
packet.setSound(sound);
|
||||
packet.setIdentifier(":");
|
||||
|
|
|
@ -68,7 +68,7 @@ public class GeyserSessionAdapter extends SessionAdapter {
|
|||
|
||||
@Override
|
||||
public void packetSending(PacketSendingEvent event) {
|
||||
if (event.getPacket() instanceof ClientIntentionPacket) {
|
||||
if (event.getPacket() instanceof ClientIntentionPacket intentionPacket) {
|
||||
BedrockClientData clientData = geyserSession.getClientData();
|
||||
|
||||
String addressSuffix;
|
||||
|
@ -109,8 +109,6 @@ public class GeyserSessionAdapter extends SessionAdapter {
|
|||
addressSuffix = "";
|
||||
}
|
||||
|
||||
ClientIntentionPacket intentionPacket = event.getPacket();
|
||||
|
||||
String address;
|
||||
if (geyser.getConfig().getRemote().isForwardHost()) {
|
||||
address = clientData.getServerAddress().split(":")[0];
|
||||
|
|
|
@ -25,23 +25,32 @@
|
|||
|
||||
package org.geysermc.geyser.session.cache;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ModalFormRequestPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.cumulus.component.util.ComponentType;
|
||||
import org.geysermc.cumulus.form.CustomForm;
|
||||
import org.geysermc.cumulus.form.Form;
|
||||
import org.geysermc.cumulus.form.SimpleForm;
|
||||
import org.geysermc.cumulus.form.impl.FormDefinitions;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class FormCache {
|
||||
private static final Gson GSON_TEMP = new Gson();
|
||||
|
||||
/**
|
||||
* The magnitude of this doesn't actually matter, but it must be negative so that
|
||||
|
@ -100,9 +109,52 @@ public class FormCache {
|
|||
return;
|
||||
}
|
||||
|
||||
String responseData = response.getFormData();
|
||||
//todo work on a proper solution in Cumulus, but that'd require all Floodgate instances to update as well and
|
||||
// drops support for older Bedrock versions (because Cumulus isn't made to support multiple versions). That's
|
||||
// why this hotfix exists.
|
||||
if (form instanceof CustomForm customForm && GameProtocol.is1_21_70orHigher(session)) {
|
||||
// Labels are no longer included as a json null, so we have to manually add them for now.
|
||||
IntList labelIndexes = new IntArrayList();
|
||||
for (int i = 0; i < customForm.content().size(); i++) {
|
||||
var component = customForm.content().get(i);
|
||||
if (component == null) {
|
||||
continue;
|
||||
}
|
||||
if (component.type() == ComponentType.LABEL) {
|
||||
labelIndexes.add(i);
|
||||
}
|
||||
}
|
||||
if (!labelIndexes.isEmpty()) {
|
||||
// If the form only has labels, the response is the literal
|
||||
// null (with a newline char) instead of a json array
|
||||
if (responseData.startsWith("null")) {
|
||||
List<Object> newResponse = new ArrayList<>();
|
||||
for (int i = 0; i < labelIndexes.size(); i++) {
|
||||
newResponse.add(null);
|
||||
}
|
||||
responseData = GSON_TEMP.toJson(newResponse);
|
||||
} else {
|
||||
JsonArray responseDataArray = GSON_TEMP.fromJson(responseData, JsonArray.class);
|
||||
List<Object> newResponse = new ArrayList<>();
|
||||
|
||||
int handledLabelCount = 0;
|
||||
for (int i = 0; i < responseDataArray.size() + labelIndexes.size(); i++) {
|
||||
if (labelIndexes.contains(i)) {
|
||||
newResponse.add(null);
|
||||
handledLabelCount++;
|
||||
continue;
|
||||
}
|
||||
newResponse.add(responseDataArray.get(i - handledLabelCount));
|
||||
}
|
||||
responseData = GSON_TEMP.toJson(newResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
formDefinitions.definitionFor(form)
|
||||
.handleFormResponse(form, response.getFormData());
|
||||
.handleFormResponse(form, responseData);
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Error while processing form response!", e);
|
||||
}
|
||||
|
|
|
@ -167,7 +167,7 @@ public class SkinProvider {
|
|||
if (count > 0) {
|
||||
GeyserImpl.getInstance().getLogger().debug(String.format("Removed %d cached image files as they have expired", count));
|
||||
}
|
||||
}, 10, 1440, TimeUnit.MINUTES);
|
||||
}, 10, 1, TimeUnit.DAYS);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -102,12 +102,14 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
|
|||
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
|
||||
session.sendUpstreamPacket(blockPacket);
|
||||
|
||||
NbtMap tag = BlockEntityTranslator.getConstantBedrockTag("Chest", position)
|
||||
NbtMapBuilder tag = BlockEntityTranslator.getConstantBedrockTag("Chest", position)
|
||||
.putInt("pairx", pairPosition.getX())
|
||||
.putInt("pairz", pairPosition.getZ())
|
||||
.putString("CustomName", inventory.getTitle()).build();
|
||||
.putString("CustomName", inventory.getTitle())
|
||||
.putBoolean("pairlead", false);
|
||||
|
||||
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
|
||||
dataPacket.setData(tag);
|
||||
dataPacket.setData(tag.build());
|
||||
dataPacket.setBlockPosition(position);
|
||||
session.sendUpstreamPacket(dataPacket);
|
||||
|
||||
|
@ -125,14 +127,15 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
|
|||
.putInt("z", pairPosition.getZ())
|
||||
.putInt("pairx", position.getX())
|
||||
.putInt("pairz", position.getZ())
|
||||
.putString("CustomName", inventory.getTitle()).build();
|
||||
.putString("CustomName", inventory.getTitle())
|
||||
.putBoolean("pairlead", true);
|
||||
|
||||
dataPacket = new BlockEntityDataPacket();
|
||||
dataPacket.setData(tag);
|
||||
dataPacket.setData(tag.build());
|
||||
dataPacket.setBlockPosition(pairPosition);
|
||||
session.sendUpstreamPacket(dataPacket);
|
||||
|
||||
inventory.setHolderPosition(position);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -160,31 +163,30 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
|
|||
return;
|
||||
}
|
||||
|
||||
if (container.isUsingRealBlock()) {
|
||||
// No need to reset a block since we didn't change any blocks
|
||||
// But send a container close packet because we aren't destroying the original.
|
||||
ContainerClosePacket packet = new ContainerClosePacket();
|
||||
packet.setId((byte) inventory.getBedrockId());
|
||||
packet.setServerInitiated(true);
|
||||
packet.setType(ContainerType.CONTAINER);
|
||||
session.sendUpstreamPacket(packet);
|
||||
return;
|
||||
// No need to reset a block since we didn't change any blocks
|
||||
// But send a container close packet because we aren't destroying the original.
|
||||
ContainerClosePacket packet = new ContainerClosePacket();
|
||||
packet.setId((byte) inventory.getBedrockId());
|
||||
packet.setServerInitiated(true);
|
||||
packet.setType(ContainerType.CONTAINER);
|
||||
session.sendUpstreamPacket(packet);
|
||||
|
||||
if (!container.isUsingRealBlock()) {
|
||||
Vector3i holderPos = inventory.getHolderPosition();
|
||||
int realBlock = session.getGeyser().getWorldManager().getBlockAt(session, holderPos);
|
||||
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
|
||||
blockPacket.setDataLayer(0);
|
||||
blockPacket.setBlockPosition(holderPos);
|
||||
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(realBlock));
|
||||
session.sendUpstreamPacket(blockPacket);
|
||||
|
||||
holderPos = holderPos.add(Vector3i.UNIT_X);
|
||||
realBlock = session.getGeyser().getWorldManager().getBlockAt(session, holderPos);
|
||||
blockPacket = new UpdateBlockPacket();
|
||||
blockPacket.setDataLayer(0);
|
||||
blockPacket.setBlockPosition(holderPos);
|
||||
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(realBlock));
|
||||
session.sendUpstreamPacket(blockPacket);
|
||||
}
|
||||
|
||||
Vector3i holderPos = inventory.getHolderPosition();
|
||||
int realBlock = session.getGeyser().getWorldManager().getBlockAt(session, holderPos);
|
||||
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
|
||||
blockPacket.setDataLayer(0);
|
||||
blockPacket.setBlockPosition(holderPos);
|
||||
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(realBlock));
|
||||
session.sendUpstreamPacket(blockPacket);
|
||||
|
||||
holderPos = holderPos.add(Vector3i.UNIT_X);
|
||||
realBlock = session.getGeyser().getWorldManager().getBlockAt(session, holderPos);
|
||||
blockPacket = new UpdateBlockPacket();
|
||||
blockPacket.setDataLayer(0);
|
||||
blockPacket.setBlockPosition(holderPos);
|
||||
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(realBlock));
|
||||
session.sendUpstreamPacket(blockPacket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
package org.geysermc.geyser.translator.protocol.bedrock;
|
||||
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ContainerClosePacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.inventory.Container;
|
||||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.inventory.MerchantContainer;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
@ -45,12 +47,29 @@ public class BedrockContainerCloseTranslator extends PacketTranslator<ContainerC
|
|||
session.sendUpstreamPacket(packet);
|
||||
session.setClosingInventory(false);
|
||||
|
||||
if (bedrockId == -1 && session.getOpenInventory() instanceof MerchantContainer) {
|
||||
// 1.16.200 - window ID is always -1 sent from Bedrock
|
||||
bedrockId = (byte) session.getOpenInventory().getBedrockId();
|
||||
// 1.21.70: Bedrock can reject opening inventories - in those cases it replies with -1
|
||||
Inventory openInventory = session.getOpenInventory();
|
||||
if (bedrockId == -1 && openInventory != null) {
|
||||
// 1.16.200 - window ID is always -1 sent from Bedrock for merchant containers
|
||||
bedrockId = (byte) openInventory.getBedrockId();
|
||||
|
||||
// If virtual inventories are opened too quickly, they can be occasionally rejected
|
||||
if (openInventory instanceof Container container && !container.isUsingRealBlock()) {
|
||||
if (session.getContainerOpenAttempts() < 3) {
|
||||
session.setContainerOpenAttempts(session.getContainerOpenAttempts() + 1);
|
||||
session.getInventoryTranslator().openInventory(session, session.getOpenInventory());
|
||||
session.getInventoryTranslator().updateInventory(session, session.getOpenInventory());
|
||||
session.getOpenInventory().setDisplayed(true);
|
||||
return;
|
||||
} else {
|
||||
GeyserImpl.getInstance().getLogger().debug("Exceeded 3 attempts to open a virtual inventory!");
|
||||
GeyserImpl.getInstance().getLogger().debug(packet + " " + session.getOpenInventory().getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Inventory openInventory = session.getOpenInventory();
|
||||
session.setContainerOpenAttempts(0);
|
||||
|
||||
if (openInventory != null) {
|
||||
if (bedrockId == openInventory.getBedrockId()) {
|
||||
InventoryUtils.sendJavaContainerClose(session, openInventory);
|
||||
|
|
|
@ -76,7 +76,7 @@ final class BedrockMovePlayer {
|
|||
boolean hasVehicle = entity.getVehicle() != null;
|
||||
|
||||
// shouldSendPositionReminder also increments a tick counter, so make sure it's always called unless the player is on a vehicle.
|
||||
boolean positionChanged = !hasVehicle && (session.getInputCache().shouldSendPositionReminder() || actualPositionChanged);
|
||||
boolean positionChangedAndShouldUpdate = !hasVehicle && (session.getInputCache().shouldSendPositionReminder() || actualPositionChanged);
|
||||
boolean rotationChanged = hasVehicle || (entity.getYaw() != yaw || entity.getPitch() != pitch || entity.getHeadYaw() != headYaw);
|
||||
|
||||
if (session.getLookBackScheduledFuture() != null) {
|
||||
|
@ -87,15 +87,23 @@ final class BedrockMovePlayer {
|
|||
}
|
||||
|
||||
// Client is telling us it wants to move down, but something is blocking it from doing so.
|
||||
boolean isOnGround = packet.getInputData().contains(PlayerAuthInputData.VERTICAL_COLLISION) && packet.getDelta().getY() < 0;
|
||||
boolean isOnGround;
|
||||
if (hasVehicle) {
|
||||
// VERTICAL_COLLISION is not accurate while in a vehicle (as of 1.21.62)
|
||||
isOnGround = Math.abs(packet.getDelta().getY()) < 0.1;
|
||||
} else {
|
||||
isOnGround = packet.getInputData().contains(PlayerAuthInputData.VERTICAL_COLLISION) && packet.getDelta().getY() < 0;
|
||||
}
|
||||
|
||||
// This takes into account no movement sent from the client, but the player is trying to move anyway.
|
||||
// (Press into a wall in a corner - you're trying to move but nothing actually happens)
|
||||
// This isn't sent when a player is riding a vehicle (as of 1.21.62)
|
||||
boolean horizontalCollision = packet.getInputData().contains(PlayerAuthInputData.HORIZONTAL_COLLISION);
|
||||
|
||||
// If only the pitch and yaw changed
|
||||
// This isn't needed, but it makes the packets closer to vanilla
|
||||
// It also means you can't "lag back" while only looking, in theory
|
||||
if (!positionChanged && rotationChanged) {
|
||||
if (!positionChangedAndShouldUpdate && rotationChanged) {
|
||||
ServerboundMovePlayerRotPacket playerRotationPacket = new ServerboundMovePlayerRotPacket(isOnGround, horizontalCollision, yaw, pitch);
|
||||
|
||||
entity.setYaw(yaw);
|
||||
|
@ -103,71 +111,79 @@ final class BedrockMovePlayer {
|
|||
entity.setHeadYaw(headYaw);
|
||||
|
||||
session.sendDownstreamGamePacket(playerRotationPacket);
|
||||
} else if (positionChanged) {
|
||||
|
||||
// Player position MUST be updated on our end, otherwise e.g. chunk loading breaks
|
||||
if (hasVehicle) {
|
||||
entity.setPositionManual(packet.getPosition());
|
||||
session.getSkullCache().updateVisibleSkulls();
|
||||
}
|
||||
} else if (positionChangedAndShouldUpdate) {
|
||||
if (isValidMove(session, entity.getPosition(), packet.getPosition())) {
|
||||
CollisionResult result = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), isOnGround, packet.getInputData().contains(PlayerAuthInputData.HANDLE_TELEPORT));
|
||||
if (result != null) { // A null return value cancels the packet
|
||||
Vector3d position = result.correctedMovement();
|
||||
boolean isBelowVoid = entity.isVoidPositionDesynched();
|
||||
if (!session.getWorldBorder().isPassingIntoBorderBoundaries(entity.getPosition(), true)) {
|
||||
CollisionResult result = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), isOnGround, packet.getInputData().contains(PlayerAuthInputData.HANDLE_TELEPORT));
|
||||
if (result != null) { // A null return value cancels the packet
|
||||
Vector3d position = result.correctedMovement();
|
||||
boolean isBelowVoid = entity.isVoidPositionDesynched();
|
||||
|
||||
boolean teleportThroughVoidFloor, mustResyncPosition;
|
||||
// Compare positions here for void floor fix below before the player's position variable is set to the packet position
|
||||
if (entity.getPosition().getY() >= packet.getPosition().getY() && !isBelowVoid) {
|
||||
int floorY = position.getFloorY();
|
||||
int voidFloorLocation = entity.voidFloorPosition();
|
||||
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 1) && floorY >= voidFloorLocation;
|
||||
} else {
|
||||
teleportThroughVoidFloor = false;
|
||||
}
|
||||
boolean teleportThroughVoidFloor, mustResyncPosition;
|
||||
// Compare positions here for void floor fix below before the player's position variable is set to the packet position
|
||||
if (entity.getPosition().getY() >= packet.getPosition().getY() && !isBelowVoid) {
|
||||
int floorY = position.getFloorY();
|
||||
int voidFloorLocation = entity.voidFloorPosition();
|
||||
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 1) && floorY >= voidFloorLocation;
|
||||
} else {
|
||||
teleportThroughVoidFloor = false;
|
||||
}
|
||||
|
||||
if (teleportThroughVoidFloor || isBelowVoid) {
|
||||
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
|
||||
isOnGround = false;
|
||||
}
|
||||
if (teleportThroughVoidFloor || isBelowVoid) {
|
||||
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
|
||||
isOnGround = false;
|
||||
}
|
||||
|
||||
if (isBelowVoid) {
|
||||
int floorY = position.getFloorY();
|
||||
int voidFloorLocation = entity.voidFloorPosition();
|
||||
mustResyncPosition = floorY < voidFloorLocation && floorY >= voidFloorLocation - 1;
|
||||
} else {
|
||||
mustResyncPosition = false;
|
||||
}
|
||||
if (isBelowVoid) {
|
||||
int floorY = position.getFloorY();
|
||||
int voidFloorLocation = entity.voidFloorPosition();
|
||||
mustResyncPosition = floorY < voidFloorLocation && floorY >= voidFloorLocation - 1;
|
||||
} else {
|
||||
mustResyncPosition = false;
|
||||
}
|
||||
|
||||
double yPosition = position.getY();
|
||||
if (entity.isVoidPositionDesynched()) { // not using the cached variable on purpose
|
||||
yPosition += 4; // We are de-synched since we had to teleport below the void floor.
|
||||
}
|
||||
double yPosition = position.getY();
|
||||
if (entity.isVoidPositionDesynched()) { // not using the cached variable on purpose
|
||||
yPosition += 4; // We are de-synched since we had to teleport below the void floor.
|
||||
}
|
||||
|
||||
Packet movePacket;
|
||||
if (rotationChanged) {
|
||||
// Send rotation updates as well
|
||||
movePacket = new ServerboundMovePlayerPosRotPacket(
|
||||
Packet movePacket;
|
||||
if (rotationChanged) {
|
||||
// Send rotation updates as well
|
||||
movePacket = new ServerboundMovePlayerPosRotPacket(
|
||||
isOnGround,
|
||||
horizontalCollision,
|
||||
position.getX(), yPosition, position.getZ(),
|
||||
yaw, pitch
|
||||
);
|
||||
entity.setYaw(yaw);
|
||||
entity.setPitch(pitch);
|
||||
entity.setHeadYaw(headYaw);
|
||||
} else {
|
||||
// Rotation did not change; don't send an update with rotation
|
||||
movePacket = new ServerboundMovePlayerPosPacket(isOnGround, horizontalCollision, position.getX(), yPosition, position.getZ());
|
||||
);
|
||||
entity.setYaw(yaw);
|
||||
entity.setPitch(pitch);
|
||||
entity.setHeadYaw(headYaw);
|
||||
} else {
|
||||
// Rotation did not change; don't send an update with rotation
|
||||
movePacket = new ServerboundMovePlayerPosPacket(isOnGround, horizontalCollision, position.getX(), yPosition, position.getZ());
|
||||
}
|
||||
|
||||
entity.setPositionManual(packet.getPosition());
|
||||
|
||||
// Send final movement changes
|
||||
session.sendDownstreamGamePacket(movePacket);
|
||||
|
||||
if (teleportThroughVoidFloor) {
|
||||
entity.teleportVoidFloorFix(false);
|
||||
} else if (mustResyncPosition) {
|
||||
entity.teleportVoidFloorFix(true);
|
||||
}
|
||||
|
||||
session.getInputCache().markPositionPacketSent();
|
||||
session.getSkullCache().updateVisibleSkulls();
|
||||
}
|
||||
|
||||
entity.setPositionManual(packet.getPosition());
|
||||
|
||||
// Send final movement changes
|
||||
session.sendDownstreamGamePacket(movePacket);
|
||||
|
||||
if (teleportThroughVoidFloor) {
|
||||
entity.teleportVoidFloorFix(false);
|
||||
} else if (mustResyncPosition) {
|
||||
entity.teleportVoidFloorFix(true);
|
||||
}
|
||||
|
||||
session.getInputCache().markPositionPacketSent();
|
||||
session.getSkullCache().updateVisibleSkulls();
|
||||
}
|
||||
} else {
|
||||
// Not a valid move
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
|
|||
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlaySoundPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket;
|
||||
|
@ -143,7 +143,7 @@ public class JavaEntityEventTranslator extends PacketTranslator<ClientboundEntit
|
|||
entityEventPacket.setType(EntityEventType.TAME_SUCCEEDED);
|
||||
break;
|
||||
case ZOMBIE_VILLAGER_CURE: // Played when a zombie bites the golden apple
|
||||
LevelSoundEvent2Packet soundPacket = new LevelSoundEvent2Packet();
|
||||
LevelSoundEventPacket soundPacket = new LevelSoundEventPacket();
|
||||
soundPacket.setSound(SoundEvent.REMEDY);
|
||||
soundPacket.setPosition(entity.getPosition());
|
||||
soundPacket.setExtraData(-1);
|
||||
|
@ -241,7 +241,7 @@ public class JavaEntityEventTranslator extends PacketTranslator<ClientboundEntit
|
|||
case LIVING_EQUIPMENT_BREAK_FEET:
|
||||
case LIVING_EQUIPMENT_BREAK_MAIN_HAND:
|
||||
case LIVING_EQUIPMENT_BREAK_OFF_HAND:
|
||||
LevelSoundEvent2Packet equipmentBreakPacket = new LevelSoundEvent2Packet();
|
||||
LevelSoundEventPacket equipmentBreakPacket = new LevelSoundEventPacket();
|
||||
equipmentBreakPacket.setSound(SoundEvent.BREAK);
|
||||
equipmentBreakPacket.setPosition(entity.getPosition());
|
||||
equipmentBreakPacket.setExtraData(-1);
|
||||
|
|
|
@ -26,11 +26,11 @@
|
|||
package org.geysermc.geyser.translator.protocol.java.inventory;
|
||||
|
||||
import org.geysermc.geyser.inventory.PlayerInventory;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundContainerClosePacket;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundContainerClosePacket;
|
||||
|
||||
@Translator(packet = ClientboundContainerClosePacket.class)
|
||||
public class JavaContainerCloseTranslator extends PacketTranslator<ClientboundContainerClosePacket> {
|
||||
|
|
|
@ -25,9 +25,6 @@
|
|||
|
||||
package org.geysermc.geyser.translator.protocol.java.inventory;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundOpenScreenPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClosePacket;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
@ -37,6 +34,9 @@ import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
|||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundOpenScreenPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClosePacket;
|
||||
|
||||
@Translator(packet = ClientboundOpenScreenPacket.class)
|
||||
public class JavaOpenScreenTranslator extends PacketTranslator<ClientboundOpenScreenPacket> {
|
||||
|
|
|
@ -55,7 +55,6 @@ import org.geysermc.geyser.text.ChatColor;
|
|||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
|
||||
import org.geysermc.geyser.translator.inventory.chest.DoubleChestInventoryTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.CompositeSlotDisplay;
|
||||
|
@ -100,7 +99,8 @@ public class InventoryUtils {
|
|||
public static void displayInventory(GeyserSession session, Inventory inventory) {
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
if (translator.prepareInventory(session, inventory)) {
|
||||
if (translator instanceof DoubleChestInventoryTranslator && !((Container) inventory).isUsingRealBlock()) {
|
||||
// 1.21.70 wants a delay on all virtual inventories: https://github.com/GeyserMC/Geyser/issues/5426
|
||||
if (inventory instanceof Container container && !container.isUsingRealBlock()) {
|
||||
session.scheduleInEventLoop(() -> {
|
||||
Inventory openInv = session.getOpenInventory();
|
||||
if (openInv != null && openInv.getJavaId() == inventory.getJavaId()) {
|
||||
|
@ -111,7 +111,7 @@ public class InventoryUtils {
|
|||
// Presumably, this inventory is no longer relevant, and the client doesn't care about it
|
||||
displayInventory(session, openInv);
|
||||
}
|
||||
}, 200, TimeUnit.MILLISECONDS);
|
||||
}, 300, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
translator.openInventory(session, inventory);
|
||||
translator.updateInventory(session, inventory);
|
||||
|
|
|
@ -28,22 +28,26 @@ package org.geysermc.geyser.util;
|
|||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.InitialDirContext;
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class WebUtils {
|
||||
|
||||
private static final Path REMOTE_PACK_CACHE = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("remote_packs");
|
||||
|
||||
/**
|
||||
* Makes a web request to the given URL and returns the body as a string
|
||||
*
|
||||
|
@ -96,6 +100,115 @@ public class WebUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a remote pack URL to see if it is valid
|
||||
* If it is, it will download the pack file and return a path to it
|
||||
*
|
||||
* @param url The URL to check
|
||||
* @param force If true, the pack will be downloaded even if it is cached to a separate location.
|
||||
* @return Path to the downloaded pack file, or null if it was unable to be loaded
|
||||
*/
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public static @Nullable Path downloadRemotePack(String url, boolean force) {
|
||||
GeyserLogger logger = GeyserImpl.getInstance().getLogger();
|
||||
try {
|
||||
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
|
||||
|
||||
con.setConnectTimeout(10000);
|
||||
con.setReadTimeout(10000);
|
||||
con.setRequestProperty("User-Agent", "Geyser-" + GeyserImpl.getInstance().getPlatformType().platformName() + "/" + GeyserImpl.VERSION);
|
||||
con.setInstanceFollowRedirects(true);
|
||||
|
||||
int responseCode = con.getResponseCode();
|
||||
if (responseCode >= 400) {
|
||||
throw new IllegalStateException(String.format("Invalid response code from remote pack at URL: %s (code: %d)", url, responseCode));
|
||||
}
|
||||
|
||||
int size = con.getContentLength();
|
||||
String type = con.getContentType();
|
||||
|
||||
if (size <= 0) {
|
||||
throw new IllegalArgumentException(String.format("Invalid content length received from remote pack at URL: %s (size: %d)", url, size));
|
||||
}
|
||||
|
||||
if (type == null || !type.equals("application/zip")) {
|
||||
throw new IllegalArgumentException(String.format("Url %s tries to provide a resource pack using the %s content type, which is not supported by Bedrock edition! " +
|
||||
"Bedrock Edition only supports the application/zip content type.", url, type));
|
||||
}
|
||||
|
||||
Path packMetadata = REMOTE_PACK_CACHE.resolve(url.hashCode() + ".metadata");
|
||||
Path downloadLocation;
|
||||
|
||||
// If we downloaded this pack before, reuse it if the ETag matches.
|
||||
if (Files.exists(packMetadata) && !force) {
|
||||
try {
|
||||
List<String> metadata = Files.readAllLines(packMetadata, StandardCharsets.UTF_8);
|
||||
int cachedSize = Integer.parseInt(metadata.get(0));
|
||||
String cachedEtag = metadata.get(1);
|
||||
long cachedLastModified = Long.parseLong(metadata.get(2));
|
||||
downloadLocation = REMOTE_PACK_CACHE.resolve(metadata.get(3));
|
||||
|
||||
if (cachedSize == size &&
|
||||
cachedEtag.equals(con.getHeaderField("ETag")) &&
|
||||
cachedLastModified == con.getLastModified() &&
|
||||
downloadLocation.toFile().exists()) {
|
||||
logger.debug("Using cached pack (%s) for %s.".formatted(downloadLocation.getFileName(), url));
|
||||
downloadLocation.toFile().setLastModified(System.currentTimeMillis());
|
||||
packMetadata.toFile().setLastModified(System.currentTimeMillis());
|
||||
return downloadLocation;
|
||||
} else {
|
||||
logger.debug("Deleting cached pack/metadata (%s) as it appears to have changed!".formatted(url));
|
||||
Files.deleteIfExists(packMetadata);
|
||||
Files.deleteIfExists(downloadLocation);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Failed to read cached pack metadata! " + e);
|
||||
packMetadata.toFile().deleteOnExit();
|
||||
}
|
||||
}
|
||||
|
||||
downloadLocation = REMOTE_PACK_CACHE.resolve(url.hashCode() + "_" + System.currentTimeMillis() + ".zip");
|
||||
Files.copy(con.getInputStream(), downloadLocation, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// This needs to match as the client fails to download the pack otherwise
|
||||
long downloadSize = Files.size(downloadLocation);
|
||||
if (downloadSize != size) {
|
||||
Files.delete(downloadLocation);
|
||||
throw new IllegalStateException("Size mismatch with resource pack at url: %s. Downloaded pack has %s bytes, expected %s bytes!"
|
||||
.formatted(url, downloadSize, size));
|
||||
}
|
||||
|
||||
try {
|
||||
Files.write(
|
||||
packMetadata,
|
||||
Arrays.asList(
|
||||
String.valueOf(size),
|
||||
con.getHeaderField("ETag"),
|
||||
String.valueOf(con.getLastModified()),
|
||||
downloadLocation.getFileName().toString()
|
||||
));
|
||||
packMetadata.toFile().setLastModified(System.currentTimeMillis());
|
||||
} catch (IOException e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Failed to write cached pack metadata: " + e.getMessage());
|
||||
Files.delete(packMetadata);
|
||||
Files.delete(downloadLocation);
|
||||
return null;
|
||||
}
|
||||
|
||||
downloadLocation.toFile().setLastModified(System.currentTimeMillis());
|
||||
return downloadLocation;
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException("Unable to download resource pack from malformed URL %s! ".formatted(url));
|
||||
} catch (SocketTimeoutException | ConnectException e) {
|
||||
logger.error("Unable to download pack from url %s due to network error! ( %s )".formatted(url, e.getMessage()));
|
||||
logger.debug(e);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Unable to download and save remote resource pack from: %s ( %s )!".formatted(url, e.getMessage()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Post a string to the given URL
|
||||
*
|
||||
|
|
Binary file not shown.
BIN
core/src/main/resources/bedrock/block_palette.1_21_70.nbt
Normal file
BIN
core/src/main/resources/bedrock/block_palette.1_21_70.nbt
Normal file
Binary file not shown.
8960
core/src/main/resources/bedrock/creative_items.1_21_70.json
Normal file
8960
core/src/main/resources/bedrock/creative_items.1_21_70.json
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
829
core/src/main/resources/bedrock/item_tags.1_21_70.json
Normal file
829
core/src/main/resources/bedrock/item_tags.1_21_70.json
Normal file
|
@ -0,0 +1,829 @@
|
|||
{
|
||||
"minecraft:arrow": [
|
||||
"minecraft:arrow"
|
||||
],
|
||||
"minecraft:banner": [
|
||||
"minecraft:banner"
|
||||
],
|
||||
"minecraft:boat": [
|
||||
"minecraft:acacia_boat",
|
||||
"minecraft:acacia_chest_boat",
|
||||
"minecraft:bamboo_chest_raft",
|
||||
"minecraft:bamboo_raft",
|
||||
"minecraft:birch_boat",
|
||||
"minecraft:birch_chest_boat",
|
||||
"minecraft:cherry_boat",
|
||||
"minecraft:cherry_chest_boat",
|
||||
"minecraft:dark_oak_boat",
|
||||
"minecraft:dark_oak_chest_boat",
|
||||
"minecraft:jungle_boat",
|
||||
"minecraft:jungle_chest_boat",
|
||||
"minecraft:mangrove_boat",
|
||||
"minecraft:mangrove_chest_boat",
|
||||
"minecraft:oak_boat",
|
||||
"minecraft:oak_chest_boat",
|
||||
"minecraft:pale_oak_boat",
|
||||
"minecraft:pale_oak_chest_boat",
|
||||
"minecraft:spruce_boat",
|
||||
"minecraft:spruce_chest_boat"
|
||||
],
|
||||
"minecraft:boats": [
|
||||
"minecraft:acacia_boat",
|
||||
"minecraft:acacia_chest_boat",
|
||||
"minecraft:bamboo_chest_raft",
|
||||
"minecraft:bamboo_raft",
|
||||
"minecraft:birch_boat",
|
||||
"minecraft:birch_chest_boat",
|
||||
"minecraft:cherry_boat",
|
||||
"minecraft:cherry_chest_boat",
|
||||
"minecraft:dark_oak_boat",
|
||||
"minecraft:dark_oak_chest_boat",
|
||||
"minecraft:jungle_boat",
|
||||
"minecraft:jungle_chest_boat",
|
||||
"minecraft:mangrove_boat",
|
||||
"minecraft:mangrove_chest_boat",
|
||||
"minecraft:oak_boat",
|
||||
"minecraft:oak_chest_boat",
|
||||
"minecraft:pale_oak_boat",
|
||||
"minecraft:pale_oak_chest_boat",
|
||||
"minecraft:spruce_boat",
|
||||
"minecraft:spruce_chest_boat"
|
||||
],
|
||||
"minecraft:bookshelf_books": [
|
||||
"minecraft:book",
|
||||
"minecraft:enchanted_book",
|
||||
"minecraft:writable_book"
|
||||
],
|
||||
"minecraft:chainmail_tier": [
|
||||
"minecraft:chainmail_boots",
|
||||
"minecraft:chainmail_chestplate",
|
||||
"minecraft:chainmail_helmet",
|
||||
"minecraft:chainmail_leggings"
|
||||
],
|
||||
"minecraft:coals": [
|
||||
"minecraft:charcoal",
|
||||
"minecraft:coal"
|
||||
],
|
||||
"minecraft:crimson_stems": [
|
||||
"minecraft:crimson_hyphae",
|
||||
"minecraft:crimson_stem",
|
||||
"minecraft:stripped_crimson_hyphae",
|
||||
"minecraft:stripped_crimson_stem"
|
||||
],
|
||||
"minecraft:decorated_pot_sherds": [
|
||||
"minecraft:angler_pottery_sherd",
|
||||
"minecraft:archer_pottery_sherd",
|
||||
"minecraft:arms_up_pottery_sherd",
|
||||
"minecraft:blade_pottery_sherd",
|
||||
"minecraft:brewer_pottery_sherd",
|
||||
"minecraft:brick",
|
||||
"minecraft:burn_pottery_sherd",
|
||||
"minecraft:danger_pottery_sherd",
|
||||
"minecraft:explorer_pottery_sherd",
|
||||
"minecraft:flow_pottery_sherd",
|
||||
"minecraft:friend_pottery_sherd",
|
||||
"minecraft:guster_pottery_sherd",
|
||||
"minecraft:heart_pottery_sherd",
|
||||
"minecraft:heartbreak_pottery_sherd",
|
||||
"minecraft:howl_pottery_sherd",
|
||||
"minecraft:miner_pottery_sherd",
|
||||
"minecraft:mourner_pottery_sherd",
|
||||
"minecraft:plenty_pottery_sherd",
|
||||
"minecraft:prize_pottery_sherd",
|
||||
"minecraft:scrape_pottery_sherd",
|
||||
"minecraft:sheaf_pottery_sherd",
|
||||
"minecraft:shelter_pottery_sherd",
|
||||
"minecraft:skull_pottery_sherd",
|
||||
"minecraft:snort_pottery_sherd"
|
||||
],
|
||||
"minecraft:diamond_tier": [
|
||||
"minecraft:diamond_axe",
|
||||
"minecraft:diamond_boots",
|
||||
"minecraft:diamond_chestplate",
|
||||
"minecraft:diamond_helmet",
|
||||
"minecraft:diamond_hoe",
|
||||
"minecraft:diamond_leggings",
|
||||
"minecraft:diamond_pickaxe",
|
||||
"minecraft:diamond_shovel",
|
||||
"minecraft:diamond_sword",
|
||||
"minecraft:mace"
|
||||
],
|
||||
"minecraft:digger": [
|
||||
"minecraft:diamond_axe",
|
||||
"minecraft:diamond_hoe",
|
||||
"minecraft:diamond_pickaxe",
|
||||
"minecraft:diamond_shovel",
|
||||
"minecraft:golden_axe",
|
||||
"minecraft:golden_hoe",
|
||||
"minecraft:golden_pickaxe",
|
||||
"minecraft:golden_shovel",
|
||||
"minecraft:iron_axe",
|
||||
"minecraft:iron_hoe",
|
||||
"minecraft:iron_pickaxe",
|
||||
"minecraft:iron_shovel",
|
||||
"minecraft:netherite_axe",
|
||||
"minecraft:netherite_hoe",
|
||||
"minecraft:netherite_pickaxe",
|
||||
"minecraft:netherite_shovel",
|
||||
"minecraft:stone_axe",
|
||||
"minecraft:stone_hoe",
|
||||
"minecraft:stone_pickaxe",
|
||||
"minecraft:stone_shovel",
|
||||
"minecraft:wooden_axe",
|
||||
"minecraft:wooden_hoe",
|
||||
"minecraft:wooden_pickaxe",
|
||||
"minecraft:wooden_shovel"
|
||||
],
|
||||
"minecraft:door": [
|
||||
"minecraft:acacia_door",
|
||||
"minecraft:bamboo_door",
|
||||
"minecraft:birch_door",
|
||||
"minecraft:cherry_door",
|
||||
"minecraft:copper_door",
|
||||
"minecraft:crimson_door",
|
||||
"minecraft:dark_oak_door",
|
||||
"minecraft:exposed_copper_door",
|
||||
"minecraft:iron_door",
|
||||
"minecraft:jungle_door",
|
||||
"minecraft:mangrove_door",
|
||||
"minecraft:oxidized_copper_door",
|
||||
"minecraft:pale_oak_door",
|
||||
"minecraft:spruce_door",
|
||||
"minecraft:warped_door",
|
||||
"minecraft:waxed_copper_door",
|
||||
"minecraft:waxed_exposed_copper_door",
|
||||
"minecraft:waxed_oxidized_copper_door",
|
||||
"minecraft:waxed_weathered_copper_door",
|
||||
"minecraft:weathered_copper_door",
|
||||
"minecraft:wooden_door"
|
||||
],
|
||||
"minecraft:egg": [
|
||||
"minecraft:blue_egg",
|
||||
"minecraft:brown_egg",
|
||||
"minecraft:egg"
|
||||
],
|
||||
"minecraft:golden_tier": [
|
||||
"minecraft:golden_axe",
|
||||
"minecraft:golden_boots",
|
||||
"minecraft:golden_chestplate",
|
||||
"minecraft:golden_helmet",
|
||||
"minecraft:golden_hoe",
|
||||
"minecraft:golden_leggings",
|
||||
"minecraft:golden_pickaxe",
|
||||
"minecraft:golden_shovel",
|
||||
"minecraft:golden_sword"
|
||||
],
|
||||
"minecraft:hanging_actor": [
|
||||
"minecraft:painting"
|
||||
],
|
||||
"minecraft:hanging_sign": [
|
||||
"minecraft:acacia_hanging_sign",
|
||||
"minecraft:bamboo_hanging_sign",
|
||||
"minecraft:birch_hanging_sign",
|
||||
"minecraft:cherry_hanging_sign",
|
||||
"minecraft:crimson_hanging_sign",
|
||||
"minecraft:dark_oak_hanging_sign",
|
||||
"minecraft:jungle_hanging_sign",
|
||||
"minecraft:mangrove_hanging_sign",
|
||||
"minecraft:oak_hanging_sign",
|
||||
"minecraft:pale_oak_hanging_sign",
|
||||
"minecraft:spruce_hanging_sign",
|
||||
"minecraft:warped_hanging_sign"
|
||||
],
|
||||
"minecraft:horse_armor": [
|
||||
"minecraft:diamond_horse_armor",
|
||||
"minecraft:golden_horse_armor",
|
||||
"minecraft:iron_horse_armor",
|
||||
"minecraft:leather_horse_armor"
|
||||
],
|
||||
"minecraft:iron_tier": [
|
||||
"minecraft:iron_axe",
|
||||
"minecraft:iron_boots",
|
||||
"minecraft:iron_chestplate",
|
||||
"minecraft:iron_helmet",
|
||||
"minecraft:iron_hoe",
|
||||
"minecraft:iron_leggings",
|
||||
"minecraft:iron_pickaxe",
|
||||
"minecraft:iron_shovel",
|
||||
"minecraft:iron_sword"
|
||||
],
|
||||
"minecraft:is_armor": [
|
||||
"minecraft:chainmail_boots",
|
||||
"minecraft:chainmail_chestplate",
|
||||
"minecraft:chainmail_helmet",
|
||||
"minecraft:chainmail_leggings",
|
||||
"minecraft:diamond_boots",
|
||||
"minecraft:diamond_chestplate",
|
||||
"minecraft:diamond_helmet",
|
||||
"minecraft:diamond_leggings",
|
||||
"minecraft:elytra",
|
||||
"minecraft:golden_boots",
|
||||
"minecraft:golden_chestplate",
|
||||
"minecraft:golden_helmet",
|
||||
"minecraft:golden_leggings",
|
||||
"minecraft:iron_boots",
|
||||
"minecraft:iron_chestplate",
|
||||
"minecraft:iron_helmet",
|
||||
"minecraft:iron_leggings",
|
||||
"minecraft:leather_boots",
|
||||
"minecraft:leather_chestplate",
|
||||
"minecraft:leather_helmet",
|
||||
"minecraft:leather_leggings",
|
||||
"minecraft:netherite_boots",
|
||||
"minecraft:netherite_chestplate",
|
||||
"minecraft:netherite_helmet",
|
||||
"minecraft:netherite_leggings",
|
||||
"minecraft:turtle_helmet"
|
||||
],
|
||||
"minecraft:is_axe": [
|
||||
"minecraft:diamond_axe",
|
||||
"minecraft:golden_axe",
|
||||
"minecraft:iron_axe",
|
||||
"minecraft:netherite_axe",
|
||||
"minecraft:stone_axe",
|
||||
"minecraft:wooden_axe"
|
||||
],
|
||||
"minecraft:is_cooked": [
|
||||
"minecraft:cooked_beef",
|
||||
"minecraft:cooked_chicken",
|
||||
"minecraft:cooked_cod",
|
||||
"minecraft:cooked_mutton",
|
||||
"minecraft:cooked_porkchop",
|
||||
"minecraft:cooked_rabbit",
|
||||
"minecraft:cooked_salmon",
|
||||
"minecraft:rabbit_stew"
|
||||
],
|
||||
"minecraft:is_fish": [
|
||||
"minecraft:cod",
|
||||
"minecraft:cooked_cod",
|
||||
"minecraft:cooked_salmon",
|
||||
"minecraft:pufferfish",
|
||||
"minecraft:salmon",
|
||||
"minecraft:tropical_fish"
|
||||
],
|
||||
"minecraft:is_food": [
|
||||
"minecraft:apple",
|
||||
"minecraft:baked_potato",
|
||||
"minecraft:beef",
|
||||
"minecraft:beetroot",
|
||||
"minecraft:beetroot_soup",
|
||||
"minecraft:bread",
|
||||
"minecraft:carrot",
|
||||
"minecraft:chicken",
|
||||
"minecraft:cooked_beef",
|
||||
"minecraft:cooked_chicken",
|
||||
"minecraft:cooked_mutton",
|
||||
"minecraft:cooked_porkchop",
|
||||
"minecraft:cooked_rabbit",
|
||||
"minecraft:cookie",
|
||||
"minecraft:dried_kelp",
|
||||
"minecraft:enchanted_golden_apple",
|
||||
"minecraft:golden_apple",
|
||||
"minecraft:golden_carrot",
|
||||
"minecraft:melon_slice",
|
||||
"minecraft:mushroom_stew",
|
||||
"minecraft:mutton",
|
||||
"minecraft:porkchop",
|
||||
"minecraft:potato",
|
||||
"minecraft:pumpkin_pie",
|
||||
"minecraft:rabbit",
|
||||
"minecraft:rabbit_stew",
|
||||
"minecraft:rotten_flesh",
|
||||
"minecraft:sweet_berries"
|
||||
],
|
||||
"minecraft:is_hoe": [
|
||||
"minecraft:diamond_hoe",
|
||||
"minecraft:golden_hoe",
|
||||
"minecraft:iron_hoe",
|
||||
"minecraft:netherite_hoe",
|
||||
"minecraft:stone_hoe",
|
||||
"minecraft:wooden_hoe"
|
||||
],
|
||||
"minecraft:is_meat": [
|
||||
"minecraft:beef",
|
||||
"minecraft:chicken",
|
||||
"minecraft:cooked_beef",
|
||||
"minecraft:cooked_chicken",
|
||||
"minecraft:cooked_mutton",
|
||||
"minecraft:cooked_porkchop",
|
||||
"minecraft:cooked_rabbit",
|
||||
"minecraft:mutton",
|
||||
"minecraft:porkchop",
|
||||
"minecraft:rabbit",
|
||||
"minecraft:rabbit_stew",
|
||||
"minecraft:rotten_flesh"
|
||||
],
|
||||
"minecraft:is_minecart": [
|
||||
"minecraft:chest_minecart",
|
||||
"minecraft:command_block_minecart",
|
||||
"minecraft:hopper_minecart",
|
||||
"minecraft:minecart",
|
||||
"minecraft:tnt_minecart"
|
||||
],
|
||||
"minecraft:is_pickaxe": [
|
||||
"minecraft:diamond_pickaxe",
|
||||
"minecraft:golden_pickaxe",
|
||||
"minecraft:iron_pickaxe",
|
||||
"minecraft:netherite_pickaxe",
|
||||
"minecraft:stone_pickaxe",
|
||||
"minecraft:wooden_pickaxe"
|
||||
],
|
||||
"minecraft:is_shears": [
|
||||
"minecraft:shears"
|
||||
],
|
||||
"minecraft:is_shovel": [
|
||||
"minecraft:diamond_shovel",
|
||||
"minecraft:golden_shovel",
|
||||
"minecraft:iron_shovel",
|
||||
"minecraft:netherite_shovel",
|
||||
"minecraft:stone_shovel",
|
||||
"minecraft:wooden_shovel"
|
||||
],
|
||||
"minecraft:is_sword": [
|
||||
"minecraft:diamond_sword",
|
||||
"minecraft:golden_sword",
|
||||
"minecraft:iron_sword",
|
||||
"minecraft:mace",
|
||||
"minecraft:netherite_sword",
|
||||
"minecraft:stone_sword",
|
||||
"minecraft:wooden_sword"
|
||||
],
|
||||
"minecraft:is_tool": [
|
||||
"minecraft:diamond_axe",
|
||||
"minecraft:diamond_hoe",
|
||||
"minecraft:diamond_pickaxe",
|
||||
"minecraft:diamond_shovel",
|
||||
"minecraft:diamond_sword",
|
||||
"minecraft:golden_axe",
|
||||
"minecraft:golden_hoe",
|
||||
"minecraft:golden_pickaxe",
|
||||
"minecraft:golden_shovel",
|
||||
"minecraft:golden_sword",
|
||||
"minecraft:iron_axe",
|
||||
"minecraft:iron_hoe",
|
||||
"minecraft:iron_pickaxe",
|
||||
"minecraft:iron_shovel",
|
||||
"minecraft:iron_sword",
|
||||
"minecraft:mace",
|
||||
"minecraft:netherite_axe",
|
||||
"minecraft:netherite_hoe",
|
||||
"minecraft:netherite_pickaxe",
|
||||
"minecraft:netherite_shovel",
|
||||
"minecraft:netherite_sword",
|
||||
"minecraft:stone_axe",
|
||||
"minecraft:stone_hoe",
|
||||
"minecraft:stone_pickaxe",
|
||||
"minecraft:stone_shovel",
|
||||
"minecraft:stone_sword",
|
||||
"minecraft:wooden_axe",
|
||||
"minecraft:wooden_hoe",
|
||||
"minecraft:wooden_pickaxe",
|
||||
"minecraft:wooden_shovel",
|
||||
"minecraft:wooden_sword"
|
||||
],
|
||||
"minecraft:is_trident": [
|
||||
"minecraft:trident"
|
||||
],
|
||||
"minecraft:leather_tier": [
|
||||
"minecraft:leather_boots",
|
||||
"minecraft:leather_chestplate",
|
||||
"minecraft:leather_helmet",
|
||||
"minecraft:leather_leggings"
|
||||
],
|
||||
"minecraft:lectern_books": [
|
||||
"minecraft:writable_book"
|
||||
],
|
||||
"minecraft:logs": [
|
||||
"minecraft:acacia_log",
|
||||
"minecraft:acacia_wood",
|
||||
"minecraft:birch_log",
|
||||
"minecraft:birch_wood",
|
||||
"minecraft:cherry_log",
|
||||
"minecraft:cherry_wood",
|
||||
"minecraft:crimson_hyphae",
|
||||
"minecraft:crimson_stem",
|
||||
"minecraft:dark_oak_log",
|
||||
"minecraft:dark_oak_wood",
|
||||
"minecraft:jungle_log",
|
||||
"minecraft:jungle_wood",
|
||||
"minecraft:mangrove_log",
|
||||
"minecraft:mangrove_wood",
|
||||
"minecraft:oak_log",
|
||||
"minecraft:oak_wood",
|
||||
"minecraft:pale_oak_log",
|
||||
"minecraft:pale_oak_wood",
|
||||
"minecraft:spruce_log",
|
||||
"minecraft:spruce_wood",
|
||||
"minecraft:stripped_acacia_log",
|
||||
"minecraft:stripped_acacia_wood",
|
||||
"minecraft:stripped_birch_log",
|
||||
"minecraft:stripped_birch_wood",
|
||||
"minecraft:stripped_cherry_log",
|
||||
"minecraft:stripped_cherry_wood",
|
||||
"minecraft:stripped_crimson_hyphae",
|
||||
"minecraft:stripped_crimson_stem",
|
||||
"minecraft:stripped_dark_oak_log",
|
||||
"minecraft:stripped_dark_oak_wood",
|
||||
"minecraft:stripped_jungle_log",
|
||||
"minecraft:stripped_jungle_wood",
|
||||
"minecraft:stripped_mangrove_log",
|
||||
"minecraft:stripped_mangrove_wood",
|
||||
"minecraft:stripped_oak_log",
|
||||
"minecraft:stripped_oak_wood",
|
||||
"minecraft:stripped_pale_oak_log",
|
||||
"minecraft:stripped_pale_oak_wood",
|
||||
"minecraft:stripped_spruce_log",
|
||||
"minecraft:stripped_spruce_wood",
|
||||
"minecraft:stripped_warped_hyphae",
|
||||
"minecraft:stripped_warped_stem",
|
||||
"minecraft:warped_hyphae",
|
||||
"minecraft:warped_stem"
|
||||
],
|
||||
"minecraft:logs_that_burn": [
|
||||
"minecraft:acacia_log",
|
||||
"minecraft:acacia_wood",
|
||||
"minecraft:birch_log",
|
||||
"minecraft:birch_wood",
|
||||
"minecraft:cherry_log",
|
||||
"minecraft:cherry_wood",
|
||||
"minecraft:dark_oak_log",
|
||||
"minecraft:dark_oak_wood",
|
||||
"minecraft:jungle_log",
|
||||
"minecraft:jungle_wood",
|
||||
"minecraft:mangrove_log",
|
||||
"minecraft:mangrove_wood",
|
||||
"minecraft:oak_log",
|
||||
"minecraft:oak_wood",
|
||||
"minecraft:pale_oak_log",
|
||||
"minecraft:pale_oak_wood",
|
||||
"minecraft:spruce_log",
|
||||
"minecraft:spruce_wood",
|
||||
"minecraft:stripped_acacia_log",
|
||||
"minecraft:stripped_acacia_wood",
|
||||
"minecraft:stripped_birch_log",
|
||||
"minecraft:stripped_birch_wood",
|
||||
"minecraft:stripped_cherry_log",
|
||||
"minecraft:stripped_cherry_wood",
|
||||
"minecraft:stripped_dark_oak_log",
|
||||
"minecraft:stripped_dark_oak_wood",
|
||||
"minecraft:stripped_jungle_log",
|
||||
"minecraft:stripped_jungle_wood",
|
||||
"minecraft:stripped_mangrove_log",
|
||||
"minecraft:stripped_mangrove_wood",
|
||||
"minecraft:stripped_oak_log",
|
||||
"minecraft:stripped_oak_wood",
|
||||
"minecraft:stripped_pale_oak_log",
|
||||
"minecraft:stripped_pale_oak_wood",
|
||||
"minecraft:stripped_spruce_log",
|
||||
"minecraft:stripped_spruce_wood"
|
||||
],
|
||||
"minecraft:mangrove_logs": [
|
||||
"minecraft:mangrove_log",
|
||||
"minecraft:mangrove_wood",
|
||||
"minecraft:stripped_mangrove_log",
|
||||
"minecraft:stripped_mangrove_wood"
|
||||
],
|
||||
"minecraft:music_disc": [
|
||||
"minecraft:music_disc_11",
|
||||
"minecraft:music_disc_13",
|
||||
"minecraft:music_disc_5",
|
||||
"minecraft:music_disc_blocks",
|
||||
"minecraft:music_disc_cat",
|
||||
"minecraft:music_disc_chirp",
|
||||
"minecraft:music_disc_creator",
|
||||
"minecraft:music_disc_creator_music_box",
|
||||
"minecraft:music_disc_far",
|
||||
"minecraft:music_disc_mall",
|
||||
"minecraft:music_disc_mellohi",
|
||||
"minecraft:music_disc_otherside",
|
||||
"minecraft:music_disc_pigstep",
|
||||
"minecraft:music_disc_precipice",
|
||||
"minecraft:music_disc_relic",
|
||||
"minecraft:music_disc_stal",
|
||||
"minecraft:music_disc_strad",
|
||||
"minecraft:music_disc_wait",
|
||||
"minecraft:music_disc_ward"
|
||||
],
|
||||
"minecraft:netherite_tier": [
|
||||
"minecraft:netherite_axe",
|
||||
"minecraft:netherite_boots",
|
||||
"minecraft:netherite_chestplate",
|
||||
"minecraft:netherite_helmet",
|
||||
"minecraft:netherite_hoe",
|
||||
"minecraft:netherite_leggings",
|
||||
"minecraft:netherite_pickaxe",
|
||||
"minecraft:netherite_shovel",
|
||||
"minecraft:netherite_sword"
|
||||
],
|
||||
"minecraft:planks": [
|
||||
"minecraft:acacia_planks",
|
||||
"minecraft:bamboo_planks",
|
||||
"minecraft:birch_planks",
|
||||
"minecraft:cherry_planks",
|
||||
"minecraft:crimson_planks",
|
||||
"minecraft:dark_oak_planks",
|
||||
"minecraft:jungle_planks",
|
||||
"minecraft:mangrove_planks",
|
||||
"minecraft:oak_planks",
|
||||
"minecraft:pale_oak_planks",
|
||||
"minecraft:spruce_planks",
|
||||
"minecraft:warped_planks"
|
||||
],
|
||||
"minecraft:sand": [
|
||||
"minecraft:red_sand",
|
||||
"minecraft:sand"
|
||||
],
|
||||
"minecraft:sign": [
|
||||
"minecraft:acacia_hanging_sign",
|
||||
"minecraft:acacia_sign",
|
||||
"minecraft:bamboo_hanging_sign",
|
||||
"minecraft:bamboo_sign",
|
||||
"minecraft:birch_hanging_sign",
|
||||
"minecraft:birch_sign",
|
||||
"minecraft:cherry_hanging_sign",
|
||||
"minecraft:cherry_sign",
|
||||
"minecraft:crimson_hanging_sign",
|
||||
"minecraft:crimson_sign",
|
||||
"minecraft:dark_oak_hanging_sign",
|
||||
"minecraft:dark_oak_sign",
|
||||
"minecraft:jungle_hanging_sign",
|
||||
"minecraft:jungle_sign",
|
||||
"minecraft:mangrove_hanging_sign",
|
||||
"minecraft:mangrove_sign",
|
||||
"minecraft:oak_hanging_sign",
|
||||
"minecraft:oak_sign",
|
||||
"minecraft:pale_oak_hanging_sign",
|
||||
"minecraft:pale_oak_sign",
|
||||
"minecraft:spruce_hanging_sign",
|
||||
"minecraft:spruce_sign",
|
||||
"minecraft:warped_hanging_sign",
|
||||
"minecraft:warped_sign"
|
||||
],
|
||||
"minecraft:soul_fire_base_blocks": [
|
||||
"minecraft:soul_sand",
|
||||
"minecraft:soul_soil"
|
||||
],
|
||||
"minecraft:spawn_egg": [
|
||||
"minecraft:allay_spawn_egg",
|
||||
"minecraft:armadillo_spawn_egg",
|
||||
"minecraft:axolotl_spawn_egg",
|
||||
"minecraft:bat_spawn_egg",
|
||||
"minecraft:bee_spawn_egg",
|
||||
"minecraft:blaze_spawn_egg",
|
||||
"minecraft:bogged_spawn_egg",
|
||||
"minecraft:breeze_spawn_egg",
|
||||
"minecraft:camel_spawn_egg",
|
||||
"minecraft:cat_spawn_egg",
|
||||
"minecraft:cave_spider_spawn_egg",
|
||||
"minecraft:chicken_spawn_egg",
|
||||
"minecraft:cod_spawn_egg",
|
||||
"minecraft:cow_spawn_egg",
|
||||
"minecraft:creaking_spawn_egg",
|
||||
"minecraft:creeper_spawn_egg",
|
||||
"minecraft:dolphin_spawn_egg",
|
||||
"minecraft:donkey_spawn_egg",
|
||||
"minecraft:drowned_spawn_egg",
|
||||
"minecraft:elder_guardian_spawn_egg",
|
||||
"minecraft:ender_dragon_spawn_egg",
|
||||
"minecraft:enderman_spawn_egg",
|
||||
"minecraft:endermite_spawn_egg",
|
||||
"minecraft:evoker_spawn_egg",
|
||||
"minecraft:fox_spawn_egg",
|
||||
"minecraft:frog_spawn_egg",
|
||||
"minecraft:ghast_spawn_egg",
|
||||
"minecraft:glow_squid_spawn_egg",
|
||||
"minecraft:goat_spawn_egg",
|
||||
"minecraft:guardian_spawn_egg",
|
||||
"minecraft:hoglin_spawn_egg",
|
||||
"minecraft:horse_spawn_egg",
|
||||
"minecraft:husk_spawn_egg",
|
||||
"minecraft:iron_golem_spawn_egg",
|
||||
"minecraft:llama_spawn_egg",
|
||||
"minecraft:magma_cube_spawn_egg",
|
||||
"minecraft:mooshroom_spawn_egg",
|
||||
"minecraft:mule_spawn_egg",
|
||||
"minecraft:ocelot_spawn_egg",
|
||||
"minecraft:panda_spawn_egg",
|
||||
"minecraft:parrot_spawn_egg",
|
||||
"minecraft:phantom_spawn_egg",
|
||||
"minecraft:pig_spawn_egg",
|
||||
"minecraft:piglin_brute_spawn_egg",
|
||||
"minecraft:piglin_spawn_egg",
|
||||
"minecraft:pillager_spawn_egg",
|
||||
"minecraft:polar_bear_spawn_egg",
|
||||
"minecraft:pufferfish_spawn_egg",
|
||||
"minecraft:rabbit_spawn_egg",
|
||||
"minecraft:ravager_spawn_egg",
|
||||
"minecraft:salmon_spawn_egg",
|
||||
"minecraft:sheep_spawn_egg",
|
||||
"minecraft:shulker_spawn_egg",
|
||||
"minecraft:silverfish_spawn_egg",
|
||||
"minecraft:skeleton_horse_spawn_egg",
|
||||
"minecraft:skeleton_spawn_egg",
|
||||
"minecraft:slime_spawn_egg",
|
||||
"minecraft:sniffer_spawn_egg",
|
||||
"minecraft:snow_golem_spawn_egg",
|
||||
"minecraft:spider_spawn_egg",
|
||||
"minecraft:squid_spawn_egg",
|
||||
"minecraft:stray_spawn_egg",
|
||||
"minecraft:strider_spawn_egg",
|
||||
"minecraft:tadpole_spawn_egg",
|
||||
"minecraft:trader_llama_spawn_egg",
|
||||
"minecraft:tropical_fish_spawn_egg",
|
||||
"minecraft:turtle_spawn_egg",
|
||||
"minecraft:vex_spawn_egg",
|
||||
"minecraft:villager_spawn_egg",
|
||||
"minecraft:vindicator_spawn_egg",
|
||||
"minecraft:wandering_trader_spawn_egg",
|
||||
"minecraft:warden_spawn_egg",
|
||||
"minecraft:witch_spawn_egg",
|
||||
"minecraft:wither_skeleton_spawn_egg",
|
||||
"minecraft:wither_spawn_egg",
|
||||
"minecraft:wolf_spawn_egg",
|
||||
"minecraft:zoglin_spawn_egg",
|
||||
"minecraft:zombie_horse_spawn_egg",
|
||||
"minecraft:zombie_pigman_spawn_egg",
|
||||
"minecraft:zombie_spawn_egg",
|
||||
"minecraft:zombie_villager_spawn_egg"
|
||||
],
|
||||
"minecraft:stone_bricks": [
|
||||
"minecraft:chiseled_stone_bricks",
|
||||
"minecraft:cracked_stone_bricks",
|
||||
"minecraft:mossy_stone_bricks",
|
||||
"minecraft:stone_bricks"
|
||||
],
|
||||
"minecraft:stone_crafting_materials": [
|
||||
"minecraft:blackstone",
|
||||
"minecraft:cobbled_deepslate",
|
||||
"minecraft:cobblestone"
|
||||
],
|
||||
"minecraft:stone_tier": [
|
||||
"minecraft:stone_axe",
|
||||
"minecraft:stone_hoe",
|
||||
"minecraft:stone_pickaxe",
|
||||
"minecraft:stone_shovel",
|
||||
"minecraft:stone_sword"
|
||||
],
|
||||
"minecraft:stone_tool_materials": [
|
||||
"minecraft:blackstone",
|
||||
"minecraft:cobbled_deepslate",
|
||||
"minecraft:cobblestone"
|
||||
],
|
||||
"minecraft:transform_materials": [
|
||||
"minecraft:netherite_ingot"
|
||||
],
|
||||
"minecraft:transform_templates": [
|
||||
"minecraft:netherite_upgrade_smithing_template"
|
||||
],
|
||||
"minecraft:transformable_items": [
|
||||
"minecraft:diamond_axe",
|
||||
"minecraft:diamond_boots",
|
||||
"minecraft:diamond_chestplate",
|
||||
"minecraft:diamond_helmet",
|
||||
"minecraft:diamond_hoe",
|
||||
"minecraft:diamond_leggings",
|
||||
"minecraft:diamond_pickaxe",
|
||||
"minecraft:diamond_shovel",
|
||||
"minecraft:diamond_sword",
|
||||
"minecraft:golden_boots"
|
||||
],
|
||||
"minecraft:trim_materials": [
|
||||
"minecraft:amethyst_shard",
|
||||
"minecraft:copper_ingot",
|
||||
"minecraft:diamond",
|
||||
"minecraft:emerald",
|
||||
"minecraft:gold_ingot",
|
||||
"minecraft:iron_ingot",
|
||||
"minecraft:lapis_lazuli",
|
||||
"minecraft:netherite_ingot",
|
||||
"minecraft:quartz",
|
||||
"minecraft:redstone",
|
||||
"minecraft:resin_brick"
|
||||
],
|
||||
"minecraft:trim_templates": [
|
||||
"minecraft:bolt_armor_trim_smithing_template",
|
||||
"minecraft:coast_armor_trim_smithing_template",
|
||||
"minecraft:dune_armor_trim_smithing_template",
|
||||
"minecraft:eye_armor_trim_smithing_template",
|
||||
"minecraft:flow_armor_trim_smithing_template",
|
||||
"minecraft:host_armor_trim_smithing_template",
|
||||
"minecraft:raiser_armor_trim_smithing_template",
|
||||
"minecraft:rib_armor_trim_smithing_template",
|
||||
"minecraft:sentry_armor_trim_smithing_template",
|
||||
"minecraft:shaper_armor_trim_smithing_template",
|
||||
"minecraft:silence_armor_trim_smithing_template",
|
||||
"minecraft:snout_armor_trim_smithing_template",
|
||||
"minecraft:spire_armor_trim_smithing_template",
|
||||
"minecraft:tide_armor_trim_smithing_template",
|
||||
"minecraft:vex_armor_trim_smithing_template",
|
||||
"minecraft:ward_armor_trim_smithing_template",
|
||||
"minecraft:wayfinder_armor_trim_smithing_template",
|
||||
"minecraft:wild_armor_trim_smithing_template"
|
||||
],
|
||||
"minecraft:trimmable_armors": [
|
||||
"minecraft:chainmail_boots",
|
||||
"minecraft:chainmail_chestplate",
|
||||
"minecraft:chainmail_helmet",
|
||||
"minecraft:chainmail_leggings",
|
||||
"minecraft:diamond_boots",
|
||||
"minecraft:diamond_chestplate",
|
||||
"minecraft:diamond_helmet",
|
||||
"minecraft:diamond_leggings",
|
||||
"minecraft:golden_boots",
|
||||
"minecraft:golden_chestplate",
|
||||
"minecraft:golden_helmet",
|
||||
"minecraft:golden_leggings",
|
||||
"minecraft:iron_boots",
|
||||
"minecraft:iron_chestplate",
|
||||
"minecraft:iron_helmet",
|
||||
"minecraft:iron_leggings",
|
||||
"minecraft:leather_boots",
|
||||
"minecraft:leather_chestplate",
|
||||
"minecraft:leather_helmet",
|
||||
"minecraft:leather_leggings",
|
||||
"minecraft:netherite_boots",
|
||||
"minecraft:netherite_chestplate",
|
||||
"minecraft:netherite_helmet",
|
||||
"minecraft:netherite_leggings",
|
||||
"minecraft:turtle_helmet"
|
||||
],
|
||||
"minecraft:vibration_damper": [
|
||||
"minecraft:black_carpet",
|
||||
"minecraft:black_wool",
|
||||
"minecraft:blue_carpet",
|
||||
"minecraft:blue_wool",
|
||||
"minecraft:brown_carpet",
|
||||
"minecraft:brown_wool",
|
||||
"minecraft:cyan_carpet",
|
||||
"minecraft:cyan_wool",
|
||||
"minecraft:gray_carpet",
|
||||
"minecraft:gray_wool",
|
||||
"minecraft:green_carpet",
|
||||
"minecraft:green_wool",
|
||||
"minecraft:light_blue_carpet",
|
||||
"minecraft:light_blue_wool",
|
||||
"minecraft:light_gray_carpet",
|
||||
"minecraft:light_gray_wool",
|
||||
"minecraft:lime_carpet",
|
||||
"minecraft:lime_wool",
|
||||
"minecraft:magenta_carpet",
|
||||
"minecraft:magenta_wool",
|
||||
"minecraft:orange_carpet",
|
||||
"minecraft:orange_wool",
|
||||
"minecraft:pink_carpet",
|
||||
"minecraft:pink_wool",
|
||||
"minecraft:purple_carpet",
|
||||
"minecraft:purple_wool",
|
||||
"minecraft:red_carpet",
|
||||
"minecraft:red_wool",
|
||||
"minecraft:white_carpet",
|
||||
"minecraft:white_wool",
|
||||
"minecraft:yellow_carpet",
|
||||
"minecraft:yellow_wool"
|
||||
],
|
||||
"minecraft:warped_stems": [
|
||||
"minecraft:stripped_warped_hyphae",
|
||||
"minecraft:stripped_warped_stem",
|
||||
"minecraft:warped_hyphae",
|
||||
"minecraft:warped_stem"
|
||||
],
|
||||
"minecraft:wooden_slabs": [
|
||||
"minecraft:acacia_slab",
|
||||
"minecraft:bamboo_slab",
|
||||
"minecraft:birch_slab",
|
||||
"minecraft:cherry_slab",
|
||||
"minecraft:crimson_slab",
|
||||
"minecraft:dark_oak_slab",
|
||||
"minecraft:jungle_slab",
|
||||
"minecraft:mangrove_slab",
|
||||
"minecraft:oak_slab",
|
||||
"minecraft:pale_oak_slab",
|
||||
"minecraft:spruce_slab",
|
||||
"minecraft:warped_slab"
|
||||
],
|
||||
"minecraft:wooden_tier": [
|
||||
"minecraft:wooden_axe",
|
||||
"minecraft:wooden_hoe",
|
||||
"minecraft:wooden_pickaxe",
|
||||
"minecraft:wooden_shovel",
|
||||
"minecraft:wooden_sword"
|
||||
],
|
||||
"minecraft:wool": [
|
||||
"minecraft:black_wool",
|
||||
"minecraft:blue_wool",
|
||||
"minecraft:brown_wool",
|
||||
"minecraft:cyan_wool",
|
||||
"minecraft:gray_wool",
|
||||
"minecraft:green_wool",
|
||||
"minecraft:light_blue_wool",
|
||||
"minecraft:light_gray_wool",
|
||||
"minecraft:lime_wool",
|
||||
"minecraft:magenta_wool",
|
||||
"minecraft:orange_wool",
|
||||
"minecraft:pink_wool",
|
||||
"minecraft:purple_wool",
|
||||
"minecraft:red_wool",
|
||||
"minecraft:white_wool",
|
||||
"minecraft:yellow_wool"
|
||||
]
|
||||
}
|
10784
core/src/main/resources/bedrock/runtime_item_states.1_21_70.json
Normal file
10784
core/src/main/resources/bedrock/runtime_item_states.1_21_70.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1 +1 @@
|
|||
Subproject commit 362665358ee8baa2abf90f93453fb6801b0ec3ce
|
||||
Subproject commit 7654aabd95a06f02c3827fc212b819fea63d72bb
|
|
@ -62,7 +62,7 @@ public class ResourcePackLoaderTest {
|
|||
public void testPack() throws Exception {
|
||||
// this mcpack only contains a folder, which the manifest is in
|
||||
Path path = getResource("empty_pack.mcpack");
|
||||
ResourcePack pack = ResourcePackLoader.readPack(path);
|
||||
ResourcePack pack = ResourcePackLoader.readPack(path).build();
|
||||
assertEquals("", pack.contentKey());
|
||||
// should probably add some more tests here related to the manifest
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ public class ResourcePackLoaderTest {
|
|||
public void testEncryptedPack() throws Exception {
|
||||
// this zip only contains a contents.json and manifest.json at the root
|
||||
Path path = getResource("encrypted_pack.zip");
|
||||
ResourcePack pack = ResourcePackLoader.readPack(path);
|
||||
ResourcePack pack = ResourcePackLoader.readPack(path).build();
|
||||
assertEquals("JAGcSXcXwcODc1YS70GzeWAUKEO172UA", pack.contentKey());
|
||||
}
|
||||
|
||||
|
|
|
@ -25,14 +25,29 @@
|
|||
|
||||
package org.geysermc.geyser.scoreboard.network;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketMatch;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketType;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.cloudburstmc.protocol.bedrock.data.ScoreInfo;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.RemoveEntityPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetDisplayObjectivePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetScorePacket;
|
||||
import org.geysermc.geyser.entity.type.living.monster.EnderDragonPartEntity;
|
||||
import org.geysermc.geyser.session.cache.EntityCache;
|
||||
import org.geysermc.geyser.translator.protocol.java.entity.JavaRemoveEntitiesTranslator;
|
||||
|
@ -40,7 +55,10 @@ import org.geysermc.geyser.translator.protocol.java.entity.JavaSetEntityDataTran
|
|||
import org.geysermc.geyser.translator.protocol.java.entity.player.JavaPlayerInfoUpdateTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.java.entity.spawn.JavaAddEntityTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.java.entity.spawn.JavaAddExperienceOrbTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetDisplayObjectiveTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetObjectiveTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetPlayerTeamTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetScoreTranslator;
|
||||
import org.geysermc.mcprotocollib.auth.GameProfile;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.PlayerListEntry;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.PlayerListEntryAction;
|
||||
|
@ -53,6 +71,9 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
|||
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.CollisionRule;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ObjectiveAction;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamAction;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundPlayerInfoUpdatePacket;
|
||||
|
@ -60,18 +81,12 @@ import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.Clie
|
|||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundSetEntityDataPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddEntityPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddExperienceOrbPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetDisplayObjectivePacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetObjectivePacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetPlayerTeamPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetScorePacket;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.*;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for issues reported on GitHub.
|
||||
*/
|
||||
|
@ -95,7 +110,7 @@ public class ScoreboardIssueTests {
|
|||
String displayName = context.mockOrSpy(EntityCache.class).getEntityByJavaId(2).getDisplayName();
|
||||
assertEquals("entity.minecraft.experience_orb", displayName);
|
||||
|
||||
context.translate(removeEntitiesTranslator, new ClientboundRemoveEntitiesPacket(new int[] { 2 }));
|
||||
context.translate(removeEntitiesTranslator, new ClientboundRemoveEntitiesPacket(new int[]{2}));
|
||||
});
|
||||
|
||||
// we know that spawning and removing the entity should be fine
|
||||
|
@ -153,7 +168,7 @@ public class ScoreboardIssueTests {
|
|||
playerInfoUpdateTranslator,
|
||||
new ClientboundPlayerInfoUpdatePacket(
|
||||
EnumSet.of(PlayerListEntryAction.ADD_PLAYER, PlayerListEntryAction.UPDATE_LISTED),
|
||||
new PlayerListEntry[] {
|
||||
new PlayerListEntry[]{
|
||||
new PlayerListEntry(npcUuid, new GameProfile(npcUuid, "1297"), false, 0, GameMode.SURVIVAL, null, false, 0, null, 0, null, null)
|
||||
}));
|
||||
|
||||
|
@ -183,7 +198,7 @@ public class ScoreboardIssueTests {
|
|||
);
|
||||
context.translate(
|
||||
setPlayerTeamTranslator,
|
||||
new ClientboundSetPlayerTeamPacket("npc_team_1297", TeamAction.ADD_PLAYER, new String[]{ "1297" }));
|
||||
new ClientboundSetPlayerTeamPacket("npc_team_1297", TeamAction.ADD_PLAYER, new String[]{"1297"}));
|
||||
|
||||
context.translate(addEntityTranslator, new ClientboundAddEntityPacket(1297, npcUuid, EntityType.PLAYER, 1, 2, 3, 4, 5, 6));
|
||||
// then it updates the displayed skin parts, which isn't relevant for us
|
||||
|
@ -245,7 +260,7 @@ public class ScoreboardIssueTests {
|
|||
);
|
||||
context.translate(
|
||||
setPlayerTeamTranslator,
|
||||
new ClientboundSetPlayerTeamPacket("npc_team_1298", TeamAction.ADD_PLAYER, new String[]{ hologramUuid.toString() }));
|
||||
new ClientboundSetPlayerTeamPacket("npc_team_1298", TeamAction.ADD_PLAYER, new String[]{hologramUuid.toString()}));
|
||||
|
||||
assertNextPacket(context, () -> {
|
||||
var packet = new SetEntityDataPacket();
|
||||
|
@ -255,4 +270,76 @@ public class ScoreboardIssueTests {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for <a href="https://github.com/GeyserMC/Geyser/issues/5353">#5353</a>.
|
||||
* It follows a code snippet provided in <a href="https://github.com/GeyserMC/Geyser/pull/5415">the PR description</a>.
|
||||
*/
|
||||
@Test
|
||||
void prefixNotShowing() {
|
||||
mockContextScoreboard(context -> {
|
||||
var setObjectiveTranslator = new JavaSetObjectiveTranslator();
|
||||
var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator();
|
||||
var setPlayerTeamTranslator = new JavaSetPlayerTeamTranslator();
|
||||
var setScoreTranslator = new JavaSetScoreTranslator();
|
||||
|
||||
context.translate(
|
||||
setObjectiveTranslator,
|
||||
new ClientboundSetObjectivePacket(
|
||||
"sb-0",
|
||||
ObjectiveAction.ADD,
|
||||
Component.text("Test Scoreboard"),
|
||||
ScoreType.INTEGER,
|
||||
null
|
||||
)
|
||||
);
|
||||
assertNoNextPacket(context);
|
||||
|
||||
context.translate(
|
||||
setDisplayObjectiveTranslator,
|
||||
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "sb-0")
|
||||
);
|
||||
assertNextPacket(context, () -> {
|
||||
var packet = new SetDisplayObjectivePacket();
|
||||
packet.setObjectiveId("0");
|
||||
packet.setDisplayName("Test Scoreboard");
|
||||
packet.setCriteria("dummy");
|
||||
packet.setDisplaySlot("sidebar");
|
||||
packet.setSortOrder(1);
|
||||
return packet;
|
||||
});
|
||||
|
||||
context.translate(
|
||||
setPlayerTeamTranslator,
|
||||
new ClientboundSetPlayerTeamPacket(
|
||||
"sbt-1",
|
||||
Component.text("displaynametest"),
|
||||
Component.text("§aScore: 10"),
|
||||
Component.empty(),
|
||||
false,
|
||||
false,
|
||||
NameTagVisibility.NEVER,
|
||||
CollisionRule.NEVER,
|
||||
TeamColor.DARK_GREEN,
|
||||
new String[]{"§0"})
|
||||
);
|
||||
assertNoNextPacket(context);
|
||||
|
||||
context.translate(
|
||||
setScoreTranslator,
|
||||
new ClientboundSetScorePacket(
|
||||
"§0",
|
||||
"sb-0",
|
||||
10
|
||||
).withDisplay(Component.empty())
|
||||
);
|
||||
assertNextPacket(context, () -> {
|
||||
var packet = new SetScorePacket();
|
||||
packet.setAction(SetScorePacket.Action.SET);
|
||||
packet.setInfos(List.of(new ScoreInfo(1, "0", 10, "§2§aScore: 10§r§2§r§2")));
|
||||
return packet;
|
||||
});
|
||||
assertNoNextPacket(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,11 @@ public class EmptyGeyserLogger implements GeyserLogger {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Object... arguments) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
|
||||
|
|
|
@ -8,5 +8,5 @@ org.gradle.vfs.watch=false
|
|||
|
||||
group=org.geysermc
|
||||
id=geyser
|
||||
version=2.6.1-SNAPSHOT
|
||||
version=2.6.2-SNAPSHOT
|
||||
description=Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers.
|
||||
|
|
|
@ -4,15 +4,15 @@ cumulus = "1.1.2"
|
|||
erosion = "1.1-20240521.000109-3"
|
||||
events = "1.1-SNAPSHOT"
|
||||
jackson = "2.17.0"
|
||||
fastutil = "8.5.2"
|
||||
fastutil = "8.5.15-SNAPSHOT"
|
||||
netty = "4.1.107.Final"
|
||||
netty-io-uring = "0.0.25.Final-SNAPSHOT"
|
||||
guava = "29.0-jre"
|
||||
gson = "2.3.1" # Provided by Spigot 1.8.8
|
||||
websocket = "1.5.1"
|
||||
protocol-connection = "3.0.0.Beta6-20250212.131009-3"
|
||||
protocol-common = "3.0.0.Beta6-20250212.131009-3"
|
||||
protocol-codec = "3.0.0.Beta6-20250212.131009-3"
|
||||
protocol-connection = "3.0.0.Beta6-20250324.162731-5"
|
||||
protocol-common = "3.0.0.Beta6-20250324.162731-5"
|
||||
protocol-codec = "3.0.0.Beta6-20250324.162731-5"
|
||||
raknet = "1.0.0.CR3-20250218.160705-18"
|
||||
minecraftauth = "4.1.1"
|
||||
mcprotocollib = "1.21.4-20250311.232133-24"
|
||||
|
@ -68,12 +68,13 @@ jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-datab
|
|||
jackson-core = { group = "com.fasterxml.jackson.core", name = "jackson-core", version.ref = "jackson" }
|
||||
jackson-dataformat-yaml = { group = "com.fasterxml.jackson.dataformat", name = "jackson-dataformat-yaml", version.ref = "jackson" }
|
||||
|
||||
fastutil-int-int-maps = { group = "com.nukkitx.fastutil", name = "fastutil-int-int-maps", version.ref = "fastutil" }
|
||||
fastutil-int-long-maps = { group = "com.nukkitx.fastutil", name = "fastutil-int-long-maps", version.ref = "fastutil" }
|
||||
fastutil-int-byte-maps = { group = "com.nukkitx.fastutil", name = "fastutil-int-byte-maps", version.ref = "fastutil" }
|
||||
fastutil-int-boolean-maps = { group = "com.nukkitx.fastutil", name = "fastutil-int-boolean-maps", version.ref = "fastutil" }
|
||||
fastutil-object-int-maps = { group = "com.nukkitx.fastutil", name = "fastutil-object-int-maps", version.ref = "fastutil" }
|
||||
fastutil-object-object-maps = { group = "com.nukkitx.fastutil", name = "fastutil-object-object-maps", version.ref = "fastutil" }
|
||||
fastutil-int-int-maps = { group = "org.cloudburstmc.fastutil.maps", name = "int-int-maps", version.ref = "fastutil" }
|
||||
fastutil-int-long-maps = { group = "org.cloudburstmc.fastutil.maps", name = "int-long-maps", version.ref = "fastutil" }
|
||||
fastutil-int-byte-maps = { group = "org.cloudburstmc.fastutil.maps", name = "int-byte-maps", version.ref = "fastutil" }
|
||||
fastutil-int-boolean-maps = { group = "org.cloudburstmc.fastutil.maps", name = "int-boolean-maps", version.ref = "fastutil" }
|
||||
fastutil-object-int-maps = { group = "org.cloudburstmc.fastutil.maps", name = "object-int-maps", version.ref = "fastutil" }
|
||||
fastutil-object-object-maps = { group = "org.cloudburstmc.fastutil.maps", name = "object-object-maps", version.ref = "fastutil" }
|
||||
fastutil-long-object-maps = { group = "org.cloudburstmc.fastutil.maps", name = "long-object-maps", version.ref = "fastutil" }
|
||||
|
||||
adventure-text-serializer-gson = { group = "net.kyori", name = "adventure-text-serializer-gson", version.ref = "adventure" } # Remove when we remove our Adventure bump
|
||||
adventure-text-serializer-legacy = { group = "net.kyori", name = "adventure-text-serializer-legacy", version.ref = "adventure" }
|
||||
|
@ -160,7 +161,7 @@ runpaper = { id = "xyz.jpenilla.run-paper", version.ref = "runtask" }
|
|||
|
||||
[bundles]
|
||||
jackson = [ "jackson-annotations", "jackson-databind", "jackson-dataformat-yaml" ]
|
||||
fastutil = [ "fastutil-int-int-maps", "fastutil-int-long-maps", "fastutil-int-byte-maps", "fastutil-int-boolean-maps", "fastutil-object-int-maps", "fastutil-object-object-maps" ]
|
||||
fastutil = [ "fastutil-int-int-maps", "fastutil-int-long-maps", "fastutil-long-object-maps", "fastutil-int-byte-maps", "fastutil-int-boolean-maps", "fastutil-object-int-maps", "fastutil-object-object-maps" ]
|
||||
adventure = [ "adventure-text-serializer-gson", "adventure-text-serializer-legacy", "adventure-text-serializer-plain" ]
|
||||
log4j = [ "log4j-api", "log4j-core", "log4j-slf4j2-impl", "log4j-iostreams", "log4j-jul" ]
|
||||
jline = [ "jline-terminal", "jline-terminal-jna", "jline-reader" ]
|
||||
|
|
Loading…
Add table
Reference in a new issue