diff --git a/.editorconfig b/.editorconfig index c32bad0c803..b6b9cda9388 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,3 +12,7 @@ max_line_length = off ij_java_class_count_to_use_import_on_demand = 9999 ij_java_doc_align_exception_comments = false ij_java_doc_align_param_comments = false + +[*.json] +indent_size = 2 +tab_width = 2 diff --git a/README.md b/README.md index 88163bb8375..9c1772bd95b 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Special thanks to the DragonProxy project for being a trailblazer in protocol tr | Edition | Supported Versions | |---------|------------------------------------------------------------------------------------------------------| | Bedrock | 1.21.130 - 1.21.132, 26.0, 26.1, 26.2, 26.3, 26.10, 26.20 | -| Java | 1.21.11 (For older versions, [see this guide](https://geysermc.org/wiki/geyser/supported-versions/)) | +| Java | 26.1 (For older versions, [see this guide](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. diff --git a/ap/src/main/java/org/geysermc/geyser/processor/BlockEntityProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/BlockEntityProcessor.java index ccdaeda4e8c..e08d83a96c2 100644 --- a/ap/src/main/java/org/geysermc/geyser/processor/BlockEntityProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/BlockEntityProcessor.java @@ -30,7 +30,7 @@ import javax.lang.model.SourceVersion; @SupportedAnnotationTypes("*") -@SupportedSourceVersion(SourceVersion.RELEASE_17) +@SupportedSourceVersion(SourceVersion.RELEASE_21) public class BlockEntityProcessor extends ClassProcessor { public BlockEntityProcessor() { super("org.geysermc.geyser.translator.level.block.entity.BlockEntity"); diff --git a/ap/src/main/java/org/geysermc/geyser/processor/CollisionRemapperProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/CollisionRemapperProcessor.java index 30d94b7f57d..cf6fe14d2b6 100644 --- a/ap/src/main/java/org/geysermc/geyser/processor/CollisionRemapperProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/CollisionRemapperProcessor.java @@ -30,7 +30,7 @@ import javax.lang.model.SourceVersion; @SupportedAnnotationTypes("*") -@SupportedSourceVersion(SourceVersion.RELEASE_17) +@SupportedSourceVersion(SourceVersion.RELEASE_21) public class CollisionRemapperProcessor extends ClassProcessor { public CollisionRemapperProcessor() { super("org.geysermc.geyser.translator.collision.CollisionRemapper"); diff --git a/ap/src/main/java/org/geysermc/geyser/processor/PacketTranslatorProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/PacketTranslatorProcessor.java index d538a9ca12b..4f47efd5a11 100644 --- a/ap/src/main/java/org/geysermc/geyser/processor/PacketTranslatorProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/PacketTranslatorProcessor.java @@ -30,7 +30,7 @@ import javax.lang.model.SourceVersion; @SupportedAnnotationTypes("*") -@SupportedSourceVersion(SourceVersion.RELEASE_17) +@SupportedSourceVersion(SourceVersion.RELEASE_21) public class PacketTranslatorProcessor extends ClassProcessor { public PacketTranslatorProcessor() { super("org.geysermc.geyser.translator.protocol.Translator"); diff --git a/ap/src/main/java/org/geysermc/geyser/processor/SoundHandlerProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/SoundHandlerProcessor.java index 30ceef2c66a..a9e4a6329da 100644 --- a/ap/src/main/java/org/geysermc/geyser/processor/SoundHandlerProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/SoundHandlerProcessor.java @@ -30,7 +30,7 @@ import javax.lang.model.SourceVersion; @SupportedAnnotationTypes("*") -@SupportedSourceVersion(SourceVersion.RELEASE_17) +@SupportedSourceVersion(SourceVersion.RELEASE_21) public class SoundHandlerProcessor extends ClassProcessor { public SoundHandlerProcessor() { super("org.geysermc.geyser.translator.sound.SoundTranslator"); diff --git a/api/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/src/main/java/org/geysermc/geyser/api/GeyserApi.java index 45779ca43c0..2aea0e5f0cf 100644 --- a/api/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,8 +25,6 @@ package org.geysermc.geyser.api; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.api.Geyser; import org.geysermc.api.GeyserApiBase; import org.geysermc.api.util.ApiVersion; @@ -40,6 +38,7 @@ import org.geysermc.geyser.api.util.MinecraftVersion; import org.geysermc.geyser.api.util.PlatformType; import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.Nullable; import java.nio.file.Path; import java.util.List; @@ -54,18 +53,17 @@ public interface GeyserApi extends GeyserApiBase { * {@inheritDoc} */ @Override - @Nullable GeyserConnection connectionByUuid(@NonNull UUID uuid); + @Nullable GeyserConnection connectionByUuid(UUID uuid); /** * {@inheritDoc} */ @Override - @Nullable GeyserConnection connectionByXuid(@NonNull String xuid); + @Nullable GeyserConnection connectionByXuid(String xuid); /** * {@inheritDoc} */ - @NonNull List onlineConnections(); /** @@ -73,7 +71,6 @@ public interface GeyserApi extends GeyserApiBase { * * @return the extension manager */ - @NonNull ExtensionManager extensionManager(); /** @@ -85,8 +82,7 @@ public interface GeyserApi extends GeyserApiBase { * @throws IllegalArgumentException if there is no provider for the specified API class * @return the builder instance */ - @NonNull - R provider(@NonNull Class apiClass, @Nullable Object... args); + R provider(Class apiClass, @Nullable Object... args); /** * Gets the {@link EventBus} for handling @@ -94,7 +90,6 @@ public interface GeyserApi extends GeyserApiBase { * * @return the event bus */ - @NonNull EventBus eventBus(); /** @@ -103,7 +98,6 @@ public interface GeyserApi extends GeyserApiBase { * * @return the default remote server used within Geyser */ - @NonNull RemoteServer defaultRemoteServer(); /** @@ -112,7 +106,6 @@ public interface GeyserApi extends GeyserApiBase { * * @return the listener used for Bedrock client connectins */ - @NonNull BedrockListener bedrockListener(); /** @@ -120,7 +113,6 @@ public interface GeyserApi extends GeyserApiBase { * * @return the path to the Geyser config directory */ - @NonNull Path configDirectory(); /** @@ -128,7 +120,6 @@ public interface GeyserApi extends GeyserApiBase { * * @return the path to the Geyser packs directory */ - @NonNull Path packDirectory(); /** @@ -136,7 +127,6 @@ public interface GeyserApi extends GeyserApiBase { * * @return type of platform */ - @NonNull PlatformType platformType(); /** @@ -144,7 +134,6 @@ public interface GeyserApi extends GeyserApiBase { * * @return the supported version of Java Minecraft */ - @NonNull MinecraftVersion supportedJavaVersion(); /** @@ -152,7 +141,6 @@ public interface GeyserApi extends GeyserApiBase { * * @return the list of supported Bedrock Minecraft versions */ - @NonNull List supportedBedrockVersions(); /** @@ -160,7 +148,6 @@ public interface GeyserApi extends GeyserApiBase { * * @return the console command source */ - @NonNull CommandSource consoleCommandSource(); /** @@ -168,7 +155,6 @@ public interface GeyserApi extends GeyserApiBase { * * @return the current geyser api instance */ - @NonNull static GeyserApi api() { return Geyser.api(GeyserApi.class); } diff --git a/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraData.java b/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraData.java index f208879d16d..c041ca67672 100644 --- a/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraData.java +++ b/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,9 +25,8 @@ package org.geysermc.geyser.api.bedrock.camera; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.connection.GeyserConnection; +import org.jspecify.annotations.Nullable; import java.util.Set; import java.util.UUID; @@ -46,7 +45,7 @@ public interface CameraData { * * @param fade the camera fade instruction to send */ - void sendCameraFade(@NonNull CameraFade fade); + void sendCameraFade(CameraFade fade); /** * Sends a camera position instruction to the client. @@ -58,7 +57,7 @@ public interface CameraData { * * @param position the camera position instruction to send */ - void sendCameraPosition(@NonNull CameraPosition position); + void sendCameraPosition(CameraPosition position); /** * Stops all sent camera instructions (fades, movements, and perspective locks). @@ -76,7 +75,7 @@ public interface CameraData { * * @param perspective the {@link CameraPerspective} to force */ - void forceCameraPerspective(@NonNull CameraPerspective perspective); + void forceCameraPerspective(CameraPerspective perspective); /** * Gets the client's current {@link CameraPerspective}, if one is currently forced. @@ -99,7 +98,7 @@ public interface CameraData { * @param duration the time in seconds that the shake will occur for * @param type the type of shake */ - void shakeCamera(float intensity, float duration, @NonNull CameraShake type); + void shakeCamera(float intensity, float duration, CameraShake type); /** * Stops all camera shakes of any type. @@ -125,7 +124,6 @@ public interface CameraData { /** * Returns an immutable copy of all fog affects currently applied to this client. */ - @NonNull Set fogEffects(); /** @@ -137,7 +135,7 @@ public interface CameraData { * @param owner the owner of the lock, represented with a UUID * @return if the camera is locked after this method call */ - boolean lockCamera(boolean lock, @NonNull UUID owner); + boolean lockCamera(boolean lock, UUID owner); /** * Returns whether the client's camera is locked. @@ -151,7 +149,7 @@ public interface CameraData { * * @param element the {@link GuiElement} to hide */ - void hideElement(@NonNull GuiElement... element); + void hideElement(GuiElement... element); /** * Resets a {@link GuiElement} on the client's side. @@ -162,19 +160,19 @@ public interface CameraData { * * @param element the {@link GuiElement} to reset */ - void resetElement(@NonNull GuiElement @Nullable... element); + void resetElement(GuiElement @Nullable... element); /** * Determines whether a {@link GuiElement} is currently hidden. * * @param element the {@link GuiElement} to check */ - boolean isHudElementHidden(@NonNull GuiElement element); + boolean isHudElementHidden(GuiElement element); /** * Returns the currently hidden {@link GuiElement}s. * * @return an unmodifiable view of all currently hidden {@link GuiElement}s */ - @NonNull Set hiddenElements(); + Set hiddenElements(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraFade.java b/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraFade.java index 38baa73bbb5..36cdbb677f4 100644 --- a/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraFade.java +++ b/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraFade.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.bedrock.camera; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.value.qual.IntRange; import org.geysermc.geyser.api.GeyserApi; @@ -44,7 +43,7 @@ public interface CameraFade { * * @return the color of the fade */ - @NonNull Color color(); + Color color(); /** * Gets the seconds it takes to fade in. @@ -81,7 +80,7 @@ static CameraFade.Builder builder() { interface Builder { - Builder color(@NonNull Color color); + Builder color(Color color); Builder fadeInSeconds(@IntRange(from = 0, to = 10) float fadeInSeconds); diff --git a/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraPosition.java b/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraPosition.java index 6d42d499e78..ab499a632fa 100644 --- a/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraPosition.java +++ b/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraPosition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,11 +25,10 @@ package org.geysermc.geyser.api.bedrock.camera; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.common.value.qual.IntRange; import org.cloudburstmc.math.vector.Vector3f; import org.geysermc.geyser.api.GeyserApi; +import org.jspecify.annotations.Nullable; /** * This interface represents a camera position instruction. Can be built with the {@link #builder()}. @@ -47,7 +46,7 @@ public interface CameraPosition { * * @return camera position vector */ - @NonNull Vector3f position(); + Vector3f position(); /** * Gets the {@link CameraEaseType} of the camera. @@ -137,7 +136,7 @@ interface Builder { Builder easeSeconds(float easeSeconds); - Builder position(@NonNull Vector3f position); + Builder position(Vector3f position); Builder rotationX(@IntRange(from = -90, to = 90) int rotationX); diff --git a/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/package-info.java b/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/package-info.java new file mode 100644 index 00000000000..b11bd1f2406 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.bedrock.camera; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/CustomBlockData.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/CustomBlockData.java index 2605cda2164..01a0c0a1366 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/CustomBlockData.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/CustomBlockData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,12 +25,11 @@ package org.geysermc.geyser.api.block.custom; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents; import org.geysermc.geyser.api.block.custom.property.CustomBlockProperty; import org.geysermc.geyser.api.util.CreativeCategory; +import org.jspecify.annotations.Nullable; import java.util.List; import java.util.Map; @@ -44,14 +43,14 @@ public interface CustomBlockData { * * @return The name of the custom block. */ - @NonNull String name(); + String name(); /** * Gets the identifier of the custom block * * @return The identifier of the custom block. */ - @NonNull String identifier(); + String identifier(); /** * Gets if the custom block is included in the creative inventory @@ -87,28 +86,28 @@ public interface CustomBlockData { * * @return The custom block's map of block property names to CustomBlockProperty objects. */ - @NonNull Map> properties(); + Map> properties(); /** * Gets the list of the custom block's permutations * * @return The permutations of the custom block. */ - @NonNull List permutations(); + List permutations(); /** * Gets the custom block's default block state * * @return The default block state of the custom block. */ - @NonNull CustomBlockState defaultBlockState(); + CustomBlockState defaultBlockState(); /** * Gets a builder for a custom block state * * @return The builder for a custom block state. */ - CustomBlockState.@NonNull Builder blockStateBuilder(); + CustomBlockState.Builder blockStateBuilder(); /** * Create a Builder for CustomBlockData @@ -120,7 +119,7 @@ static CustomBlockData.Builder builder() { } interface Builder { - Builder name(@NonNull String name); + Builder name(String name); Builder includedInCreativeInventory(boolean includedInCreativeInventory); @@ -128,15 +127,15 @@ interface Builder { Builder creativeGroup(@Nullable String creativeGroup); - Builder components(@NonNull CustomBlockComponents components); + Builder components(CustomBlockComponents components); - Builder booleanProperty(@NonNull String propertyName); + Builder booleanProperty(String propertyName); - Builder intProperty(@NonNull String propertyName, List values); + Builder intProperty(String propertyName, List values); - Builder stringProperty(@NonNull String propertyName, List values); + Builder stringProperty(String propertyName, List values); - Builder permutations(@NonNull List permutations); + Builder permutations(List permutations); CustomBlockData build(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/CustomBlockPermutation.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/CustomBlockPermutation.java index fca39b533e5..5ade1afe610 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/CustomBlockPermutation.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/CustomBlockPermutation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.block.custom; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents; /** @@ -35,5 +34,5 @@ * @param components The components of the block * @param condition The Molang query that should return true or false */ -public record CustomBlockPermutation(@NonNull CustomBlockComponents components, @NonNull String condition) { +public record CustomBlockPermutation(CustomBlockComponents components, String condition) { } diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/CustomBlockState.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/CustomBlockState.java index 70b3c1e2d23..7287254f105 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/CustomBlockState.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/CustomBlockState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,8 +25,6 @@ package org.geysermc.geyser.api.block.custom; -import org.checkerframework.checker.nullness.qual.NonNull; - import java.util.Map; /** @@ -39,14 +37,14 @@ public interface CustomBlockState { * * @return The custom block data for the state. */ - @NonNull CustomBlockData block(); + CustomBlockData block(); /** * Gets the name of the state * * @return The name of the state. */ - @NonNull String name(); + String name(); /** * Gets the given property for the state @@ -54,21 +52,21 @@ public interface CustomBlockState { * @param propertyName the property name * @return the boolean, int, or string property. */ - @NonNull T property(@NonNull String propertyName); + T property(String propertyName); /** * Gets a map of the properties for the state * * @return The properties for the state. */ - @NonNull Map properties(); + Map properties(); interface Builder { - Builder booleanProperty(@NonNull String propertyName, boolean value); + Builder booleanProperty(String propertyName, boolean value); - Builder intProperty(@NonNull String propertyName, int value); + Builder intProperty(String propertyName, int value); - Builder stringProperty(@NonNull String propertyName, @NonNull String value); + Builder stringProperty(String propertyName, String value); CustomBlockState build(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/NonVanillaCustomBlockData.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/NonVanillaCustomBlockData.java index 4ab186ce621..a91e49fa0ce 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/NonVanillaCustomBlockData.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/NonVanillaCustomBlockData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,11 +25,10 @@ package org.geysermc.geyser.api.block.custom; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents; import org.geysermc.geyser.api.util.CreativeCategory; +import org.jspecify.annotations.Nullable; import java.util.List; @@ -42,7 +41,7 @@ public interface NonVanillaCustomBlockData extends CustomBlockData { * * @return The namespace of the custom block. */ - @NonNull String namespace(); + String namespace(); /** @@ -56,10 +55,10 @@ static NonVanillaCustomBlockData.Builder builder() { interface Builder extends CustomBlockData.Builder { - Builder namespace(@NonNull String namespace); + Builder namespace(String namespace); @Override - Builder name(@NonNull String name); + Builder name(String name); @Override Builder includedInCreativeInventory(boolean includedInCreativeInventory); @@ -71,19 +70,19 @@ interface Builder extends CustomBlockData.Builder { Builder creativeGroup(@Nullable String creativeGroup); @Override - Builder components(@NonNull CustomBlockComponents components); + Builder components(CustomBlockComponents components); @Override - Builder booleanProperty(@NonNull String propertyName); + Builder booleanProperty(String propertyName); @Override - Builder intProperty(@NonNull String propertyName, List values); + Builder intProperty(String propertyName, List values); @Override - Builder stringProperty(@NonNull String propertyName, List values); + Builder stringProperty(String propertyName, List values); @Override - Builder permutations(@NonNull List permutations); + Builder permutations(List permutations); @Override NonVanillaCustomBlockData build(); diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/CustomBlockComponents.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/CustomBlockComponents.java index 5c977c11d6a..7eabe8458f1 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/CustomBlockComponents.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/CustomBlockComponents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,10 +25,9 @@ package org.geysermc.geyser.api.block.custom.component; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; +import org.jspecify.annotations.Nullable; import java.util.Collection; import java.util.List; @@ -69,7 +68,7 @@ public interface CustomBlockComponents { * @return the collision boxes * @since 2.9.5 */ - @NonNull Set collisionBoxes(); + Set collisionBoxes(); /** * Gets the display name component. @@ -96,7 +95,7 @@ public interface CustomBlockComponents { * @return the material instances * @since 2.2.0 */ - @NonNull Map materialInstances(); + Map materialInstances(); /** * Gets the placement filter component @@ -179,7 +178,7 @@ public interface CustomBlockComponents { * @return the set of tags * @since 2.2.0 */ - @NonNull Set tags(); + Set tags(); /** * Create a Builder for CustomBlockComponents @@ -222,7 +221,7 @@ interface Builder { * @return this builder * @since 2.9.5 */ - @This Builder collisionBoxes(@Nullable BoxComponent... collisionBoxes); + @This Builder collisionBoxes(@Nullable BoxComponent @Nullable ... collisionBoxes); /** * Convenience method to set collision boxes for the block. Can be null to disable collisions. @@ -263,7 +262,7 @@ interface Builder { * @return this builder * @since 2.2.0 */ - @This Builder materialInstance(@NonNull String name, @NonNull MaterialInstance materialInstance); + @This Builder materialInstance(String name, MaterialInstance materialInstance); /** * Sets the placement filter of the block. diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/GeometryComponent.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/GeometryComponent.java index 6d85989a7f6..7123bb89002 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/GeometryComponent.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/GeometryComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,9 +25,8 @@ package org.geysermc.geyser.api.block.custom.component; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.GeyserApi; +import org.jspecify.annotations.Nullable; import java.util.Map; @@ -41,7 +40,7 @@ public interface GeometryComponent { * * @return The identifier of the geometry. */ - @NonNull String identifier(); + String identifier(); /** * Gets the bone visibility of the geometry @@ -60,7 +59,7 @@ static GeometryComponent.Builder builder() { } interface Builder { - Builder identifier(@NonNull String identifier); + Builder identifier(String identifier); Builder boneVisibility(@Nullable Map boneVisibility); diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/MaterialInstance.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/MaterialInstance.java index a7e86d89bb7..14f0dd1f7a8 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/MaterialInstance.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/MaterialInstance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2025 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,8 +25,8 @@ package org.geysermc.geyser.api.block.custom.component; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.GeyserApi; +import org.jspecify.annotations.Nullable; /** * This class is used to store data for a material instance. diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/PlacementConditions.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/PlacementConditions.java index e274fd8bd60..e6174443812 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/PlacementConditions.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/PlacementConditions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -21,12 +21,10 @@ * * @author GeyserMC * @link https://github.com/GeyserMC/Geyser -*/ + */ package org.geysermc.geyser.api.block.custom.component; -import org.checkerframework.checker.nullness.qual.NonNull; - import java.util.LinkedHashMap; import java.util.Set; @@ -36,7 +34,7 @@ * @param allowedFaces The faces that the block can be placed on * @param blockFilters The block filters that control what blocks the block can be placed on */ -public record PlacementConditions(@NonNull Set allowedFaces, @NonNull LinkedHashMap blockFilters) { +public record PlacementConditions(Set allowedFaces, LinkedHashMap blockFilters) { public enum Face { DOWN, UP, @@ -50,4 +48,4 @@ public enum BlockFilterType { BLOCK, TAG } -} \ No newline at end of file +} diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/package-info.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/package-info.java new file mode 100644 index 00000000000..2251409433d --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.block.custom.component; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/nonvanilla/JavaBlockState.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/nonvanilla/JavaBlockState.java index c1950f96549..673e6412af7 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/nonvanilla/JavaBlockState.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/nonvanilla/JavaBlockState.java @@ -1,9 +1,33 @@ +/* + * Copyright (c) 2023-2026 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.block.custom.nonvanilla; import org.checkerframework.checker.index.qual.NonNegative; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.GeyserApi; +import org.jspecify.annotations.Nullable; public interface JavaBlockState { /** @@ -11,7 +35,7 @@ public interface JavaBlockState { * * @return the identifier of the block state */ - @NonNull String identifier(); + String identifier(); /** * Gets the Java ID of the block state @@ -46,7 +70,7 @@ public interface JavaBlockState { * * @return the collision of the block state */ - @NonNull JavaBoundingBox[] collision(); + JavaBoundingBox[] collision(); /** * Gets whether the block state can be broken with hand @@ -57,7 +81,7 @@ public interface JavaBlockState { /** * Gets the pick item of the block state - * + * * @return the pick item of the block state * @deprecated the pick item is sent by the Java server */ @@ -91,7 +115,7 @@ static JavaBlockState.Builder builder() { } interface Builder { - Builder identifier(@NonNull String identifier); + Builder identifier(String identifier); Builder javaId(@NonNegative int javaId); @@ -101,7 +125,7 @@ interface Builder { Builder waterlogged(boolean waterlogged); - Builder collision(@NonNull JavaBoundingBox[] collision); + Builder collision(JavaBoundingBox[] collision); Builder canBreakWithHand(boolean canBreakWithHand); diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/nonvanilla/package-info.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/nonvanilla/package-info.java new file mode 100644 index 00000000000..1957f813228 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/nonvanilla/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.block.custom.nonvanilla; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/package-info.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/package-info.java new file mode 100644 index 00000000000..5bb13896fbc --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.block.custom; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/property/CustomBlockProperty.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/property/CustomBlockProperty.java index fc39c4663fa..66da0393b57 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/property/CustomBlockProperty.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/property/CustomBlockProperty.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,8 +25,6 @@ package org.geysermc.geyser.api.block.custom.property; -import org.checkerframework.checker.nullness.qual.NonNull; - import java.util.List; /** @@ -38,19 +36,19 @@ public interface CustomBlockProperty { * * @return The name of the property. */ - @NonNull String name(); + String name(); /** * Gets the values of the property * * @return The values of the property. */ - @NonNull List values(); + List values(); /** * Gets the type of the property * * @return The type of the property. */ - @NonNull PropertyType type(); + PropertyType type(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/property/PropertyType.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/property/PropertyType.java index 48f25c36c90..0ea1b70ff01 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/property/PropertyType.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/property/PropertyType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,8 +25,6 @@ package org.geysermc.geyser.api.block.custom.property; -import org.checkerframework.checker.nullness.qual.NonNull; - /** * This class is used to define a custom block property's type. */ @@ -40,7 +38,7 @@ public class PropertyType { * * @return The property type for a boolean. */ - @NonNull public static PropertyType booleanProp() { + public static PropertyType booleanProp() { return BOOLEAN; } @@ -49,7 +47,7 @@ public class PropertyType { * * @return The property type for an integer. */ - @NonNull public static PropertyType integerProp() { + public static PropertyType integerProp() { return INTEGER; } @@ -58,7 +56,7 @@ public class PropertyType { * * @return The property type for a string. */ - @NonNull public static PropertyType stringProp() { + public static PropertyType stringProp() { return STRING; } @@ -69,7 +67,7 @@ public class PropertyType { * * @return The class of the property type. */ - @NonNull public Class typeClass() { + public Class typeClass() { return typeClass; } diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/property/package-info.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/property/package-info.java new file mode 100644 index 00000000000..d16467358fc --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/property/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.block.custom.property; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/command/Command.java b/api/src/main/java/org/geysermc/geyser/api/command/Command.java index 29922ae1e3a..98338fe9c34 100644 --- a/api/src/main/java/org/geysermc/geyser/api/command/Command.java +++ b/api/src/main/java/org/geysermc/geyser/api/command/Command.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.command; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.lifecycle.GeyserRegisterPermissionsEvent; @@ -45,7 +44,6 @@ public interface Command { * * @return the command name */ - @NonNull String name(); /** @@ -53,7 +51,6 @@ public interface Command { * * @return the command description */ - @NonNull String description(); /** @@ -62,7 +59,6 @@ public interface Command { * * @return the permission node for this command if defined, otherwise an empty string */ - @NonNull String permission(); /** @@ -70,7 +66,6 @@ public interface Command { * * @return the aliases for this command as an unmodifiable list */ - @NonNull List aliases(); /** @@ -107,7 +102,6 @@ default boolean isExecutableOnConsole() { * @deprecated this method will always return an empty immutable list */ @Deprecated(forRemoval = true) - @NonNull default List subCommands() { return Collections.emptyList(); } @@ -119,7 +113,7 @@ default List subCommands() { * @param the source type * @return a new command builder used to construct commands */ - static Command.Builder builder(@NonNull Extension extension) { + static Command.Builder builder(Extension extension) { return GeyserApi.api().provider(Builder.class, extension); } @@ -136,7 +130,7 @@ interface Builder { * @param sourceType the source type * @return this builder */ - Builder source(@NonNull Class sourceType); + Builder source(Class sourceType); /** * Sets the command name. @@ -144,7 +138,7 @@ interface Builder { * @param name the command name * @return this builder */ - Builder name(@NonNull String name); + Builder name(String name); /** * Sets the command description. @@ -152,7 +146,7 @@ interface Builder { * @param description the command description * @return this builder */ - Builder description(@NonNull String description); + Builder description(String description); /** * Sets the permission node required to run this command.
@@ -162,7 +156,7 @@ interface Builder { * @param permission the permission node * @return this builder */ - Builder permission(@NonNull String permission); + Builder permission(String permission); /** * Sets the permission node and its default value. The usage of the default value is platform dependant @@ -177,7 +171,7 @@ interface Builder { * @deprecated this method is experimental and may be removed in the future */ @Deprecated - Builder permission(@NonNull String permission, @NonNull TriState defaultValue); + Builder permission(String permission, TriState defaultValue); /** * Sets the aliases. @@ -185,7 +179,7 @@ interface Builder { * @param aliases the aliases * @return this builder */ - Builder aliases(@NonNull List aliases); + Builder aliases(List aliases); /** * Sets if this command is designed to be used only by server operators. @@ -231,7 +225,7 @@ interface Builder { * @deprecated this method has no effect */ @Deprecated(forRemoval = true) - default Builder subCommands(@NonNull List subCommands) { + default Builder subCommands(List subCommands) { return this; } @@ -241,14 +235,13 @@ default Builder subCommands(@NonNull List subCommands) { * @param executor the command executor * @return this builder */ - Builder executor(@NonNull CommandExecutor executor); + Builder executor(CommandExecutor executor); /** * Builds the command. * * @return a new command from this builder */ - @NonNull Command build(); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java b/api/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java index 12a54ee9082..7f12a57c7c3 100644 --- a/api/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java +++ b/api/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,8 +25,6 @@ package org.geysermc.geyser.api.command; -import org.checkerframework.checker.nullness.qual.NonNull; - /** * Handles executing a command. * @@ -41,5 +39,5 @@ public interface CommandExecutor { * @param command the command * @param args the arguments */ - void execute(@NonNull T source, @NonNull Command command, @NonNull String[] args); + void execute(T source, Command command, String[] args); } diff --git a/api/src/main/java/org/geysermc/geyser/api/command/CommandSource.java b/api/src/main/java/org/geysermc/geyser/api/command/CommandSource.java index c1453f5798d..98968bc68e8 100644 --- a/api/src/main/java/org/geysermc/geyser/api/command/CommandSource.java +++ b/api/src/main/java/org/geysermc/geyser/api/command/CommandSource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,9 +25,8 @@ package org.geysermc.geyser.api.command; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.connection.GeyserConnection; +import org.jspecify.annotations.Nullable; import java.util.UUID; @@ -48,7 +47,7 @@ public interface CommandSource { * * @param message the message to send */ - void sendMessage(@NonNull String message); + void sendMessage(String message); /** * Sends the given messages to the command source diff --git a/api/src/main/java/org/geysermc/geyser/api/command/package-info.java b/api/src/main/java/org/geysermc/geyser/api/command/package-info.java new file mode 100644 index 00000000000..3d0cb33a328 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/command/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.command; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java b/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java index 9371905bc3e..389b721a436 100644 --- a/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java +++ b/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -27,8 +27,6 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.Positive; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.api.connection.Connection; import org.geysermc.geyser.api.bedrock.camera.CameraData; import org.geysermc.geyser.api.bedrock.camera.CameraShake; @@ -37,6 +35,7 @@ import org.geysermc.geyser.api.entity.type.GeyserEntity; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.api.skin.SkinData; +import org.jspecify.annotations.Nullable; import java.util.NoSuchElementException; import java.util.Set; @@ -54,7 +53,7 @@ public interface GeyserConnection extends Connection, CommandSource { * * @return the CameraData for this connection. */ - @NonNull CameraData camera(); + CameraData camera(); /** * Exposes the {@link EntityData} for this connection. @@ -62,7 +61,7 @@ public interface GeyserConnection extends Connection, CommandSource { * * @return the EntityData for this connection. */ - @NonNull EntityData entities(); + EntityData entities(); /** * Returns the current ping of the connection. @@ -135,7 +134,6 @@ public interface GeyserConnection extends Connection, CommandSource { * @return the ip address or hostname string the player used to join * @since 2.8.3 */ - @NonNull String joinAddress(); /** @@ -164,7 +162,7 @@ public interface GeyserConnection extends Connection, CommandSource { * @param skinData the skin data to apply * @since 2.8.3 */ - void sendSkin(@NonNull UUID player, @NonNull SkinData skinData); + void sendSkin(UUID player, SkinData skinData); /** * @param javaId the Java entity ID to look up. @@ -172,7 +170,6 @@ public interface GeyserConnection extends Connection, CommandSource { * @deprecated Use {@link EntityData#entityByJavaId(int)} instead */ @Deprecated - @NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId); /** @@ -181,7 +178,7 @@ public interface GeyserConnection extends Connection, CommandSource { * @param emoter the player entity emoting. * @param emoteId the emote ID to send to this client. */ - void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId); + void showEmote(GeyserPlayerEntity emoter, String emoteId); /** * Shakes the client's camera. @@ -198,7 +195,7 @@ public interface GeyserConnection extends Connection, CommandSource { * @deprecated Use {@link CameraData#shakeCamera(float, float, CameraShake)} instead. */ @Deprecated - void shakeCamera(float intensity, float duration, @NonNull CameraShake type); + void shakeCamera(float intensity, float duration, CameraShake type); /** * Stops all camera shake of any type. @@ -234,7 +231,6 @@ public interface GeyserConnection extends Connection, CommandSource { * @deprecated Use {@link CameraData#fogEffects()} instead. */ @Deprecated - @NonNull Set fogEffects(); /** @@ -243,7 +239,7 @@ public interface GeyserConnection extends Connection, CommandSource { * @return the {@link GeyserPlayerEntity} for this connection * @since 2.9.3 */ - @NonNull GeyserPlayerEntity playerEntity(); + GeyserPlayerEntity playerEntity(); /** * Requests an offhand swap from the Java server. @@ -258,5 +254,5 @@ public interface GeyserConnection extends Connection, CommandSource { * * @since 2.9.4 */ - @NonNull String playFabId(); + String playFabId(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/connection/package-info.java b/api/src/main/java/org/geysermc/geyser/api/connection/package-info.java new file mode 100644 index 00000000000..d0c236430c1 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/connection/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.connection; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/entity/EntityData.java b/api/src/main/java/org/geysermc/geyser/api/entity/EntityData.java index 25d91b2f207..44c0254e0ef 100644 --- a/api/src/main/java/org/geysermc/geyser/api/entity/EntityData.java +++ b/api/src/main/java/org/geysermc/geyser/api/entity/EntityData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -26,11 +26,10 @@ package org.geysermc.geyser.api.entity; import org.checkerframework.checker.index.qual.NonNegative; -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.entity.type.GeyserEntity; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; +import org.jspecify.annotations.Nullable; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -47,7 +46,7 @@ public interface EntityData { * @param javaId the Java entity ID to look up * @return a {@link GeyserEntity} if present in this connection's entity tracker */ - @NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId); + CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId); /** * (Un)locks the client's movement inputs, so that they cannot move. @@ -58,7 +57,7 @@ public interface EntityData { * @param owner the owner of the lock * @return if the movement is locked after this method call */ - boolean lockMovement(boolean lock, @NonNull UUID owner); + boolean lockMovement(boolean lock, UUID owner); /** * Returns whether the client's movement is currently locked. @@ -77,11 +76,11 @@ public interface EntityData { * @deprecated Use {@link GeyserConnection#showEmote(GeyserPlayerEntity, String)} instead. */ @Deprecated(since = "2.9.3") - void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId); + void showEmote(GeyserPlayerEntity emoter, String emoteId); /** * @deprecated Use {@link GeyserConnection#playerEntity} instead. */ @Deprecated(since = "2.9.3") - @NonNull GeyserPlayerEntity playerEntity(); + GeyserPlayerEntity playerEntity(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/entity/package-info.java b/api/src/main/java/org/geysermc/geyser/api/entity/package-info.java new file mode 100644 index 00000000000..b7034bbdf17 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/entity/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.entity; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/entity/property/BatchPropertyUpdater.java b/api/src/main/java/org/geysermc/geyser/api/entity/property/BatchPropertyUpdater.java index 308d23cd1eb..49c103825e9 100644 --- a/api/src/main/java/org/geysermc/geyser/api/entity/property/BatchPropertyUpdater.java +++ b/api/src/main/java/org/geysermc/geyser/api/entity/property/BatchPropertyUpdater.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,9 +25,8 @@ package org.geysermc.geyser.api.entity.property; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.event.lifecycle.GeyserDefineEntityPropertiesEvent; +import org.jspecify.annotations.Nullable; /** * Collects property changes to be applied as a single, batched update to an entity. @@ -65,5 +64,5 @@ public interface BatchPropertyUpdater { * * @since 2.9.0 */ - void update(@NonNull GeyserEntityProperty property, @Nullable T value); + void update(GeyserEntityProperty property, @Nullable T value); } diff --git a/api/src/main/java/org/geysermc/geyser/api/entity/property/GeyserEntityProperty.java b/api/src/main/java/org/geysermc/geyser/api/entity/property/GeyserEntityProperty.java index 9489a9946dc..b4a52654770 100644 --- a/api/src/main/java/org/geysermc/geyser/api/entity/property/GeyserEntityProperty.java +++ b/api/src/main/java/org/geysermc/geyser/api/entity/property/GeyserEntityProperty.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.entity.property; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.util.Identifier; /** @@ -50,7 +49,6 @@ public interface GeyserEntityProperty { * @return the property identifier * @since 2.9.0 */ - @NonNull Identifier identifier(); /** @@ -60,6 +58,5 @@ public interface GeyserEntityProperty { * @return the default value of this property * @since 2.9.0 */ - @NonNull T defaultValue(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/entity/property/package-info.java b/api/src/main/java/org/geysermc/geyser/api/entity/property/package-info.java new file mode 100644 index 00000000000..df06d0848c0 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/entity/property/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.entity.property; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/entity/property/type/package-info.java b/api/src/main/java/org/geysermc/geyser/api/entity/property/type/package-info.java new file mode 100644 index 00000000000..b44bba254e1 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/entity/property/type/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.entity.property.type; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/entity/type/GeyserEntity.java b/api/src/main/java/org/geysermc/geyser/api/entity/type/GeyserEntity.java index 6dd3c4fe2a5..922c87491a8 100644 --- a/api/src/main/java/org/geysermc/geyser/api/entity/type/GeyserEntity.java +++ b/api/src/main/java/org/geysermc/geyser/api/entity/type/GeyserEntity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -26,12 +26,11 @@ package org.geysermc.geyser.api.entity.type; import org.checkerframework.checker.index.qual.NonNegative; -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.entity.property.BatchPropertyUpdater; import org.geysermc.geyser.api.entity.property.GeyserEntityProperty; import org.geysermc.geyser.api.event.lifecycle.GeyserDefineEntityPropertiesEvent; +import org.jspecify.annotations.Nullable; import java.util.function.Consumer; @@ -55,7 +54,7 @@ public interface GeyserEntity { * @param the type of the value * @since 2.9.0 */ - default void updateProperty(@NonNull GeyserEntityProperty property, @Nullable T value) { + default void updateProperty(GeyserEntityProperty property, @Nullable T value) { this.updatePropertiesBatched(consumer -> consumer.update(property, value)); } diff --git a/api/src/main/java/org/geysermc/geyser/api/entity/type/package-info.java b/api/src/main/java/org/geysermc/geyser/api/entity/type/package-info.java new file mode 100644 index 00000000000..beb6e216717 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/entity/type/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.entity.type; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/entity/type/player/package-info.java b/api/src/main/java/org/geysermc/geyser/api/entity/type/player/package-info.java new file mode 100644 index 00000000000..a43e2b23b1d --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/entity/type/player/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.entity.type.player; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/event/EventBus.java b/api/src/main/java/org/geysermc/geyser/api/event/EventBus.java index 3344e38f41d..64757b911e1 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/EventBus.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/EventBus.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.event.bus.OwnedEventBus; @@ -37,6 +36,5 @@ */ public interface EventBus extends OwnedEventBus> { @Override - @NonNull - Set> subscribers(@NonNull Class eventClass); + Set> subscribers(Class eventClass); } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java b/api/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java index 064dd55f60f..b7e73d9883b 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.GeyserApi; /** @@ -40,8 +39,7 @@ public interface EventRegistrar { * @param object the object to wrap around * @return an event registrar instance */ - @NonNull - static EventRegistrar of(@NonNull Object object) { + static EventRegistrar of(Object object) { return GeyserApi.api().provider(EventRegistrar.class, object); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java b/api/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java index a58d3589173..e1b21af7ac8 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.extension.Extension; @@ -37,5 +36,5 @@ */ public interface ExtensionEventBus extends org.geysermc.event.bus.EventBus> { @Override - @NonNull Set> subscribers(@NonNull Class eventClass); + Set> subscribers(Class eventClass); } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/ClientEmoteEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/ClientEmoteEvent.java index 8f969e4d587..f27018f4308 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/ClientEmoteEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/ClientEmoteEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.bedrock; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Cancellable; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.connection.ConnectionEvent; @@ -42,7 +41,7 @@ public final class ClientEmoteEvent extends ConnectionEvent implements Cancellab private boolean cancelled; @ApiStatus.Internal - public ClientEmoteEvent(@NonNull GeyserConnection connection, @NonNull String emoteId) { + public ClientEmoteEvent(GeyserConnection connection, String emoteId) { super(connection); this.emoteId = emoteId; } @@ -54,7 +53,6 @@ public ClientEmoteEvent(@NonNull GeyserConnection connection, @NonNull String em * @return the emote ID requested by the player * @since 2.1.0 */ - @NonNull public String emoteId() { return emoteId; } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionAcceptCodeOfConductEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionAcceptCodeOfConductEvent.java index 0df79765e09..b4583632fca 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionAcceptCodeOfConductEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionAcceptCodeOfConductEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.bedrock; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.connection.ConnectionEvent; import org.geysermc.geyser.api.event.java.ServerCodeOfConductEvent; @@ -44,7 +43,7 @@ public class SessionAcceptCodeOfConductEvent extends ConnectionEvent { private final String codeOfConduct; private boolean skipSaving = false; - public SessionAcceptCodeOfConductEvent(@NonNull GeyserConnection connection, String codeOfConduct) { + public SessionAcceptCodeOfConductEvent(GeyserConnection connection, String codeOfConduct) { super(connection); this.codeOfConduct = codeOfConduct; } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionDisconnectEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionDisconnectEvent.java index f97f32f92d7..0d85eae244d 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionDisconnectEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionDisconnectEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.bedrock; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.connection.ConnectionEvent; @@ -35,7 +34,7 @@ public class SessionDisconnectEvent extends ConnectionEvent { private String disconnectReason; - public SessionDisconnectEvent(@NonNull GeyserConnection connection, @NonNull String reason) { + public SessionDisconnectEvent(GeyserConnection connection, String reason) { super(connection); this.disconnectReason = reason; } @@ -45,7 +44,7 @@ public SessionDisconnectEvent(@NonNull GeyserConnection connection, @NonNull Str * * @return the reason for the disconnect */ - public @NonNull String disconnectReason() { + public String disconnectReason() { return disconnectReason; } @@ -54,7 +53,7 @@ public SessionDisconnectEvent(@NonNull GeyserConnection connection, @NonNull Str * * @param disconnectReason the reason for the disconnect */ - public void disconnectReason(@NonNull String disconnectReason) { + public void disconnectReason(String disconnectReason) { this.disconnectReason = disconnectReason; } } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionInitializeEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionInitializeEvent.java index 91cdea99a2d..85e49f5a362 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionInitializeEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionInitializeEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.bedrock; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.connection.ConnectionEvent; @@ -33,7 +32,7 @@ * Called when Geyser initialises a session for a new bedrock client. */ public final class SessionInitializeEvent extends ConnectionEvent { - public SessionInitializeEvent(@NonNull GeyserConnection connection) { + public SessionInitializeEvent(GeyserConnection connection) { super(connection); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionJoinEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionJoinEvent.java index 0228214a7e2..478093f0d54 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionJoinEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionJoinEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.bedrock; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.connection.ConnectionEvent; @@ -34,7 +33,7 @@ * @since 2.1.1 */ public final class SessionJoinEvent extends ConnectionEvent { - public SessionJoinEvent(@NonNull GeyserConnection connection) { + public SessionJoinEvent(GeyserConnection connection) { super(connection); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoadResourcePacksEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoadResourcePacksEvent.java index 9ea0a04a3ef..a955e30219e 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoadResourcePacksEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoadResourcePacksEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,13 +25,12 @@ 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 org.jspecify.annotations.Nullable; import java.util.Collection; import java.util.List; @@ -42,7 +41,7 @@ * @since 2.1.1 */ public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent { - public SessionLoadResourcePacksEvent(@NonNull GeyserConnection connection) { + public SessionLoadResourcePacksEvent(GeyserConnection connection) { super(connection); } @@ -54,13 +53,13 @@ public SessionLoadResourcePacksEvent(@NonNull GeyserConnection connection) { * @return an unmodifiable list of {@link ResourcePack}'s * @since 2.1.1 */ - public abstract @NonNull List resourcePacks(); + public abstract List resourcePacks(); /** * @deprecated Use {{@link #register(ResourcePack, ResourcePackOption[])}} instead */ @Deprecated - public abstract boolean register(@NonNull ResourcePack pack); + public abstract boolean register(ResourcePack pack); /** * Registers a {@link ResourcePack} to be sent to the client, optionally alongside @@ -71,7 +70,7 @@ public SessionLoadResourcePacksEvent(@NonNull GeyserConnection connection) { * @throws ResourcePackException if an issue occurred during pack registration * @since 2.6.2 */ - public abstract void register(@NonNull ResourcePack pack, @Nullable ResourcePackOption... options); + public abstract void register(ResourcePack pack, @Nullable ResourcePackOption... options); /** * Sets {@link ResourcePackOption}'s for a {@link ResourcePack}. @@ -83,7 +82,7 @@ public SessionLoadResourcePacksEvent(@NonNull GeyserConnection connection) { * @throws ResourcePackException if an issue occurred during {@link ResourcePackOption} registration * @since 2.6.2 */ - public abstract void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption... options); + public abstract void registerOptions(UUID uuid, ResourcePackOption... options); /** * Returns a collection of {@link ResourcePackOption}'s for a registered {@link ResourcePack}. @@ -94,7 +93,7 @@ public SessionLoadResourcePacksEvent(@NonNull GeyserConnection connection) { * @throws ResourcePackException if the pack was not registered * @since 2.6.2 */ - public abstract Collection> options(@NonNull UUID uuid); + public abstract Collection> options(UUID uuid); /** * Returns the current {@link ResourcePackOption}, or null, for a given {@link ResourcePackOption.Type}. @@ -104,7 +103,7 @@ public SessionLoadResourcePacksEvent(@NonNull GeyserConnection connection) { * @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); + public abstract @Nullable ResourcePackOption option(UUID uuid, ResourcePackOption.Type type); /** * Unregisters a {@link ResourcePack} from the list of packs sent to this {@link GeyserConnection}. @@ -112,7 +111,7 @@ public SessionLoadResourcePacksEvent(@NonNull GeyserConnection connection) { * @param uuid the UUID of the {@link ResourcePack} to be removed * @since 2.1.1 */ - public abstract boolean unregister(@NonNull UUID uuid); + public abstract boolean unregister(UUID uuid); /** * Whether to forcefully disable vibrant visuals for joining clients. diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoginEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoginEvent.java index 9200c21f338..d772a978995 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoginEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoginEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,13 +25,12 @@ package org.geysermc.geyser.api.event.bedrock; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.event.Cancellable; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.connection.ConnectionEvent; import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.api.util.PlatformType; +import org.jspecify.annotations.Nullable; import java.util.Map; import java.util.Objects; @@ -43,13 +42,13 @@ public final class SessionLoginEvent extends ConnectionEvent implements Cancellable { private RemoteServer remoteServer; private boolean cancelled; - private String disconnectReason; + private @Nullable String disconnectReason; private Map cookies; private boolean transferring; - public SessionLoginEvent(@NonNull GeyserConnection connection, - @NonNull RemoteServer remoteServer, - @NonNull Map cookies) { + public SessionLoginEvent(GeyserConnection connection, + RemoteServer remoteServer, + Map cookies) { super(connection); this.remoteServer = remoteServer; this.cookies = cookies; @@ -85,7 +84,7 @@ public void setCancelled(boolean cancelled) { * @param cancelled If the login event should be cancelled. * @param disconnectReason The reason for the cancellation. */ - public void setCancelled(boolean cancelled, @NonNull String disconnectReason) { + public void setCancelled(boolean cancelled, String disconnectReason) { this.cancelled = cancelled; this.disconnectReason = disconnectReason; } @@ -104,7 +103,7 @@ public void setCancelled(boolean cancelled, @NonNull String disconnectReason) { * * @return the {@link RemoteServer} the session will attempt to connect to. */ - public @NonNull RemoteServer remoteServer() { + public RemoteServer remoteServer() { return this.remoteServer; } @@ -115,7 +114,7 @@ public void setCancelled(boolean cancelled, @NonNull String disconnectReason) { * * @param remoteServer Sets the {@link RemoteServer} to connect to. */ - public void remoteServer(@NonNull RemoteServer remoteServer) { + public void remoteServer(RemoteServer remoteServer) { this.remoteServer = remoteServer; } @@ -123,7 +122,7 @@ public void remoteServer(@NonNull RemoteServer remoteServer) { * Sets a map of cookies from a possible previous session. The Java server can send and request these * to store information on the client across server transfers. */ - public void cookies(@NonNull Map cookies) { + public void cookies(Map cookies) { Objects.requireNonNull(cookies); this.cookies = cookies; } @@ -132,7 +131,7 @@ public void cookies(@NonNull Map cookies) { * Gets a map of the sessions cookies, if set. * @return the connections cookies */ - public @NonNull Map cookies() { + public Map cookies() { return cookies; } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionSkinApplyEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionSkinApplyEvent.java index f22241e41c3..4903dea4712 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionSkinApplyEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionSkinApplyEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2024 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.bedrock; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.connection.ConnectionEvent; import org.geysermc.geyser.api.skin.Cape; @@ -48,7 +47,7 @@ public abstract class SessionSkinApplyEvent extends ConnectionEvent { private final boolean bedrock; private final SkinData originalSkinData; - public SessionSkinApplyEvent(@NonNull GeyserConnection connection, String username, UUID uuid, boolean slim, boolean bedrock, SkinData skinData) { + public SessionSkinApplyEvent(GeyserConnection connection, String username, UUID uuid, boolean slim, boolean bedrock, SkinData skinData) { super(connection); this.username = username; this.uuid = uuid; @@ -62,7 +61,7 @@ public SessionSkinApplyEvent(@NonNull GeyserConnection connection, String userna * * @return the username of the player */ - public @NonNull String username() { + public String username() { return username; } @@ -71,7 +70,7 @@ public SessionSkinApplyEvent(@NonNull GeyserConnection connection, String userna * * @return the UUID of the player */ - public @NonNull UUID uuid() { + public UUID uuid() { return uuid; } @@ -98,7 +97,7 @@ public boolean bedrock() { * * @return the original skin data of the player */ - public @NonNull SkinData originalSkin() { + public SkinData originalSkin() { return originalSkinData; } @@ -107,28 +106,28 @@ public boolean bedrock() { * * @return the current skin data of the player */ - public abstract @NonNull SkinData skinData(); + public abstract SkinData skinData(); /** * Change the skin of the player. * * @param newSkin the new skin */ - public abstract void skin(@NonNull Skin newSkin); + public abstract void skin(Skin newSkin); /** * Change the cape of the player. * * @param newCape the new cape */ - public abstract void cape(@NonNull Cape newCape); + public abstract void cape(Cape newCape); /** * Change the geometry of the player. * * @param newGeometry the new geometry */ - public abstract void geometry(@NonNull SkinGeometry newGeometry); + public abstract void geometry(SkinGeometry newGeometry); /** * Change the geometry of the player. @@ -138,7 +137,7 @@ public boolean bedrock() { * @param geometryName the name of the geometry * @param geometryData the data of the geometry */ - public void geometry(@NonNull String geometryName, @NonNull String geometryData) { + public void geometry(String geometryName, String geometryData) { geometry(new SkinGeometry("{\"geometry\" :{\"default\" :\"" + geometryName + "\"}}", geometryData)); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/package-info.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/package-info.java new file mode 100644 index 00000000000..322b4b0037c --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.event.bedrock; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java index 158f14d53bb..9eb0384e077 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.connection; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.connection.GeyserConnection; @@ -35,7 +34,7 @@ public abstract class ConnectionEvent implements Event { private final GeyserConnection connection; - public ConnectionEvent(@NonNull GeyserConnection connection) { + public ConnectionEvent(GeyserConnection connection) { this.connection = connection; } @@ -44,7 +43,6 @@ public ConnectionEvent(@NonNull GeyserConnection connection) { * * @return the connection */ - @NonNull public GeyserConnection connection() { return this.connection; } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionRequestEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionRequestEvent.java index b36ee8bfb27..0c2a4bfe2b7 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionRequestEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionRequestEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2024 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,10 +25,9 @@ package org.geysermc.geyser.api.event.connection; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.event.Cancellable; import org.geysermc.event.Event; +import org.jspecify.annotations.Nullable; import java.net.InetSocketAddress; @@ -39,9 +38,9 @@ public final class ConnectionRequestEvent implements Event, Cancellable { private boolean cancelled; private final InetSocketAddress ip; - private final InetSocketAddress proxyIp; + private final @Nullable InetSocketAddress proxyIp; - public ConnectionRequestEvent(@NonNull InetSocketAddress ip, @Nullable InetSocketAddress proxyIp) { + public ConnectionRequestEvent(InetSocketAddress ip, @Nullable InetSocketAddress proxyIp) { this.ip = ip; this.proxyIp = proxyIp; } @@ -52,7 +51,7 @@ public ConnectionRequestEvent(@NonNull InetSocketAddress ip, @Nullable InetSocke * @return the IP address of the client attempting to connect * @deprecated Use {@link #inetSocketAddress()} instead */ - @NonNull @Deprecated(forRemoval = true) + @Deprecated(forRemoval = true) public InetSocketAddress getInetSocketAddress() { return ip; } @@ -63,8 +62,8 @@ public InetSocketAddress getInetSocketAddress() { * @return the IP address of the proxy handling the connection * @deprecated Use {@link #proxyIp()} instead */ - @Nullable @Deprecated(forRemoval = true) - public InetSocketAddress getProxyIp() { + @Deprecated(forRemoval = true) + public @Nullable InetSocketAddress getProxyIp() { return proxyIp; } @@ -73,7 +72,6 @@ public InetSocketAddress getProxyIp() { * * @return the IP address of the client attempting to connect */ - @NonNull public InetSocketAddress inetSocketAddress() { return ip; } @@ -83,8 +81,7 @@ public InetSocketAddress inetSocketAddress() { * * @return the IP address of the proxy handling the connection */ - @Nullable - public InetSocketAddress proxyIp() { + public @Nullable InetSocketAddress proxyIp() { return proxyIp; } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/connection/GeyserBedrockPingEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/connection/GeyserBedrockPingEvent.java index 64d3cb44f84..044a3cba749 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/connection/GeyserBedrockPingEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/connection/GeyserBedrockPingEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -26,9 +26,8 @@ package org.geysermc.geyser.api.event.connection; import org.checkerframework.checker.index.qual.NonNegative; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.event.Event; +import org.jspecify.annotations.Nullable; import java.net.InetSocketAddress; @@ -45,7 +44,7 @@ public interface GeyserBedrockPingEvent extends Event { * * @param primary the string to set as the primary motd */ - void primaryMotd(@NonNull String primary); + void primaryMotd(String primary); /** * Sets the given string as the secondary motd, the given string cannot be null. @@ -53,7 +52,7 @@ public interface GeyserBedrockPingEvent extends Event { * * @param secondary the string to set as the secondary motd */ - void secondaryMotd(@NonNull String secondary); + void secondaryMotd(String secondary); /** * Sets how many players are currently online, the given number cannot be below 0. @@ -74,16 +73,14 @@ public interface GeyserBedrockPingEvent extends Event { * * @return the primary motd string */ - @Nullable - String primaryMotd(); + @Nullable String primaryMotd(); /** * Gets the secondary motd. * * @return the secondary motd string */ - @Nullable - String secondaryMotd(); + @Nullable String secondaryMotd(); /** * Gets the current number of players. @@ -105,6 +102,5 @@ public interface GeyserBedrockPingEvent extends Event { * * @return a {@link InetSocketAddress} */ - @NonNull InetSocketAddress address(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/connection/package-info.java b/api/src/main/java/org/geysermc/geyser/api/event/connection/package-info.java new file mode 100644 index 00000000000..af93be29d5a --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/event/connection/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.event.connection; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java index ff4a1d074ea..9500bf469db 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.downstream; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Cancellable; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.connection.ConnectionEvent; @@ -41,7 +40,7 @@ public class ServerDefineCommandsEvent extends ConnectionEvent implements Cancel private final Set commands; private boolean cancelled; - public ServerDefineCommandsEvent(@NonNull GeyserConnection connection, @NonNull Set commands) { + public ServerDefineCommandsEvent(GeyserConnection connection, Set commands) { super(connection); this.commands = commands; } @@ -52,7 +51,6 @@ public ServerDefineCommandsEvent(@NonNull GeyserConnection connection, @NonNull * * @return a collection of the commands sent over */ - @NonNull public Set commands() { return this.commands; } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/downstream/package-info.java b/api/src/main/java/org/geysermc/geyser/api/event/downstream/package-info.java new file mode 100644 index 00000000000..91100bccb35 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/event/downstream/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.event.downstream; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/event/java/ServerCodeOfConductEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/java/ServerCodeOfConductEvent.java index d40094fb634..2960233a754 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/java/ServerCodeOfConductEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/java/ServerCodeOfConductEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.java; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.bedrock.SessionAcceptCodeOfConductEvent; import org.geysermc.geyser.api.event.connection.ConnectionEvent; @@ -45,7 +44,7 @@ public final class ServerCodeOfConductEvent extends ConnectionEvent { private final String codeOfConduct; private boolean hasAccepted = false; - public ServerCodeOfConductEvent(@NonNull GeyserConnection connection, String codeOfConduct) { + public ServerCodeOfConductEvent(GeyserConnection connection, String codeOfConduct) { super(connection); this.codeOfConduct = codeOfConduct; } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/java/ServerDefineCommandsEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/java/ServerDefineCommandsEvent.java index 40268d5b2c3..ba131c0ceb2 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/java/ServerDefineCommandsEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/java/ServerDefineCommandsEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.java; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Cancellable; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.connection.ConnectionEvent; @@ -41,7 +40,7 @@ public final class ServerDefineCommandsEvent extends ConnectionEvent implements private final Set commands; private boolean cancelled; - public ServerDefineCommandsEvent(@NonNull GeyserConnection connection, @NonNull Set commands) { + public ServerDefineCommandsEvent(GeyserConnection connection, Set commands) { super(connection); this.commands = commands; } @@ -52,7 +51,6 @@ public ServerDefineCommandsEvent(@NonNull GeyserConnection connection, @NonNull * * @return a collection of the commands sent over */ - @NonNull public Set commands() { return this.commands; } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/java/ServerTransferEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/java/ServerTransferEvent.java index f32d84f6a51..c74f3ab607f 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/java/ServerTransferEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/java/ServerTransferEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2024 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,11 +25,10 @@ package org.geysermc.geyser.api.event.java; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.common.value.qual.IntRange; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.event.connection.ConnectionEvent; +import org.jspecify.annotations.Nullable; import java.util.Map; @@ -41,12 +40,12 @@ public final class ServerTransferEvent extends ConnectionEvent { private final String host; private final int port; - private String bedrockHost; + private @Nullable String bedrockHost; private int bedrockPort; private final Map cookies; - public ServerTransferEvent(@NonNull GeyserConnection connection, - @NonNull String host, int port, @NonNull Map cookies) { + public ServerTransferEvent(GeyserConnection connection, + String host, int port, Map cookies) { super(connection); this.host = host; this.port = port; @@ -60,7 +59,7 @@ public ServerTransferEvent(@NonNull GeyserConnection connection, * * @return the host */ - public @NonNull String host() { + public String host() { return this.host; } @@ -96,7 +95,7 @@ public int bedrockPort() { /** * Sets the host for the Bedrock player to be transferred to */ - public void bedrockHost(@NonNull String host) { + public void bedrockHost(@Nullable String host) { if (host == null || host.isBlank()) { throw new IllegalArgumentException("Server address cannot be null or blank"); } @@ -118,7 +117,7 @@ public void bedrockPort(@IntRange(from = 0, to = 65535) int port) { * * @return the connections cookies */ - public @NonNull Map cookies() { + public Map cookies() { return cookies; } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/java/package-info.java b/api/src/main/java/org/geysermc/geyser/api/event/java/package-info.java new file mode 100644 index 00000000000..551b8e4273c --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/event/java/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.event.java; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java index d136202bd26..67ca526567e 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.lifecycle; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.command.Command; @@ -45,13 +44,12 @@ public interface GeyserDefineCommandsEvent extends Event { * * @param command the command to register */ - void register(@NonNull Command command); + void register(Command command); /** * Gets all the registered built-in {@link Command}s. * * @return all the registered built-in commands as an unmodifiable map */ - @NonNull Map commands(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomBlocksEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomBlocksEvent.java index a105578d944..77a3cf8707f 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomBlocksEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomBlocksEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.lifecycle; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.block.custom.CustomBlockData; import org.geysermc.geyser.api.block.custom.CustomBlockState; import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState; @@ -42,7 +41,7 @@ public abstract class GeyserDefineCustomBlocksEvent implements Event { * * @param customBlockData the custom block to register */ - public abstract void register(@NonNull CustomBlockData customBlockData); + public abstract void register(CustomBlockData customBlockData); /** * Registers the given {@link CustomBlockState} as an override for the @@ -53,7 +52,7 @@ public abstract class GeyserDefineCustomBlocksEvent implements Event { * @param javaIdentifier the java state identifier to override * @param customBlockState the custom block state with which to override java state identifier */ - public abstract void registerOverride(@NonNull String javaIdentifier, @NonNull CustomBlockState customBlockState); + public abstract void registerOverride(String javaIdentifier, CustomBlockState customBlockState); /** * Registers the given {@link CustomBlockData} as an override for the @@ -62,7 +61,7 @@ public abstract class GeyserDefineCustomBlocksEvent implements Event { * @param javaIdentifier the java item identifier to override * @param customBlockData the custom block data with which to override java item identifier */ - public abstract void registerItemOverride(@NonNull String javaIdentifier, @NonNull CustomBlockData customBlockData); + public abstract void registerItemOverride(String javaIdentifier, CustomBlockData customBlockData); /** * Registers the given {@link CustomBlockState} as an override for the @@ -71,5 +70,5 @@ public abstract class GeyserDefineCustomBlocksEvent implements Event { * @param javaBlockState the java block state for the non-vanilla block * @param customBlockState the custom block state with which to override java state identifier */ - public abstract void registerOverride(@NonNull JavaBlockState javaBlockState, @NonNull CustomBlockState customBlockState); + public abstract void registerOverride(JavaBlockState javaBlockState, CustomBlockState customBlockState); } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java index dc10648b5ec..c05ea8ac722 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.lifecycle; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.item.custom.v2.CustomItemDefinitionRegisterException; import org.geysermc.geyser.api.item.custom.CustomItemData; @@ -55,7 +54,6 @@ public interface GeyserDefineCustomItemsEvent extends Event { * @deprecated replaced by {@link GeyserDefineCustomItemsEvent#customItemDefinitions()} */ @Deprecated - @NonNull Map> getExistingCustomItems(); /** @@ -63,7 +61,6 @@ public interface GeyserDefineCustomItemsEvent extends Event { * indexed by the {@link Identifier} of the Java item which the item is based on. * @since 2.9.3 */ - @NonNull Map> customItemDefinitions(); /** @@ -74,7 +71,6 @@ public interface GeyserDefineCustomItemsEvent extends Event { * @deprecated replaced by {@link GeyserDefineCustomItemsEvent#nonVanillaCustomItemDefinitions()} */ @Deprecated - @NonNull List getExistingNonVanillaCustomItems(); /** @@ -83,7 +79,6 @@ public interface GeyserDefineCustomItemsEvent extends Event { *

This multimap will, at the moment, always have one entry per key.

* @since 2.9.3 */ - @NonNull Map> nonVanillaCustomItemDefinitions(); /** @@ -96,7 +91,7 @@ public interface GeyserDefineCustomItemsEvent extends Event { * @deprecated use {@link GeyserDefineCustomItemsEvent#register(Identifier, CustomItemDefinition)} */ @Deprecated - boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData); + boolean register(String identifier, CustomItemData customItemData); /** * Registers a Bedrock custom item definition based on a Java item. This is used to register items with custom textures and properties @@ -107,7 +102,7 @@ public interface GeyserDefineCustomItemsEvent extends Event { * @throws CustomItemDefinitionRegisterException when an error occurred while registering the item * @since 2.9.3 */ - void register(@NonNull Identifier identifier, @NonNull CustomItemDefinition customItemDefinition); + void register(Identifier identifier, CustomItemDefinition customItemDefinition); /** * Registers a custom item with no base item. This is used for mods. @@ -118,7 +113,7 @@ public interface GeyserDefineCustomItemsEvent extends Event { * @deprecated use {@link GeyserDefineCustomItemsEvent#register(NonVanillaCustomItemDefinition)} */ @Deprecated - boolean register(@NonNull NonVanillaCustomItemData customItemData); + boolean register(NonVanillaCustomItemData customItemData); /** * Registers a custom item with no base Java edition item. This is used for non-vanilla items added by mods. @@ -127,5 +122,5 @@ public interface GeyserDefineCustomItemsEvent extends Event { * @throws CustomItemDefinitionRegisterException when an error occurred while registering the item * @since 2.9.3 */ - void register(@NonNull NonVanillaCustomItemDefinition customItemDefinition); + void register(NonVanillaCustomItemDefinition customItemDefinition); } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomSkullsEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomSkullsEvent.java index 6443bbeb3bb..ea5a259a539 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomSkullsEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomSkullsEvent.java @@ -1,6 +1,30 @@ +/* + * Copyright (c) 2023-2026 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.geysermc.event.Event; /** @@ -24,5 +48,5 @@ public enum SkullTextureType { * @param texture the username, UUID, base64 encoded profile, or skin hash * @param type the type of texture provided */ - public abstract void register(@NonNull String texture, @NonNull SkullTextureType type); + public abstract void register(String texture, SkullTextureType type); } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineEntityPropertiesEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineEntityPropertiesEvent.java index ef8380041ec..1230e3551fe 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineEntityPropertiesEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineEntityPropertiesEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,8 +25,6 @@ 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.entity.EntityData; import org.geysermc.geyser.api.entity.property.GeyserEntityProperty; @@ -37,6 +35,7 @@ import org.geysermc.geyser.api.entity.property.type.GeyserStringEnumProperty; import org.geysermc.geyser.api.entity.type.GeyserEntity; import org.geysermc.geyser.api.util.Identifier; +import org.jspecify.annotations.Nullable; import java.util.Collection; import java.util.List; @@ -89,7 +88,7 @@ public interface GeyserDefineEntityPropertiesEvent extends Event { * * @since 2.9.0 */ - Collection> properties(@NonNull Identifier entityType); + Collection> properties(Identifier entityType); /** * Registers a {@code float}-backed entity property. @@ -103,7 +102,7 @@ public interface GeyserDefineEntityPropertiesEvent extends Event { * * @since 2.9.0 */ - GeyserFloatEntityProperty registerFloatProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, float min, float max, @Nullable Float defaultValue); + GeyserFloatEntityProperty registerFloatProperty(Identifier entityType, Identifier propertyIdentifier, float min, float max, @Nullable Float defaultValue); /** * Registers a {@code float}-backed entity property with a default value set to the minimum value. @@ -117,7 +116,7 @@ public interface GeyserDefineEntityPropertiesEvent extends Event { * * @since 2.9.0 */ - default GeyserFloatEntityProperty registerFloatProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, float min, float max) { + default GeyserFloatEntityProperty registerFloatProperty(Identifier entityType, Identifier propertyIdentifier, float min, float max) { return registerFloatProperty(entityType, propertyIdentifier, min, max, null); } @@ -133,7 +132,7 @@ default GeyserFloatEntityProperty registerFloatProperty(@NonNull Identifier enti * * @since 2.9.0 */ - GeyserIntEntityProperty registerIntegerProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, int min, int max, @Nullable Integer defaultValue); + GeyserIntEntityProperty registerIntegerProperty(Identifier entityType, Identifier propertyIdentifier, int min, int max, @Nullable Integer defaultValue); /** * Registers an {@code int}-backed entity property with a default value set to the minimum value. @@ -146,7 +145,7 @@ default GeyserFloatEntityProperty registerFloatProperty(@NonNull Identifier enti * * @since 2.9.0 */ - default GeyserIntEntityProperty registerIntegerProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, int min, int max) { + default GeyserIntEntityProperty registerIntegerProperty(Identifier entityType, Identifier propertyIdentifier, int min, int max) { return registerIntegerProperty(entityType, propertyIdentifier, min, max, null); } @@ -160,7 +159,7 @@ default GeyserIntEntityProperty registerIntegerProperty(@NonNull Identifier enti * * @since 2.9.0 */ - GeyserBooleanEntityProperty registerBooleanProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, boolean defaultValue); + GeyserBooleanEntityProperty registerBooleanProperty(Identifier entityType, Identifier propertyIdentifier, boolean defaultValue); /** * Registers a {@code boolean}-backed entity property with a default of {@code false}. @@ -171,7 +170,7 @@ default GeyserIntEntityProperty registerIntegerProperty(@NonNull Identifier enti * @return the created boolean property * @since 2.9.0 */ - default GeyserBooleanEntityProperty registerBooleanProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier) { + default GeyserBooleanEntityProperty registerBooleanProperty(Identifier entityType, Identifier propertyIdentifier) { return registerBooleanProperty(entityType, propertyIdentifier, false); } @@ -191,7 +190,7 @@ default GeyserBooleanEntityProperty registerBooleanProperty(@NonNull Identifier * * @since 2.9.0 */ - > GeyserEnumEntityProperty registerEnumProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, @NonNull Class enumClass, @Nullable E defaultValue); + > GeyserEnumEntityProperty registerEnumProperty(Identifier entityType, Identifier propertyIdentifier, Class enumClass, @Nullable E defaultValue); /** * Registers a typed {@linkplain Enum enum}-backed entity property with the first value set as the default. @@ -205,7 +204,7 @@ default GeyserBooleanEntityProperty registerBooleanProperty(@NonNull Identifier * * @since 2.9.0 */ - default > GeyserEnumEntityProperty registerEnumProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, @NonNull Class enumClass) { + default > GeyserEnumEntityProperty registerEnumProperty(Identifier entityType, Identifier propertyIdentifier, Class enumClass) { return registerEnumProperty(entityType, propertyIdentifier, enumClass, null); } @@ -223,7 +222,7 @@ default > GeyserEnumEntityProperty registerEnumProperty(@No * * @since 2.9.0 */ - GeyserStringEnumProperty registerEnumProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, @NonNull List values, @Nullable String defaultValue); + GeyserStringEnumProperty registerEnumProperty(Identifier entityType, Identifier propertyIdentifier, List values, @Nullable String defaultValue); /** * Registers a string-backed "enum-like" entity property with the first value as the default. @@ -236,7 +235,7 @@ default > GeyserEnumEntityProperty registerEnumProperty(@No * * @since 2.9.0 */ - default GeyserStringEnumProperty registerEnumProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, @NonNull List values) { + default GeyserStringEnumProperty registerEnumProperty(Identifier entityType, Identifier propertyIdentifier, List values) { return registerEnumProperty(entityType, propertyIdentifier, values, null); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineResourcePacksEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineResourcePacksEvent.java index 4128f1c47e0..15d3f3cdf0a 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineResourcePacksEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineResourcePacksEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2025 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,12 +25,11 @@ 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 org.jspecify.annotations.Nullable; import java.util.Collection; import java.util.List; @@ -50,7 +49,7 @@ public abstract class GeyserDefineResourcePacksEvent implements Event { * @return an unmodifiable list of {@link ResourcePack}'s * @since 2.6.2 */ - public abstract @NonNull List resourcePacks(); + public abstract List resourcePacks(); /** * Registers a {@link ResourcePack} to be sent to the client, optionally alongside @@ -61,7 +60,7 @@ public abstract class GeyserDefineResourcePacksEvent implements Event { * @throws ResourcePackException if an issue occurred during pack registration * @since 2.6.2 */ - public abstract void register(@NonNull ResourcePack pack, @Nullable ResourcePackOption... options); + public abstract void register(ResourcePack pack, @Nullable ResourcePackOption... options); /** * Sets {@link ResourcePackOption}'s for a {@link ResourcePack}. @@ -71,7 +70,7 @@ public abstract class GeyserDefineResourcePacksEvent implements Event { * @throws ResourcePackException if an issue occurred during {@link ResourcePackOption} registration * @since 2.6.2 */ - public abstract void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption... options); + public abstract void registerOptions(UUID uuid, ResourcePackOption... options); /** * Returns a collection of {@link ResourcePackOption}'s for a registered {@link ResourcePack}. @@ -82,7 +81,7 @@ public abstract class GeyserDefineResourcePacksEvent implements Event { * @throws ResourcePackException if the pack was not registered * @since 2.6.2 */ - public abstract Collection> options(@NonNull UUID uuid); + public abstract Collection> options(UUID uuid); /** * Returns the current option, or null, for a given {@link ResourcePackOption.Type}. @@ -92,7 +91,7 @@ public abstract class GeyserDefineResourcePacksEvent implements Event { * @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); + public abstract @Nullable ResourcePackOption option(UUID uuid, ResourcePackOption.Type type); /** * Unregisters a {@link ResourcePack} from the list of packs sent to connecting Bedrock clients. @@ -100,5 +99,5 @@ public abstract class GeyserDefineResourcePacksEvent implements Event { * @param uuid the UUID of the {@link ResourcePack} to be removed * @since 2.6.2 */ - public abstract void unregister(@NonNull UUID uuid); + public abstract void unregister(UUID uuid); } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java index 047f9df5771..96debbd2afb 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.lifecycle; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import java.nio.file.Path; @@ -35,5 +34,5 @@ * @deprecated Use the {@link GeyserDefineResourcePacksEvent} instead. */ @Deprecated -public record GeyserLoadResourcePacksEvent(@NonNull List resourcePacks) implements Event { +public record GeyserLoadResourcePacksEvent(List resourcePacks) implements Event { } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java index 8d145f615dc..636b4d6a303 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.lifecycle; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventRegistrar; @@ -37,5 +36,5 @@ * @param extensionManager the extension manager * @param eventBus the event bus */ -public record GeyserPostInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { +public record GeyserPostInitializeEvent(ExtensionManager extensionManager, EventBus eventBus) implements Event { } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostReloadEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostReloadEvent.java index c421cda37c9..b5a79321b07 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostReloadEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostReloadEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2024 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.lifecycle; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventRegistrar; @@ -38,5 +37,5 @@ * @param extensionManager the extension manager * @param eventBus the event bus */ -public record GeyserPostReloadEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { +public record GeyserPostReloadEvent(ExtensionManager extensionManager, EventBus eventBus) implements Event { } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java index 8be89dafdcf..51e878b7f08 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.lifecycle; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventRegistrar; @@ -37,5 +36,5 @@ * @param extensionManager the extension manager * @param eventBus the event bus */ -public record GeyserPreInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { +public record GeyserPreInitializeEvent(ExtensionManager extensionManager, EventBus eventBus) implements Event { } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreReloadEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreReloadEvent.java index 9147ad4b815..638c5b2346d 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreReloadEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreReloadEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2024 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.lifecycle; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventRegistrar; @@ -38,5 +37,5 @@ * @param extensionManager the extension manager * @param eventBus the event bus */ -public record GeyserPreReloadEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { +public record GeyserPreReloadEvent(ExtensionManager extensionManager, EventBus eventBus) implements Event { } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserRegisterPermissionsEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserRegisterPermissionsEvent.java index 4f06c4e5f92..bc8a8aef28c 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserRegisterPermissionsEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserRegisterPermissionsEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.lifecycle; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.util.TriState; @@ -47,5 +46,5 @@ public interface GeyserRegisterPermissionsEvent extends Event { * @param permission the permission node to register * @param defaultValue the default value of the node */ - void register(@NonNull String permission, @NonNull TriState defaultValue); + void register(String permission, TriState defaultValue); } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java index 7793ef997c8..7056dc39ab0 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.event.lifecycle; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.event.Event; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventRegistrar; @@ -34,5 +33,5 @@ /** * Called when Geyser is shutting down. */ -public record GeyserShutdownEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { +public record GeyserShutdownEvent(ExtensionManager extensionManager, EventBus eventBus) implements Event { } diff --git a/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/package-info.java b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/package-info.java new file mode 100644 index 00000000000..d88053795db --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/event/lifecycle/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.event.lifecycle; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/event/package-info.java b/api/src/main/java/org/geysermc/geyser/api/event/package-info.java new file mode 100644 index 00000000000..6429d0e4c32 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/event/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.event; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/extension/Extension.java b/api/src/main/java/org/geysermc/geyser/api/extension/Extension.java index 1eacfea9aa1..d3367011951 100644 --- a/api/src/main/java/org/geysermc/geyser/api/extension/Extension.java +++ b/api/src/main/java/org/geysermc/geyser/api/extension/Extension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.extension; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.api.GeyserApiBase; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.event.EventRegistrar; @@ -62,7 +61,6 @@ default void setEnabled(boolean enabled) { * * @return the extension's data folder */ - @NonNull default Path dataFolder() { return this.extensionLoader().dataFolder(this); } @@ -72,7 +70,6 @@ default Path dataFolder() { * * @return the extension event bus */ - @NonNull default ExtensionEventBus eventBus() { return this.extensionLoader().eventBus(this); } @@ -82,7 +79,6 @@ default ExtensionEventBus eventBus() { * * @return the extension manager */ - @NonNull default ExtensionManager extensionManager() { return this.geyserApi().extensionManager(); } @@ -92,7 +88,6 @@ default ExtensionManager extensionManager() { * * @return the extension's name */ - @NonNull default String name() { return this.description().name(); } @@ -102,7 +97,6 @@ default String name() { * * @return the extension's description */ - @NonNull default ExtensionDescription description() { return this.extensionLoader().description(this); } @@ -111,7 +105,6 @@ default ExtensionDescription description() { * @return the root command that all of this extension's commands will stem from. * By default, this is the extension's id. */ - @NonNull default String rootCommand() { return this.description().id(); } @@ -121,7 +114,6 @@ default String rootCommand() { * * @return the extension's logger */ - @NonNull default ExtensionLogger logger() { return this.extensionLoader().logger(this); } @@ -131,7 +123,6 @@ default ExtensionLogger logger() { * * @return the extension loader */ - @NonNull default ExtensionLoader extensionLoader() { return Objects.requireNonNull(this.extensionManager().extensionLoader()); } @@ -141,7 +132,6 @@ default ExtensionLoader extensionLoader() { * * @return the geyser api instance */ - @NonNull default GeyserApi geyserApi() { return GeyserApi.api(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java b/api/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java index 25daf450faa..eaca9f5ab33 100644 --- a/api/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java +++ b/api/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.extension; -import org.checkerframework.checker.nullness.qual.NonNull; import java.util.List; @@ -39,7 +38,6 @@ public interface ExtensionDescription { * * @return the extension's id */ - @NonNull String id(); /** @@ -47,7 +45,6 @@ public interface ExtensionDescription { * * @return the extension's name */ - @NonNull String name(); /** @@ -55,7 +52,6 @@ public interface ExtensionDescription { * * @return the extension's main class */ - @NonNull String main(); /** @@ -106,7 +102,6 @@ default String apiVersion() { * * @return the extension's description */ - @NonNull String version(); /** @@ -114,6 +109,5 @@ default String apiVersion() { * * @return the extension's authors */ - @NonNull List authors(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java b/api/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java index 30414d500e4..da76dfc0afc 100644 --- a/api/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java +++ b/api/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.extension; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.event.ExtensionEventBus; import java.nio.file.Path; @@ -40,7 +39,7 @@ public abstract class ExtensionLoader { * @param extension the extension * @return if the extension is enabled */ - protected abstract boolean isEnabled(@NonNull Extension extension); + protected abstract boolean isEnabled(Extension extension); /** * Sets if the given {@link Extension} is enabled. @@ -48,7 +47,7 @@ public abstract class ExtensionLoader { * @param extension the extension to enable * @param enabled if the extension should be enabled */ - protected abstract void setEnabled(@NonNull Extension extension, boolean enabled); + protected abstract void setEnabled(Extension extension, boolean enabled); /** * Gets the given {@link Extension}'s data folder. @@ -56,8 +55,7 @@ public abstract class ExtensionLoader { * @param extension the extension * @return the data folder of the given extension */ - @NonNull - protected abstract Path dataFolder(@NonNull Extension extension); + protected abstract Path dataFolder(Extension extension); /** * Gets the given {@link Extension}'s {@link ExtensionDescription}. @@ -65,8 +63,7 @@ public abstract class ExtensionLoader { * @param extension the extension * @return the description of the given extension */ - @NonNull - protected abstract ExtensionDescription description(@NonNull Extension extension); + protected abstract ExtensionDescription description(Extension extension); /** * Gets the given {@link Extension}'s {@link ExtensionEventBus}. @@ -74,8 +71,7 @@ public abstract class ExtensionLoader { * @param extension the extension * @return the extension's event bus */ - @NonNull - protected abstract ExtensionEventBus eventBus(@NonNull Extension extension); + protected abstract ExtensionEventBus eventBus(Extension extension); /** * Gets the {@link ExtensionLogger} for the given {@link Extension}. @@ -83,15 +79,14 @@ public abstract class ExtensionLoader { * @param extension the extension * @return the extension logger for the given extension */ - @NonNull - protected abstract ExtensionLogger logger(@NonNull Extension extension); + protected abstract ExtensionLogger logger(Extension extension); /** * Loads all extensions. * * @param extensionManager the extension manager */ - protected abstract void loadAllExtensions(@NonNull ExtensionManager extensionManager); + protected abstract void loadAllExtensions(ExtensionManager extensionManager); /** * Registers the given {@link Extension} with the given {@link ExtensionManager}. @@ -99,7 +94,7 @@ public abstract class ExtensionLoader { * @param extension the extension * @param extensionManager the extension manager */ - protected void register(@NonNull Extension extension, @NonNull ExtensionManager extensionManager) { + protected void register(Extension extension, ExtensionManager extensionManager) { extensionManager.register(extension); } -} \ No newline at end of file +} diff --git a/api/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java b/api/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java index 5226221df7c..5af8e10315b 100644 --- a/api/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java +++ b/api/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,8 +25,7 @@ package org.geysermc.geyser.api.extension; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; import java.util.Collection; @@ -41,29 +40,27 @@ public abstract class ExtensionManager { * @param id the ID of the extension * @return an extension with the given ID */ - @Nullable - public abstract Extension extension(@NonNull String id); + public abstract @Nullable Extension extension(String id); /** * Enables the given {@link Extension}. * * @param extension the extension to enable */ - public abstract void enable(@NonNull Extension extension); + public abstract void enable(Extension extension); /** * Disables the given {@link Extension}. * * @param extension the extension to disable */ - public abstract void disable(@NonNull Extension extension); + public abstract void disable(Extension extension); /** * Gets all the {@link Extension}s currently loaded. * * @return all the extensions currently loaded */ - @NonNull public abstract Collection extensions(); /** @@ -71,20 +68,19 @@ public abstract class ExtensionManager { * * @return the extension loader */ - @Nullable - public abstract ExtensionLoader extensionLoader(); + public abstract @Nullable ExtensionLoader extensionLoader(); /** * Registers an {@link Extension} with the given {@link ExtensionLoader}. * * @param extension the extension */ - public abstract void register(@NonNull Extension extension); + public abstract void register(Extension extension); /** * Loads all extensions from the given {@link ExtensionLoader}. */ - protected final void loadAllExtensions(@NonNull ExtensionLoader extensionLoader) { + protected final void loadAllExtensions(ExtensionLoader extensionLoader) { extensionLoader.loadAllExtensions(this); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/extension/exception/package-info.java b/api/src/main/java/org/geysermc/geyser/api/extension/exception/package-info.java new file mode 100644 index 00000000000..735d9c9d532 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/extension/exception/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.extension.exception; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/extension/package-info.java b/api/src/main/java/org/geysermc/geyser/api/extension/package-info.java new file mode 100644 index 00000000000..9e9d2282636 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/extension/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.extension; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java index a7bedd59ca5..eb392b88263 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,9 +25,8 @@ package org.geysermc.geyser.api.item.custom; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.GeyserApi; +import org.jspecify.annotations.Nullable; import java.util.OptionalInt; import java.util.Set; @@ -44,7 +43,7 @@ public interface CustomItemData { * * @return the item's name */ - @NonNull String name(); + String name(); /** * Gets the custom item options of the item. @@ -58,14 +57,14 @@ public interface CustomItemData { * * @return the item's display name */ - @NonNull String displayName(); + String displayName(); /** * Gets the item's icon. By default, this is the item's name. * * @return the item's icon */ - @NonNull String icon(); + String icon(); /** * Gets if the item is allowed to be put into the offhand. @@ -86,7 +85,7 @@ public interface CustomItemData { * * @return the item's creative category */ - @NonNull OptionalInt creativeCategory(); + OptionalInt creativeCategory(); /** * Gets the item's creative group. @@ -119,7 +118,7 @@ public interface CustomItemData { * * @return the item's tags, if they exist */ - @NonNull Set tags(); + Set tags(); static CustomItemData.Builder builder() { return GeyserApi.api().provider(CustomItemData.Builder.class); @@ -129,13 +128,13 @@ interface Builder { /** * Will also set the display name and icon to the provided parameter, if it is currently not set. */ - Builder name(@NonNull String name); + Builder name(String name); - Builder customItemOptions(@NonNull CustomItemOptions customItemOptions); + Builder customItemOptions(CustomItemOptions customItemOptions); - Builder displayName(@NonNull String displayName); + Builder displayName(String displayName); - Builder icon(@NonNull String icon); + Builder icon(String icon); Builder allowOffhand(boolean allowOffhand); diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java index cc9496cb331..4e3f209d936 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.item.custom; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.TriState; @@ -43,21 +42,21 @@ public interface CustomItemOptions { * * @return if the item should be unbreakable */ - @NonNull TriState unbreakable(); + TriState unbreakable(); /** * Gets the item's custom model data predicate. * * @return the item's custom model data */ - @NonNull OptionalInt customModelData(); + OptionalInt customModelData(); /** * Gets the item's damage predicate. * * @return the item's damage predicate */ - @NonNull OptionalInt damagePredicate(); + OptionalInt damagePredicate(); /** * Gets if this mapping should just translate to the default item. diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java index f81da0ae261..6cc4bd64482 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,7 @@ package org.geysermc.geyser.api.item.custom; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * This class is used to store the render offsets of custom items. diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java index 0eaf62d96b8..ac0f82fae9c 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2024 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -26,9 +26,8 @@ package org.geysermc.geyser.api.item.custom; import org.checkerframework.checker.index.qual.NonNegative; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.GeyserApi; +import org.jspecify.annotations.Nullable; import java.util.Set; @@ -44,7 +43,7 @@ public interface NonVanillaCustomItemData extends CustomItemData { * * @return The java identifier for this item. */ - @NonNull String identifier(); + String identifier(); /** * Gets the java item id of the item. @@ -175,9 +174,9 @@ static NonVanillaCustomItemData.Builder builder() { interface Builder extends CustomItemData.Builder { @Override - Builder name(@NonNull String name); + Builder name(String name); - Builder identifier(@NonNull String identifier); + Builder identifier(String identifier); Builder javaId(@NonNegative int javaId); @@ -226,13 +225,13 @@ default Builder tool(boolean isTool) { Builder creativeGroup(@Nullable String creativeGroup); @Override - Builder customItemOptions(@NonNull CustomItemOptions customItemOptions); + Builder customItemOptions(CustomItemOptions customItemOptions); @Override - Builder displayName(@NonNull String displayName); + Builder displayName(String displayName); @Override - Builder icon(@NonNull String icon); + Builder icon(String icon); @Override Builder allowOffhand(boolean allowOffhand); diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/package-info.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/package-info.java new file mode 100644 index 00000000000..dfc7aca0f2c --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.item.custom; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/CustomItemBedrockOptions.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/CustomItemBedrockOptions.java index 91e3361122e..d48bff1d8ae 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/CustomItemBedrockOptions.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/CustomItemBedrockOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 GeyserMC. http://geysermc.org + * Copyright (c) 2024-2026 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 @@ -25,12 +25,11 @@ package org.geysermc.geyser.api.item.custom.v2; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.CreativeCategory; import org.geysermc.geyser.api.util.Identifier; +import org.jspecify.annotations.Nullable; import java.util.Set; @@ -47,8 +46,7 @@ public interface CustomItemBedrockOptions { * @see CustomItemDefinition#icon() * @since 2.9.3 */ - @Nullable - String icon(); + @Nullable String icon(); /** * If the item is allowed to be put into the offhand. Defaults to true. @@ -83,7 +81,6 @@ public interface CustomItemBedrockOptions { * @return the item's creative category * @since 2.9.3 */ - @NonNull CreativeCategory creativeCategory(); /** @@ -94,8 +91,7 @@ public interface CustomItemBedrockOptions { * @return the item's creative group * @since 2.9.3 */ - @Nullable - String creativeGroup(); + @Nullable String creativeGroup(); /** * Gets the item's set of bedrock tags that can be used in Molang. @@ -104,7 +100,6 @@ public interface CustomItemBedrockOptions { * @return the item's set of bedrock tags, can be empty * @since 2.9.3 */ - @NonNull Set tags(); /** @@ -176,7 +171,7 @@ interface Builder { * @since 2.9.3 */ @This - Builder creativeCategory(@NonNull CreativeCategory creativeCategory); + Builder creativeCategory(CreativeCategory creativeCategory); /** * Sets the item's creative group. @@ -198,7 +193,7 @@ interface Builder { * @since 2.9.3 */ @This - Builder tag(@NonNull Identifier tag); + Builder tag(Identifier tag); /** * Sets the item's set of bedrock tags, for use in Molang. Pass {@code null} to clear all tags. diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/CustomItemDefinition.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/CustomItemDefinition.java index 1aa14bebc06..50864ca5590 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/CustomItemDefinition.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/CustomItemDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 GeyserMC. http://geysermc.org + * Copyright (c) 2024-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.item.custom.v2; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.item.custom.v2.component.ItemDataComponent; @@ -76,7 +75,7 @@ public interface CustomItemDefinition { * @return the Bedrock item identifier * @since 2.9.3 */ - @NonNull Identifier bedrockIdentifier(); + Identifier bedrockIdentifier(); /** * The display name of the item. If none is set, the display name is taken from the item's Bedrock identifier. @@ -84,7 +83,7 @@ public interface CustomItemDefinition { * @return the display name shown to Bedrock clients * @since 2.9.3 */ - @NonNull String displayName(); + String displayName(); /** * The item model this definition is for. If the model is in the {@code minecraft} namespace, then the definition must have at least one predicate. @@ -94,7 +93,7 @@ public interface CustomItemDefinition { * @return the identifier of the Java item model used to match this definition * @since 2.9.3 */ - @NonNull Identifier model(); + Identifier model(); /** * The icon used for this item. @@ -108,7 +107,7 @@ public interface CustomItemDefinition { * @return the icon shown to Bedrock players * @since 2.9.3 */ - @NonNull String icon(); + String icon(); /** * The predicates that have to match for this item definition to be used. These predicates can access properties similar to the Java item model predicates. @@ -122,14 +121,14 @@ public interface CustomItemDefinition { * * @since 2.9.3 */ - @NonNull List> predicates(); + List> predicates(); /** * The predicate strategy used when evaluating predicates. Determines if one of, or all of the predicates have to pass for this item definition to be used. Defaults to {@link PredicateStrategy#AND}. * * @since 2.9.3 */ - @NonNull PredicateStrategy predicateStrategy(); + PredicateStrategy predicateStrategy(); /** * @return the priority of this definition. For all definitions for a single Java item model, definitions with a higher priority will be matched first. Defaults to 0. @@ -141,7 +140,7 @@ public interface CustomItemDefinition { * @return the item's Bedrock options. These describe item properties that can't be described in item components, e.g. item texture size and if the item is allowed in the off-hand. * @since 2.9.3 */ - @NonNull CustomItemBedrockOptions bedrockOptions(); + CustomItemBedrockOptions bedrockOptions(); /** * The item's data components. It is expected that the item always has these components on the server. If the components mismatch, bugs will occur. @@ -173,7 +172,6 @@ public interface CustomItemDefinition { * @return the item's data component patch * @since 2.9.3 */ - @NonNull ItemDataComponentMap components(); /** @@ -184,7 +182,7 @@ public interface CustomItemDefinition { * @return a list of removed default item data components * @since 2.9.3 */ - @NonNull List removedComponents(); + List removedComponents(); /** * Creates a builder for the custom item definition. @@ -196,7 +194,7 @@ public interface CustomItemDefinition { * @return a new builder * @since 2.9.3 */ - static Builder builder(@NonNull Identifier bedrockIdentifier, @NonNull Identifier itemModel) { + static Builder builder(Identifier bedrockIdentifier, Identifier itemModel) { return GeyserApi.api().provider(Builder.class, bedrockIdentifier, itemModel); } @@ -215,7 +213,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder displayName(@NonNull String displayName); + Builder displayName(String displayName); /** * Sets the priority of this definition, used for definition matching. @@ -239,7 +237,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder bedrockOptions(CustomItemBedrockOptions.@NonNull Builder options); + Builder bedrockOptions(CustomItemBedrockOptions.Builder options); /** * Adds a predicate that must match for Geyser to use this item definition. @@ -250,7 +248,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder predicate(@NonNull MinecraftPredicate predicate); + Builder predicate(MinecraftPredicate predicate); /** * Sets the predicate strategy that should be used for item definition matching. @@ -260,7 +258,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder predicateStrategy(@NonNull PredicateStrategy strategy); + Builder predicateStrategy(PredicateStrategy strategy); /** * Sets data components that determine the item behavior. These are assumed to also be @@ -278,7 +276,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder component(@NonNull ItemDataComponent component, @NonNull T value); + Builder component(ItemDataComponent component, T value); /** * Convenience method for {@link CustomItemDefinition.Builder#component(ItemDataComponent, Object)} @@ -292,7 +290,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - default Builder component(@NonNull ItemDataComponent component, @NonNull GenericBuilder builder) { + default Builder component(ItemDataComponent component, GenericBuilder builder) { return component(component, builder.build()); } @@ -310,7 +308,7 @@ default Builder component(@NonNull ItemDataComponent component, @NonNull * @since 2.9.3 */ @This - Builder removeComponent(@NonNull Identifier component); + Builder removeComponent(Identifier component); /** * Convenience method for {@link CustomItemDefinition.Builder#removeComponent(Identifier)}. @@ -321,7 +319,7 @@ default Builder component(@NonNull ItemDataComponent component, @NonNull * @since 2.9.3 */ @This - default Builder removeComponent(@NonNull ItemDataComponent component) { + default Builder removeComponent(ItemDataComponent component) { Objects.requireNonNull(component); if (!component.vanilla()) { throw new IllegalArgumentException("Cannot remove non-vanilla component"); diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/NonVanillaCustomItemDefinition.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/NonVanillaCustomItemDefinition.java index 6f96acab7c6..61ffe92c1e8 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/NonVanillaCustomItemDefinition.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/NonVanillaCustomItemDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -26,8 +26,6 @@ package org.geysermc.geyser.api.item.custom.v2; import org.checkerframework.checker.index.qual.NonNegative; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.item.custom.v2.component.geyser.GeyserItemDataComponents; @@ -38,6 +36,7 @@ import org.geysermc.geyser.api.predicate.context.item.ItemPredicateContext; import org.geysermc.geyser.api.util.Identifier; import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.Nullable; import java.util.List; @@ -60,7 +59,7 @@ public interface NonVanillaCustomItemDefinition extends CustomItemDefinition { * @return the item's Java identifier * @since 2.9.3 */ - @NonNull Identifier identifier(); + Identifier identifier(); /** * The item's Java network ID. @@ -89,7 +88,6 @@ public interface NonVanillaCustomItemDefinition extends CustomItemDefinition { * @since 2.9.3 */ @Override - @NonNull default List> predicates() { throw new UnsupportedOperationException("Predicates are currently not supported for use with non-vanilla custom item definitions"); } @@ -103,7 +101,6 @@ default List> predicates() { * @since 2.9.3 */ @Override - @NonNull default PredicateStrategy predicateStrategy() { throw new UnsupportedOperationException("Predicates are currently not supported for use with non-vanilla custom item definitions"); } @@ -134,7 +131,6 @@ default int priority() { * @since 2.9.3 */ @Override - @NonNull ItemDataComponentMap components(); /** @@ -147,7 +143,7 @@ default int priority() { * @return a new builder * @since 2.9.3 */ - static Builder builder(@NonNull Identifier javaIdentifier, int javaId) { + static Builder builder(Identifier javaIdentifier, int javaId) { return builder(javaIdentifier, javaIdentifier, javaId); } @@ -163,7 +159,7 @@ static Builder builder(@NonNull Identifier javaIdentifier, int javaId) { * @return a new builder * @since 2.9.3 */ - static Builder builder(@NonNull Identifier javaIdentifier, @NonNull Identifier bedrockIdentifier, int javaId) { + static Builder builder(Identifier javaIdentifier, Identifier bedrockIdentifier, int javaId) { return GeyserApi.api().provider(Builder.class, javaIdentifier, bedrockIdentifier, javaId); } @@ -179,7 +175,7 @@ interface Builder extends CustomItemDefinition.Builder { */ @Override @This - Builder displayName(@NonNull String displayName); + Builder displayName(String displayName); /** * {@inheritDoc} @@ -195,7 +191,7 @@ interface Builder extends CustomItemDefinition.Builder { */ @Override @This - Builder bedrockOptions(CustomItemBedrockOptions.@NonNull Builder options); + Builder bedrockOptions(CustomItemBedrockOptions.Builder options); /** * {@inheritDoc} @@ -203,7 +199,7 @@ interface Builder extends CustomItemDefinition.Builder { */ @Override @This - Builder component(@NonNull ItemDataComponent component, @NonNull T value); + Builder component(ItemDataComponent component, T value); /** * Sets the Java translation string of the item. diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/ItemDataComponent.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/ItemDataComponent.java index fd4329a4bf4..2c18ee131dd 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/ItemDataComponent.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/ItemDataComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.item.custom.v2.component; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.item.custom.v2.CustomItemDefinition; import org.geysermc.geyser.api.item.custom.v2.component.geyser.GeyserItemDataComponents; import org.geysermc.geyser.api.item.custom.v2.component.java.JavaItemDataComponents; @@ -54,7 +53,6 @@ public interface ItemDataComponent { * @return the identifier * @since 2.9.3 */ - @NonNull Identifier identifier(); /** @@ -63,7 +61,6 @@ public interface ItemDataComponent { * @return the validator * @since 2.9.3 */ - @NonNull Predicate validator(); /** diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/GeyserBlockPlacer.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/GeyserBlockPlacer.java index 6e6f4ec3508..4fc90b7c9d7 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/GeyserBlockPlacer.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/GeyserBlockPlacer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.item.custom.v2.component.geyser; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.GenericBuilder; @@ -47,7 +46,7 @@ public interface GeyserBlockPlacer { * @return the identifier of the block to place * @since 2.9.3 */ - @NonNull Identifier block(); + Identifier block(); /** * Whether to use the block's rendering @@ -65,7 +64,7 @@ public interface GeyserBlockPlacer { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(GeyserBlockPlacer.Builder.class); } @@ -77,7 +76,7 @@ public interface GeyserBlockPlacer { * @return the block placer component * @since 2.9.3 */ - static @NonNull GeyserBlockPlacer of(@NonNull Identifier block, boolean useBlockIcon) { + static GeyserBlockPlacer of(Identifier block, boolean useBlockIcon) { return GeyserBlockPlacer.builder().block(block).useBlockIcon(useBlockIcon).build(); } @@ -98,7 +97,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder block(@NonNull Identifier block); + Builder block(Identifier block); /** * Whether to use the block's icon over the item icon. diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/GeyserChargeable.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/GeyserChargeable.java index 39e536cc43a..1865c7000dd 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/GeyserChargeable.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/GeyserChargeable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -26,7 +26,6 @@ package org.geysermc.geyser.api.item.custom.v2.component.geyser; import org.checkerframework.checker.index.qual.NonNegative; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.GenericBuilder; @@ -71,7 +70,7 @@ public interface GeyserChargeable { * @return all valid ammunition items * @since 2.9.3 */ - List<@NonNull Identifier> ammunition(); + List ammunition(); /** * Creates a builder for the Chargeable component. @@ -79,7 +78,7 @@ public interface GeyserChargeable { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(GeyserChargeable.Builder.class); } @@ -121,7 +120,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder ammunition(@NonNull Identifier ammunition); + Builder ammunition(Identifier ammunition); /** * Creates the chargeable component. diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/GeyserThrowableComponent.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/GeyserThrowableComponent.java index 6199303a599..ddf9eff2f4c 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/GeyserThrowableComponent.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/GeyserThrowableComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.item.custom.v2.component.geyser; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.GenericBuilder; @@ -54,7 +53,7 @@ public interface GeyserThrowableComponent { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(GeyserThrowableComponent.Builder.class); } @@ -65,7 +64,7 @@ public interface GeyserThrowableComponent { * @return a throwable component * @since 2.9.3 */ - static @NonNull GeyserThrowableComponent of(boolean doSwingAnimation) { + static GeyserThrowableComponent of(boolean doSwingAnimation) { return builder().doSwingAnimation(doSwingAnimation).build(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/package-info.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/package-info.java new file mode 100644 index 00000000000..56a185aa782 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/geyser/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.item.custom.v2.component.geyser; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaAttackRange.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaAttackRange.java index e165ccc30bd..ab560fb8336 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaAttackRange.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaAttackRange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.item.custom.v2.component.java; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import org.checkerframework.common.value.qual.IntRange; import org.geysermc.geyser.api.GeyserApi; @@ -85,7 +84,7 @@ public interface JavaAttackRange { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(Builder.class); } diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaConsumable.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaConsumable.java index 740dc0a784d..bd4c196125c 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaConsumable.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaConsumable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -26,7 +26,6 @@ package org.geysermc.geyser.api.item.custom.v2.component.java; import org.checkerframework.checker.index.qual.Positive; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.GenericBuilder; @@ -54,7 +53,7 @@ public interface JavaConsumable { * @return the animation to play * @since 2.9.3 */ - @NonNull Animation animation(); + Animation animation(); /** * Creates a builder for the consumable component. @@ -62,7 +61,7 @@ public interface JavaConsumable { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(JavaConsumable.Builder.class); } @@ -74,7 +73,7 @@ public interface JavaConsumable { * @return the consumable component * @since 2.9.3 */ - static @NonNull JavaConsumable of(float consumeSeconds, Animation animation) { + static JavaConsumable of(float consumeSeconds, Animation animation) { return JavaConsumable.builder().consumeSeconds(consumeSeconds).animation(animation).build(); } @@ -129,7 +128,7 @@ enum Animation { * Brush in 1st and 3rd person. Will look weird when not displayed handheld. * @since 2.9.3 */ - BRUSH; + BRUSH } /** @@ -160,7 +159,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder animation(@NonNull Animation animation); + Builder animation(Animation animation); /** * Creates the consumable component. diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaEquippable.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaEquippable.java index 26fec4f8a58..6e4cf38b624 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaEquippable.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaEquippable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.item.custom.v2.component.java; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.GenericBuilder; @@ -43,14 +42,14 @@ public interface JavaEquippable { * * @return the equipment slot */ - @NonNull EquipmentSlot slot(); + EquipmentSlot slot(); /** * Creates a builder for the equippable component. * * @return a new builder */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(JavaEquippable.Builder.class); } @@ -60,7 +59,7 @@ public interface JavaEquippable { * @param slot the slot in which the item can be equipped * @return the Equippable component */ - static @NonNull JavaEquippable of(EquipmentSlot slot) { + static JavaEquippable of(EquipmentSlot slot) { return builder().slot(slot).build(); } @@ -77,7 +76,7 @@ interface Builder extends GenericBuilder { * @return this builder */ @This - Builder slot(@NonNull EquipmentSlot slot); + Builder slot(EquipmentSlot slot); /** * Creates the equippable component. diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaFoodProperties.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaFoodProperties.java index 1560d97f439..7984f6a64e5 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaFoodProperties.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaFoodProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -26,7 +26,6 @@ package org.geysermc.geyser.api.item.custom.v2.component.java; import org.checkerframework.checker.index.qual.NonNegative; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.GenericBuilder; @@ -71,7 +70,7 @@ public interface JavaFoodProperties { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(JavaFoodProperties.Builder.class); } @@ -84,7 +83,7 @@ public interface JavaFoodProperties { * @return the food properties component * @since 2.9.3 */ - static @NonNull JavaFoodProperties of(int nutrition, float saturation, boolean canAlwaysEat) { + static JavaFoodProperties of(int nutrition, float saturation, boolean canAlwaysEat) { return JavaFoodProperties.builder().nutrition(nutrition).saturation(saturation).canAlwaysEat(canAlwaysEat).build(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaKineticWeapon.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaKineticWeapon.java index 96b07f9af7b..1ae579bdbca 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaKineticWeapon.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaKineticWeapon.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -26,11 +26,10 @@ package org.geysermc.geyser.api.item.custom.v2.component.java; import org.checkerframework.checker.index.qual.NonNegative; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.GenericBuilder; +import org.jspecify.annotations.Nullable; /** * The kinetic weapon component is used to specify a spear-like attack when the item is in use. @@ -60,7 +59,7 @@ public interface JavaKineticWeapon { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(Builder.class); } @@ -73,7 +72,7 @@ public interface JavaKineticWeapon { * @return the new {@link Condition} * @since 2.9.3 */ - static @NonNull Condition condition(@NonNegative int maxDurationTicks) { + static Condition condition(@NonNegative int maxDurationTicks) { return condition(maxDurationTicks, 0.0F, 0.0F); } @@ -90,7 +89,7 @@ public interface JavaKineticWeapon { * @return the new {@link Condition} * @since 2.9.3 */ - static @NonNull Condition condition(@NonNegative int maxDurationTicks, float minSpeed, float minRelativeSpeed) { + static Condition condition(@NonNegative int maxDurationTicks, float minSpeed, float minRelativeSpeed) { return Condition.builder(maxDurationTicks) .minSpeed(minSpeed) .minRelativeSpeed(minRelativeSpeed) @@ -119,7 +118,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - default Builder dismountConditions(Condition.@NonNull Builder dismountConditions) { + default Builder dismountConditions(Condition.Builder dismountConditions) { return dismountConditions(dismountConditions.build()); } @@ -183,7 +182,7 @@ interface Condition { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder(@NonNegative int maxDurationTicks) { + static Builder builder(@NonNegative int maxDurationTicks) { return GeyserApi.api().provider(Builder.class, maxDurationTicks); } diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaPiercingWeapon.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaPiercingWeapon.java index b7fb920adab..a2d017cec06 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaPiercingWeapon.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaPiercingWeapon.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.item.custom.v2.component.java; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.GeyserApi; /** @@ -38,7 +37,7 @@ public interface JavaPiercingWeapon { * @return the piercing weapon component * @since 2.9.3 */ - static @NonNull JavaPiercingWeapon instance() { + static JavaPiercingWeapon instance() { return GeyserApi.api().provider(JavaPiercingWeapon.class); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaRepairable.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaRepairable.java index 611713343ad..edc0225a805 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaRepairable.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaRepairable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.item.custom.v2.component.java; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.GenericBuilder; @@ -44,7 +43,7 @@ public interface JavaRepairable { * @return the {@link Holders} of item identifiers * @since 2.9.3 */ - @NonNull Holders items(); + Holders items(); /** * Creates a builder for the repairable component. @@ -52,7 +51,7 @@ public interface JavaRepairable { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(JavaRepairable.Builder.class); } @@ -64,7 +63,7 @@ public interface JavaRepairable { * @return the repairable component * @since 2.9.3 */ - static @NonNull JavaRepairable of(@NonNull Holders items) { + static JavaRepairable of(Holders items) { return JavaRepairable.builder().items(items).build(); } @@ -83,7 +82,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder items(@NonNull Holders items); + Builder items(Holders items); /** * Creates the repairable component. diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaSwingAnimation.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaSwingAnimation.java index 8e5c58a6199..dcea9b369c1 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaSwingAnimation.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaSwingAnimation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -26,7 +26,6 @@ package org.geysermc.geyser.api.item.custom.v2.component.java; import org.checkerframework.checker.index.qual.Positive; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.GenericBuilder; @@ -53,7 +52,7 @@ public interface JavaSwingAnimation { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(Builder.class); } @@ -64,7 +63,7 @@ public interface JavaSwingAnimation { * @return the new swing animation component * @since 2.9.3 */ - static @NonNull JavaSwingAnimation of(@Positive int duration) { + static JavaSwingAnimation of(@Positive int duration) { return builder() .duration(duration) .build(); diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaTool.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaTool.java index 611e2827f56..fa717c9a7d8 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaTool.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaTool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -26,7 +26,6 @@ package org.geysermc.geyser.api.item.custom.v2.component.java; import org.checkerframework.checker.index.qual.Positive; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.GenericBuilder; @@ -50,7 +49,7 @@ public interface JavaTool { * @return a list of rules this tool has * @since 2.9.3 */ - List<@NonNull Rule> rules(); + List rules(); /** * The default mining speed of the tool. This speed is used when no rules match when breaking a block. @@ -76,7 +75,7 @@ public interface JavaTool { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(JavaTool.Builder.class); } @@ -87,7 +86,7 @@ public interface JavaTool { * @return a tool component * @since 2.9.3 */ - static @NonNull JavaTool of(boolean canDestroyBlocksInCreative) { + static JavaTool of(boolean canDestroyBlocksInCreative) { return builder().canDestroyBlocksInCreative(canDestroyBlocksInCreative).build(); } @@ -106,7 +105,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder rule(@NonNull Rule rule); + Builder rule(Rule rule); /** * Sets the default mining speed of this tool. Vanilla-item overrides don't need a speed set. @@ -151,7 +150,7 @@ interface Rule { * @return the {@link Holders} of block identifiers that this rule is for * @since 2.9.3 */ - @NonNull Holders blocks(); + Holders blocks(); /** * @return the speed to use when mining a block that matches this rule @@ -165,7 +164,7 @@ interface Rule { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(Rule.Builder.class); } @@ -177,7 +176,7 @@ interface Rule { * @return a tool rule * @since 2.9.3 */ - static @NonNull Rule of(@NonNull Holders blocks, @Positive float speed) { + static Rule of(Holders blocks, @Positive float speed) { return Rule.builder().blocks(blocks).speed(speed).build(); } @@ -195,7 +194,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder blocks(@NonNull Holders blocks); + Builder blocks(Holders blocks); /** * Sets the speed to use when mining a block that matches this rule diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaUseCooldown.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaUseCooldown.java index 949e6885f27..ca56b169cf3 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaUseCooldown.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaUseCooldown.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -26,12 +26,11 @@ package org.geysermc.geyser.api.item.custom.v2.component.java; import org.checkerframework.checker.index.qual.Positive; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.util.GenericBuilder; import org.geysermc.geyser.api.util.Identifier; +import org.jspecify.annotations.Nullable; /** * The use cooldown component is used to add an item use cooldown to items. @@ -65,7 +64,7 @@ public interface JavaUseCooldown { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(JavaUseCooldown.Builder.class); } diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaUseEffects.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaUseEffects.java index b98658636b3..cf44a46a843 100644 --- a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaUseEffects.java +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/JavaUseEffects.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.item.custom.v2.component.java; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import org.checkerframework.common.value.qual.IntRange; import org.geysermc.geyser.api.GeyserApi; @@ -53,7 +52,7 @@ public interface JavaUseEffects { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(Builder.class); } @@ -64,7 +63,7 @@ public interface JavaUseEffects { * @return the new use effects component * @since 2.9.3 */ - static @NonNull JavaUseEffects of(@IntRange(from = 0, to = 1) float speedMultiplier) { + static JavaUseEffects of(@IntRange(from = 0, to = 1) float speedMultiplier) { return builder() .speedMultiplier(speedMultiplier) .build(); diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/package-info.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/package-info.java new file mode 100644 index 00000000000..92b92acea2d --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/java/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.item.custom.v2.component.java; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/package-info.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/package-info.java new file mode 100644 index 00000000000..ed72e7d9677 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/component/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.item.custom.v2.component; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/package-info.java b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/package-info.java new file mode 100644 index 00000000000..5a2cdcb66fa --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/item/custom/v2/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.item.custom.v2; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java b/api/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java index af35d7ad193..ee1debb0109 100644 --- a/api/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java +++ b/api/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,8 +25,6 @@ package org.geysermc.geyser.api.network; -import org.checkerframework.checker.nullness.qual.NonNull; - /** * The listener that handles connections from Minecraft: * Bedrock Edition. @@ -39,7 +37,6 @@ public interface BedrockListener { * * @return the listening address */ - @NonNull String address(); /** diff --git a/api/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java b/api/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java index 3e72ccd4466..3624c680e01 100644 --- a/api/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java +++ b/api/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,8 +25,6 @@ package org.geysermc.geyser.api.network; -import org.checkerframework.checker.nullness.qual.NonNull; - /** * Represents the Java server that Geyser is connecting to. */ @@ -65,7 +63,6 @@ public interface RemoteServer { * * @return the auth type required by the remote server */ - @NonNull AuthType authType(); /** diff --git a/api/src/main/java/org/geysermc/geyser/api/network/package-info.java b/api/src/main/java/org/geysermc/geyser/api/network/package-info.java new file mode 100644 index 00000000000..5fa48eeb0be --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/network/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.network; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/PackCodec.java b/api/src/main/java/org/geysermc/geyser/api/pack/PackCodec.java index b6626aa6a7e..2c45131264c 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/PackCodec.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/PackCodec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.pack; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.GeyserApi; import java.io.IOException; @@ -45,7 +44,7 @@ public abstract class PackCodec { * @return the hash of the resource pack * @since 2.1.1 */ - public abstract byte @NonNull [] sha256(); + public abstract byte[] sha256(); /** * Gets the resource pack size. @@ -59,10 +58,9 @@ public abstract class PackCodec { * @deprecated use {@link #serialize()} instead. */ @Deprecated - @NonNull - public SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws IOException { + public SeekableByteChannel serialize(ResourcePack resourcePack) throws IOException { return serialize(); - }; + } /** * Serializes the given codec into a byte buffer. @@ -70,7 +68,6 @@ public SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws * @return the serialized resource pack * @since 2.6.2 */ - @NonNull public abstract SeekableByteChannel serialize() throws IOException; /** @@ -79,7 +76,6 @@ public SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws * @return the new resource pack * @since 2.1.1 */ - @NonNull protected abstract ResourcePack create(); /** @@ -88,7 +84,7 @@ public SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws * @return the new resource pack builder * @since 2.6.2 */ - protected abstract ResourcePack.@NonNull Builder createBuilder(); + protected abstract ResourcePack.Builder createBuilder(); /** * Creates a new pack provider from the given path. @@ -97,8 +93,7 @@ public SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws * @return the new pack provider * @since 2.1.1 */ - @NonNull - public static PackCodec path(@NonNull Path path) { + public static PackCodec path(Path path) { return GeyserApi.api().provider(PathPackCodec.class, path); } @@ -109,8 +104,7 @@ public static PackCodec path(@NonNull Path path) { * @return the new pack provider * @since 2.6.2 */ - @NonNull - public static PackCodec url(@NonNull String url) { + public static PackCodec url(String url) { return GeyserApi.api().provider(UrlPackCodec.class, url); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/PathPackCodec.java b/api/src/main/java/org/geysermc/geyser/api/pack/PathPackCodec.java index d6d668fb2b2..8f6a53a6909 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/PathPackCodec.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/PathPackCodec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,8 +25,6 @@ package org.geysermc.geyser.api.pack; -import org.checkerframework.checker.nullness.qual.NonNull; - import java.nio.file.Path; /** @@ -42,6 +40,5 @@ public abstract class PathPackCodec extends PackCodec { * @return the path of the resource pack * @since 2.1.1 */ - @NonNull public abstract Path path(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/ResourcePack.java b/api/src/main/java/org/geysermc/geyser/api/pack/ResourcePack.java index a8572070d79..09edd52049c 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/ResourcePack.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/ResourcePack.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.pack; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.returnsreceiver.qual.This; import java.util.UUID; @@ -45,7 +44,6 @@ public interface ResourcePack { * @return the codec for this pack * @since 2.1.1 */ - @NonNull PackCodec codec(); /** @@ -54,7 +52,6 @@ public interface ResourcePack { * @return the resource pack manifest * @since 2.1.1 */ - @NonNull ResourcePackManifest manifest(); /** @@ -63,7 +60,6 @@ public interface ResourcePack { * @return the content key of the resource pack * @since 2.1.1 */ - @NonNull String contentKey(); /** @@ -72,7 +68,6 @@ public interface ResourcePack { * @return the resource pack uuid * @since 2.6.2 */ - @NonNull default UUID uuid() { return manifest().header().uuid(); } @@ -84,8 +79,7 @@ default UUID uuid() { * @return the resource pack * @since 2.1.1 */ - @NonNull - static ResourcePack create(@NonNull PackCodec codec) { + static ResourcePack create(PackCodec codec) { return codec.create(); } @@ -97,7 +91,7 @@ static ResourcePack create(@NonNull PackCodec codec) { * @return a {@link Builder} to build a resource pack * @since 2.6.2 */ - static Builder builder(@NonNull PackCodec codec) { + static Builder builder(PackCodec codec) { return codec.createBuilder(); } @@ -132,7 +126,7 @@ interface Builder { * @return this builder * @since 2.6.2 */ - @This Builder contentKey(@NonNull String contentKey); + @This Builder contentKey(String contentKey); /** * @return the resource pack diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/ResourcePackManifest.java b/api/src/main/java/org/geysermc/geyser/api/pack/ResourcePackManifest.java index 737682e2287..e585c852ba8 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/ResourcePackManifest.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/ResourcePackManifest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,11 +25,10 @@ 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 org.jspecify.annotations.Nullable; import java.util.Collection; import java.util.UUID; @@ -60,7 +59,6 @@ public interface ResourcePackManifest { * @return the {@link Header} * @since 2.1.1 */ - @NonNull Header header(); /** @@ -69,7 +67,6 @@ public interface ResourcePackManifest { * @return a collection of modules * @since 2.1.1 */ - @NonNull Collection modules(); /** @@ -78,7 +75,6 @@ public interface ResourcePackManifest { * @return a collection of dependencies * @since 2.6.2 */ - @NonNull Collection dependencies(); /** @@ -89,7 +85,6 @@ public interface ResourcePackManifest { * @return a collection of subpacks * @since 2.6.2 */ - @NonNull Collection subpacks(); /** @@ -100,7 +95,6 @@ public interface ResourcePackManifest { * @return a collection of settings * @since 2.6.2 */ - @NonNull Collection settings(); /** @@ -120,7 +114,6 @@ interface Header { * @return the UUID * @since 2.1.1 */ - @NonNull UUID uuid(); /** @@ -129,7 +122,6 @@ interface Header { * @return the version * @since 2.1.1 */ - @NonNull Version version(); /** @@ -138,7 +130,6 @@ interface Header { * @return the name * @since 2.1.1 */ - @NonNull String name(); /** @@ -147,7 +138,6 @@ interface Header { * @return the description * @since 2.1.1 */ - @NonNull String description(); /** @@ -156,7 +146,6 @@ interface Header { * @return the minimum supported Minecraft version * @since 2.1.1 */ - @NonNull Version minimumSupportedMinecraftVersion(); } @@ -177,7 +166,6 @@ interface Module { * @return the UUID * @since 2.1.1 */ - @NonNull UUID uuid(); /** @@ -186,7 +174,6 @@ interface Module { * @return the {@link Version} * @since 2.1.1 */ - @NonNull Version version(); /** @@ -195,7 +182,6 @@ interface Module { * @return the type * @since 2.1.1 */ - @NonNull String type(); /** @@ -204,7 +190,6 @@ interface Module { * @return the description * @since 2.1.1 */ - @NonNull String description(); } @@ -224,7 +209,6 @@ interface Dependency { * @return the uuid * @since 2.1.1 */ - @NonNull UUID uuid(); /** @@ -233,7 +217,6 @@ interface Dependency { * @return the {@link Version} * @since 2.1.1 */ - @NonNull Version version(); } @@ -250,7 +233,6 @@ interface Subpack { * @return the folder name * @since 2.6.2 */ - @NonNull String folderName(); /** @@ -262,7 +244,6 @@ interface Subpack { * @return the subpack name * @since 2.6.2 */ - @NonNull String name(); /** @@ -273,8 +254,7 @@ interface Subpack { * @return the memory tier * @since 2.6.2 */ - @Nullable - Float memoryTier(); + @Nullable Float memoryTier(); } /** @@ -290,7 +270,6 @@ interface Setting { * @return the type * @since 2.6.2 */ - @NonNull String type(); /** @@ -299,7 +278,6 @@ interface Setting { * @return the text content * @since 2.6.2 */ - @NonNull String text(); } @@ -339,7 +317,7 @@ interface Version { * @return the version string * @since 2.1.1 */ - @NonNull String toString(); + String toString(); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/UrlPackCodec.java b/api/src/main/java/org/geysermc/geyser/api/pack/UrlPackCodec.java index e729e3fbb8e..20c5b218c39 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/UrlPackCodec.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/UrlPackCodec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,8 +25,6 @@ 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. @@ -46,6 +44,5 @@ public abstract class UrlPackCodec extends PackCodec { * @return the URL of the resource pack * @since 2.6.2 */ - @NonNull public abstract String url(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/exception/package-info.java b/api/src/main/java/org/geysermc/geyser/api/pack/exception/package-info.java new file mode 100644 index 00000000000..d30c86856dd --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/pack/exception/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.pack.exception; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/option/ResourcePackOption.java b/api/src/main/java/org/geysermc/geyser/api/pack/option/ResourcePackOption.java index a86ad88db22..9511578c96f 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/option/ResourcePackOption.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/option/ResourcePackOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 GeyserMC. http://geysermc.org + * Copyright (c) 2024-2026 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 @@ -25,7 +25,6 @@ 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; @@ -45,13 +44,13 @@ public interface ResourcePackOption { * @return the option type * @since 2.6.2 */ - @NonNull Type type(); + Type type(); /** * @return the value of the option * @since 2.6.2 */ - @NonNull T value(); + T value(); /** * Used to validate a specific options for a pack. @@ -61,7 +60,7 @@ public interface ResourcePackOption { * @throws ResourcePackException with the {@link ResourcePackException.Cause#INVALID_PACK_OPTION} cause * @since 2.6.2 */ - void validate(@NonNull ResourcePack pack); + void validate(ResourcePack pack); /** * Represents the different types of resource pack options. diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/option/SubpackOption.java b/api/src/main/java/org/geysermc/geyser/api/pack/option/SubpackOption.java index a0462afa7fa..dc934cfdbdd 100644 --- a/api/src/main/java/org/geysermc/geyser/api/pack/option/SubpackOption.java +++ b/api/src/main/java/org/geysermc/geyser/api/pack/option/SubpackOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 GeyserMC. http://geysermc.org + * Copyright (c) 2024-2026 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 @@ -25,7 +25,6 @@ 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; @@ -43,7 +42,7 @@ public interface SubpackOption extends ResourcePackOption { * @return a subpack option specifying that subpack * @since 2.6.2 */ - static SubpackOption subpack(ResourcePackManifest.@NonNull Subpack subpack) { + static SubpackOption subpack(ResourcePackManifest.Subpack subpack) { return named(subpack.name()); } @@ -54,7 +53,7 @@ static SubpackOption subpack(ResourcePackManifest.@NonNull Subpack subpack) { * @return a subpack option specifying a subpack with that name * @since 2.6.2 */ - static SubpackOption named(@NonNull String subpackName) { + static SubpackOption named(String subpackName) { return GeyserApi.api().provider(SubpackOption.class, subpackName); } diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/option/package-info.java b/api/src/main/java/org/geysermc/geyser/api/pack/option/package-info.java new file mode 100644 index 00000000000..e9bfe727727 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/pack/option/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.pack.option; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/pack/package-info.java b/api/src/main/java/org/geysermc/geyser/api/pack/package-info.java new file mode 100644 index 00000000000..ba8fa74fbe0 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/pack/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.pack; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/package-info.java b/api/src/main/java/org/geysermc/geyser/api/package-info.java new file mode 100644 index 00000000000..743de821e66 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/permission/PermissionChecker.java b/api/src/main/java/org/geysermc/geyser/api/permission/PermissionChecker.java index c0d4af2f4ea..5d1ce486c44 100644 --- a/api/src/main/java/org/geysermc/geyser/api/permission/PermissionChecker.java +++ b/api/src/main/java/org/geysermc/geyser/api/permission/PermissionChecker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.permission; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.command.CommandSource; import org.geysermc.geyser.api.util.TriState; @@ -44,6 +43,5 @@ public interface PermissionChecker { * node itself was not found, and the source does not have such permission. * {@link TriState#TRUE} and {@link TriState#FALSE} represent explicitly set values. */ - @NonNull - TriState hasPermission(@NonNull CommandSource source, @NonNull String permission); + TriState hasPermission(CommandSource source, String permission); } diff --git a/api/src/main/java/org/geysermc/geyser/api/permission/package-info.java b/api/src/main/java/org/geysermc/geyser/api/permission/package-info.java new file mode 100644 index 00000000000..68f687c5b23 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/permission/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.permission; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/predicate/DimensionPredicate.java b/api/src/main/java/org/geysermc/geyser/api/predicate/DimensionPredicate.java index 5e6d6e98010..51b48fd1aca 100644 --- a/api/src/main/java/org/geysermc/geyser/api/predicate/DimensionPredicate.java +++ b/api/src/main/java/org/geysermc/geyser/api/predicate/DimensionPredicate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.predicate; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.predicate.context.MinecraftPredicateContext; import org.geysermc.geyser.api.util.GeyserProvided; import org.geysermc.geyser.api.util.Identifier; @@ -50,7 +49,7 @@ public interface DimensionPredicate extends MinecraftPredicate dimension(@NonNull Identifier dimension) { + static MinecraftPredicate dimension(Identifier dimension) { return GeyserApi.api().provider(DimensionPredicate.class, dimension, false); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/predicate/MinecraftPredicate.java b/api/src/main/java/org/geysermc/geyser/api/predicate/MinecraftPredicate.java index 1724ec6f1b7..26f72b86793 100644 --- a/api/src/main/java/org/geysermc/geyser/api/predicate/MinecraftPredicate.java +++ b/api/src/main/java/org/geysermc/geyser/api/predicate/MinecraftPredicate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 GeyserMC. http://geysermc.org + * Copyright (c) 2024-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.predicate; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.item.custom.v2.CustomItemDefinition; import org.geysermc.geyser.api.predicate.context.MinecraftPredicateContext; @@ -47,19 +46,19 @@ public interface MinecraftPredicate extends Predicate { @Override - default @NonNull MinecraftPredicate and(@NonNull Predicate other) { + default MinecraftPredicate and(Predicate other) { Objects.requireNonNull(other); return (context) -> this.test(context) && other.test(context); } @Override - default @NonNull MinecraftPredicate negate() { + default MinecraftPredicate negate() { return (context) -> !this.test(context); } @Override - default @NonNull MinecraftPredicate or(@NonNull Predicate other) { + default MinecraftPredicate or(Predicate other) { Objects.requireNonNull(other); - return (context) -> this.test(context) || other.test(context); + return context -> this.test(context) || other.test(context); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/predicate/context/item/ChargedProjectile.java b/api/src/main/java/org/geysermc/geyser/api/predicate/context/item/ChargedProjectile.java index 1a27a2f7914..48988f6ff2e 100644 --- a/api/src/main/java/org/geysermc/geyser/api/predicate/context/item/ChargedProjectile.java +++ b/api/src/main/java/org/geysermc/geyser/api/predicate/context/item/ChargedProjectile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -26,7 +26,6 @@ package org.geysermc.geyser.api.predicate.context.item; import org.checkerframework.checker.index.qual.Positive; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.GeyserApi; import org.jetbrains.annotations.ApiStatus; @@ -43,7 +42,7 @@ public interface ChargedProjectile { * @return the type of the projectile * @since 2.9.3 */ - @NonNull ChargeType type(); + ChargeType type(); /** * @return the amount present of this projectile @@ -59,7 +58,7 @@ public interface ChargedProjectile { * @return the charged projectile * @since 2.9.3 */ - static ChargedProjectile of(@NonNull ChargeType type, @Positive int count) { + static ChargedProjectile of(ChargeType type, @Positive int count) { return GeyserApi.api().provider(ChargedProjectile.class, type, count); } diff --git a/api/src/main/java/org/geysermc/geyser/api/predicate/context/item/ItemPredicateContext.java b/api/src/main/java/org/geysermc/geyser/api/predicate/context/item/ItemPredicateContext.java index 5f0f186d0f5..2683562ea18 100644 --- a/api/src/main/java/org/geysermc/geyser/api/predicate/context/item/ItemPredicateContext.java +++ b/api/src/main/java/org/geysermc/geyser/api/predicate/context/item/ItemPredicateContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,12 +25,11 @@ package org.geysermc.geyser.api.predicate.context.item; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.item.custom.v2.CustomItemDefinition; import org.geysermc.geyser.api.predicate.context.MinecraftPredicateContext; import org.geysermc.geyser.api.util.Identifier; import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.Nullable; import java.util.List; @@ -93,13 +92,13 @@ public interface ItemPredicateContext extends MinecraftPredicateContext { * @return all the charged projectiles in the {@code minecraft:charged_projectiles} component * @since 2.9.3 */ - @NonNull List<@NonNull ChargedProjectile> chargedProjectiles(); + List chargedProjectiles(); /** * @return a list of all the components present on the item, including default components * @since 2.9.3 */ - @NonNull List<@NonNull Identifier> components(); + List components(); /** * @param index the flag index diff --git a/api/src/main/java/org/geysermc/geyser/api/predicate/context/item/package-info.java b/api/src/main/java/org/geysermc/geyser/api/predicate/context/item/package-info.java new file mode 100644 index 00000000000..e567ef54330 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/predicate/context/item/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.predicate.context.item; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/predicate/context/package-info.java b/api/src/main/java/org/geysermc/geyser/api/predicate/context/package-info.java new file mode 100644 index 00000000000..078d6ec6ec5 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/predicate/context/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.predicate.context; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/org/geysermc/geyser/api/predicate/item/ChargeTypePredicate.java b/api/src/main/java/org/geysermc/geyser/api/predicate/item/ChargeTypePredicate.java index 07e44971654..c36f37f3776 100644 --- a/api/src/main/java/org/geysermc/geyser/api/predicate/item/ChargeTypePredicate.java +++ b/api/src/main/java/org/geysermc/geyser/api/predicate/item/ChargeTypePredicate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.predicate.item; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.predicate.MinecraftPredicate; import org.geysermc.geyser.api.predicate.context.item.ChargedProjectile; import org.geysermc.geyser.api.predicate.context.item.ItemPredicateContext; @@ -44,7 +43,7 @@ public interface ChargeTypePredicate extends MinecraftPredicate customModelData(@NonNegative int * @see HasComponentPredicate * @since 2.9.3 */ - static MinecraftPredicate hasComponent(@NonNull Identifier component) { + static MinecraftPredicate hasComponent(Identifier component) { return GeyserApi.api().provider(HasComponentPredicate.class, component); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/predicate/item/ItemMatchPredicate.java b/api/src/main/java/org/geysermc/geyser/api/predicate/item/ItemMatchPredicate.java index a107f95f5b7..1eb3c3779ee 100644 --- a/api/src/main/java/org/geysermc/geyser/api/predicate/item/ItemMatchPredicate.java +++ b/api/src/main/java/org/geysermc/geyser/api/predicate/item/ItemMatchPredicate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -26,8 +26,6 @@ package org.geysermc.geyser.api.predicate.item; import org.checkerframework.checker.index.qual.NonNegative; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.predicate.MatchPredicate; import org.geysermc.geyser.api.predicate.MinecraftPredicate; @@ -35,6 +33,7 @@ import org.geysermc.geyser.api.predicate.context.item.ItemPredicateContext; import org.geysermc.geyser.api.util.Identifier; import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.Nullable; /** * Contains factories for often-used "match" predicates, that match for a value in {@link ItemPredicateContext}. @@ -53,7 +52,7 @@ public interface ItemMatchPredicate extends MatchPredicate { * @see ChargeTypePredicate * @since 2.9.3 */ - static MinecraftPredicate chargeType(ChargedProjectile.@NonNull ChargeType type) { + static MinecraftPredicate chargeType(ChargedProjectile.ChargeType type) { return GeyserApi.api().provider(ChargeTypePredicate.class, type); } @@ -64,7 +63,7 @@ static MinecraftPredicate chargeType(ChargedProjectile.@No * @see TrimMaterialPredicate * @since 2.9.3 */ - static MinecraftPredicate trimMaterial(@NonNull Identifier material) { + static MinecraftPredicate trimMaterial(Identifier material) { return GeyserApi.api().provider(TrimMaterialPredicate.class, material); } diff --git a/api/src/main/java/org/geysermc/geyser/api/predicate/item/RangeDispatchPredicate.java b/api/src/main/java/org/geysermc/geyser/api/predicate/item/RangeDispatchPredicate.java index 5663e543b8c..2c857df0133 100644 --- a/api/src/main/java/org/geysermc/geyser/api/predicate/item/RangeDispatchPredicate.java +++ b/api/src/main/java/org/geysermc/geyser/api/predicate/item/RangeDispatchPredicate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 GeyserMC. http://geysermc.org + * Copyright (c) 2025-2026 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 @@ -26,7 +26,6 @@ package org.geysermc.geyser.api.predicate.item; import org.checkerframework.checker.index.qual.NonNegative; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.predicate.MinecraftPredicate; import org.geysermc.geyser.api.predicate.context.item.ItemPredicateContext; import org.geysermc.geyser.api.util.GeyserProvided; @@ -48,7 +47,7 @@ public interface RangeDispatchPredicate extends MinecraftPredicate identifiers) { + static Holders of(List identifiers) { Builder builder = builder(); identifiers.forEach(builder::with); return builder.build(); @@ -71,7 +70,7 @@ public interface Holders { * @return a new Holders object * @since 2.9.3 */ - static @NonNull Holders ofTag(Identifier tag) { + static Holders ofTag(Identifier tag) { return builder().tag(tag).build(); } @@ -81,7 +80,7 @@ public interface Holders { * @return a new builder * @since 2.9.3 */ - static @NonNull Builder builder() { + static Builder builder() { return GeyserApi.api().provider(Holders.Builder.class); } @@ -101,7 +100,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder with(@NonNull Identifier identifier); + Builder with(Identifier identifier); /** * Sets the tag of the Holders object. A Holders object can only consist of one tag. This will throw when at least one identifier has been @@ -113,7 +112,7 @@ interface Builder extends GenericBuilder { * @since 2.9.3 */ @This - Builder tag(@NonNull Identifier tag); + Builder tag(Identifier tag); /** * Creates the Holders object. diff --git a/api/src/main/java/org/geysermc/geyser/api/util/Identifier.java b/api/src/main/java/org/geysermc/geyser/api/util/Identifier.java index 069485611f2..37893cf5555 100644 --- a/api/src/main/java/org/geysermc/geyser/api/util/Identifier.java +++ b/api/src/main/java/org/geysermc/geyser/api/util/Identifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 GeyserMC. http://geysermc.org + * Copyright (c) 2024-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.util; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.GeyserApi; import org.jetbrains.annotations.ApiStatus; @@ -69,7 +68,7 @@ public interface Identifier { * @throws IllegalArgumentException if either namespace or path are invalid. * @since 2.9.0 */ - static Identifier of(@NonNull String namespace, @NonNull String path) { + static Identifier of(String namespace, String path) { return GeyserApi.api().provider(Identifier.class, namespace, path); } diff --git a/api/src/main/java/org/geysermc/geyser/api/util/MinecraftVersion.java b/api/src/main/java/org/geysermc/geyser/api/util/MinecraftVersion.java index 26d6de7baae..afe969eac23 100644 --- a/api/src/main/java/org/geysermc/geyser/api/util/MinecraftVersion.java +++ b/api/src/main/java/org/geysermc/geyser/api/util/MinecraftVersion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2025 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.api.util; -import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.ApiStatus; /** @@ -40,7 +39,7 @@ public interface MinecraftVersion { * * @return the version string */ - @NonNull String versionString(); + String versionString(); /** * Gets the protocol version of this Minecraft version. diff --git a/api/src/main/java/org/geysermc/geyser/api/util/TriState.java b/api/src/main/java/org/geysermc/geyser/api/util/TriState.java index a8bb723d6c7..b8ce2597991 100644 --- a/api/src/main/java/org/geysermc/geyser/api/util/TriState.java +++ b/api/src/main/java/org/geysermc/geyser/api/util/TriState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2026 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 @@ -25,8 +25,7 @@ package org.geysermc.geyser.api.util; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * This is a way to represent a boolean, but with a non set value added. @@ -67,7 +66,7 @@ public enum TriState { * @param value the Boolean value * @return the created TriState */ - public static @NonNull TriState fromBoolean(@Nullable Boolean value) { + public static TriState fromBoolean(@Nullable Boolean value) { return value == null ? NOT_SET : fromBoolean(value.booleanValue()); } @@ -77,7 +76,7 @@ public enum TriState { * @param value the boolean value * @return the created TriState */ - public @NonNull static TriState fromBoolean(boolean value) { + public static TriState fromBoolean(boolean value) { return value ? TRUE : FALSE; } } diff --git a/api/src/main/java/org/geysermc/geyser/api/util/package-info.java b/api/src/main/java/org/geysermc/geyser/api/util/package-info.java new file mode 100644 index 00000000000..2ebaceec343 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/util/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 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 + */ + +@NullMarked +package org.geysermc.geyser.api.util; + +import org.jspecify.annotations.NullMarked; diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePingPassthrough.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePingPassthrough.java index fb56d92c273..17c402037db 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePingPassthrough.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePingPassthrough.java @@ -90,8 +90,8 @@ private static ListenerInfo getDefaultListener() { private ServerPing getPingInfo() { return new ServerPing( new ServerPing.Protocol( - proxyServer.getName() + " " + ProtocolConstants.SUPPORTED_VERSIONS.get(0) + "-" + ProtocolConstants.SUPPORTED_VERSIONS.get(ProtocolConstants.SUPPORTED_VERSIONS.size() - 1), - ProtocolConstants.SUPPORTED_VERSION_IDS.get(ProtocolConstants.SUPPORTED_VERSION_IDS.size() - 1)), + proxyServer.getName() + " " + ProtocolConstants.SUPPORTED_VERSIONS.getFirst() + "-" + ProtocolConstants.SUPPORTED_VERSIONS.getLast(), + ProtocolConstants.SUPPORTED_VERSION_IDS.getLast()), new ServerPing.Players(getDefaultListener().getMaxPlayers(), proxyServer.getOnlineCount(), null), TextComponent.fromLegacyText(getDefaultListener().getMotd())[0], proxyServer.getConfig().getFaviconObject() @@ -115,7 +115,7 @@ public String getName() { @Override public int getVersion() { - return ProtocolConstants.SUPPORTED_VERSION_IDS.get(ProtocolConstants.SUPPORTED_VERSION_IDS.size() - 1); + return ProtocolConstants.SUPPORTED_VERSION_IDS.getLast(); } @Override diff --git a/bootstrap/mod/build.gradle.kts b/bootstrap/mod/build.gradle.kts index c43f123ec18..e8f568a0be5 100644 --- a/bootstrap/mod/build.gradle.kts +++ b/bootstrap/mod/build.gradle.kts @@ -6,13 +6,9 @@ architectury { common("neoforge", "fabric") } -loom { - mixin.defaultRefmapName.set("geyser-refmap.json") -} - afterEvaluate { // We don't need these - tasks.named("remapModrinthJar").configure { + tasks.named("mergeShadowAndJarJar").configure { enabled = false } } diff --git a/bootstrap/mod/fabric/build.gradle.kts b/bootstrap/mod/fabric/build.gradle.kts index 6b77303cf4a..97f2bb9d8e4 100644 --- a/bootstrap/mod/fabric/build.gradle.kts +++ b/bootstrap/mod/fabric/build.gradle.kts @@ -19,10 +19,10 @@ loom { } dependencies { - modImplementation(libs.fabric.loader) - modApi(libs.fabric.api) + implementation(libs.fabric.loader) + api(libs.fabric.api) - api(project(":mod", configuration = "namedElements")) + api(project(":mod")) shadowBundle(project(path = ":mod", configuration = "transformProductionFabric")) shadowBundle(projects.core) includeTransitive(projects.core) @@ -47,7 +47,7 @@ dependencies { shadowBundle(projects.api) shadowBundle(projects.common) - modImplementation(libs.cloud.fabric) + implementation(libs.cloud.fabric) include(libs.cloud.fabric) include(libs.fabric.permissions.api) } @@ -60,23 +60,48 @@ relocate("org.cloudburstmc.netty") relocate("org.cloudburstmc.protocol") relocate("org.spongepowered.configurate") -tasks { - remapJar { - archiveBaseName.set("Geyser-Fabric") +fabricApi { + configureTests { + createSourceSet = true + modId = "geyser-gametest" + enableClientGameTests = false + eula = true } +} - remapModrinthJar { - archiveBaseName.set("geyser-fabric") +tasks { + named("mergeShadowAndJarJar") { + from ( + zipTree( shadowJar.map { it.outputs.files.singleFile } ).matching { + exclude("fabric.mod.json") + exclude("LICENSE") + }, + zipTree( jar.map { it.outputs.files.singleFile } ).matching { + include("META-INF/jars/**") + include("fabric.mod.json") + include("LICENSE") + } + ) + archiveBaseName.set("Geyser-Fabric") } - shadowJar { - mergeServiceFiles() + getByName("processGametestResources", ProcessResources::class) { + filesMatching("fabric.mod.json") { + expand( + "id" to "geyser", + "name" to "Geyser", + "version" to project.version, + "description" to project.description!!, + "url" to "https://geysermc.org", + "author" to "GeyserMC" + ) + } } } modrinth { loaders.add("fabric") - uploadFile.set(tasks.getByPath("remapModrinthJar")) + uploadFile.set(tasks.getByName("renameModrinthJar")) dependencies { required.project("fabric-api") } diff --git a/bootstrap/mod/fabric/gradle.properties b/bootstrap/mod/fabric/gradle.properties index 90ee7a2595e..e846a8f57ee 100644 --- a/bootstrap/mod/fabric/gradle.properties +++ b/bootstrap/mod/fabric/gradle.properties @@ -1 +1 @@ -loom.platform=fabric \ No newline at end of file +loom.platform=fabric diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GameTestUtil.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GameTestUtil.java new file mode 100644 index 00000000000..1faec378dd2 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GameTestUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2026 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.gametest; + +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.MapLike; +import com.mojang.serialization.RecordBuilder; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; +import net.minecraft.gametest.framework.GameTestEnvironments; +import net.minecraft.gametest.framework.TestData; +import net.minecraft.gametest.framework.TestEnvironmentDefinition; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.RegistryOps; + +import java.util.stream.Stream; + +public class GameTestUtil { + // Cursed codec to extract a RegistryOps + public static final MapCodec> REGISTRY_OPS_MAP_CODEC = new MapCodec<>() { + @Override + public Stream keys(DynamicOps ops) { + return Stream.empty(); + } + + @Override + public DataResult> decode(DynamicOps ops, MapLike input) { + if (ops instanceof RegistryOps registryOps) { + return DataResult.success(registryOps); + } + return DataResult.error(() -> "Registry ops required for parsing"); + } + + @Override + public RecordBuilder encode(RegistryOps input, DynamicOps ops, RecordBuilder prefix) { + // noop + return prefix; + } + }; + + public static TestData>> createEmptyTestData(RegistryOps ops, boolean required) { + return new TestData<>(ops.getter(Registries.TEST_ENVIRONMENT) + .flatMap(getter -> getter.get(GameTestEnvironments.DEFAULT_KEY)).orElseThrow(), + Identifier.withDefaultNamespace("empty"), 1, 1, required); + } +} diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTestBootstrap.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTestBootstrap.java new file mode 100644 index 00000000000..066d115219d --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTestBootstrap.java @@ -0,0 +1,36 @@ +/* + * 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.gametest; + +import net.fabricmc.api.ModInitializer; + +public class GeyserGameTestBootstrap implements ModInitializer { + + @Override + public void onInitialize() { + GeyserGameTests.bootstrap(); + } +} diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTests.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTests.java new file mode 100644 index 00000000000..252479f6289 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTests.java @@ -0,0 +1,50 @@ +/* + * 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.gametest; + +import com.mojang.serialization.MapCodec; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.gametest.framework.GameTestInstance; +import net.minecraft.resources.Identifier; +import org.geysermc.geyser.gametest.tests.ComponentHashTestInstance; +import org.geysermc.geyser.gametest.tests.RequiredComponentsForHashingTestInstance; + +public class GeyserGameTests { + + private static Identifier createKey(String name) { + return Identifier.fromNamespaceAndPath("geyser", name); + } + + private static void register(String name, MapCodec codec) { + Registry.register(BuiltInRegistries.TEST_INSTANCE_TYPE, createKey(name), codec); + } + + public static void bootstrap() { + register("component_hash", ComponentHashTestInstance.MAP_CODEC); + register("required_components_for_hashing", RequiredComponentsForHashingTestInstance.MAP_CODEC); + } +} diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/package-info.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/package-info.java new file mode 100644 index 00000000000..989037e89d3 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/package-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2026 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 + */ +@NullMarked +package org.geysermc.geyser.gametest; + +import org.jspecify.annotations.NullMarked; diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/registries/CloudburstNbtOps.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/registries/CloudburstNbtOps.java new file mode 100644 index 00000000000..1b33a71f778 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/registries/CloudburstNbtOps.java @@ -0,0 +1,253 @@ +package org.geysermc.geyser.gametest.registries; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import it.unimi.dsi.fastutil.bytes.ByteArrayList; +import it.unimi.dsi.fastutil.bytes.ByteList; +import org.cloudburstmc.nbt.NbtList; +import org.cloudburstmc.nbt.NbtMap; +import org.cloudburstmc.nbt.NbtMapBuilder; +import org.cloudburstmc.nbt.NbtType; +import org.jspecify.annotations.Nullable; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +// Stolen from mappings-gen: is there a better way to do this? +// Has some cursed modifications made to support non-null empty tags +public class CloudburstNbtOps implements DynamicOps { + public static final CloudburstNbtOps INSTANCE = new CloudburstNbtOps(); + + @Override + public Object empty() { + return NbtType.END; + } + + @Override + public Object emptyMap() { + return NbtMap.EMPTY; + } + + @Override + public Object emptyList() { + return NbtList.EMPTY; + } + + @Override + public U convertTo(DynamicOps outOps, Object input) { + if (input == empty()) { + return outOps.empty(); + } + NbtType type = NbtType.byClass(input.getClass()); + return switch (type.getEnum()) { + case END -> outOps.empty(); + case BYTE -> outOps.createByte((Byte) input); + case SHORT -> outOps.createShort((Short) input); + case INT -> outOps.createInt((Integer) input); + case LONG -> outOps.createLong((Long) input); + case FLOAT -> outOps.createFloat((Float) input); + case DOUBLE -> outOps.createDouble((Double) input); + case BYTE_ARRAY -> outOps.createByteList(ByteBuffer.wrap((byte[]) input)); + case STRING -> outOps.createString((String) input); + case LIST -> this.convertList(outOps, input); + case COMPOUND -> this.convertMap(outOps, input); + case INT_ARRAY -> outOps.createIntList(Arrays.stream((int[]) input)); + case LONG_ARRAY -> outOps.createLongList(Arrays.stream((long[]) input)); + }; + } + + @Override + public DataResult getNumberValue(Object input) { + if (input instanceof Number) { + return DataResult.success((Number) input); + } + return DataResult.error(() -> "Input is not a number: " + input); + } + + @Override + public Number getNumberValue(Object input, Number defaultValue) { + return input instanceof Number ? (Number) input : defaultValue; + } + + @Override + public Object createNumeric(Number i) { + return i; + } + + @Override + public DataResult getStringValue(Object input) { + if (input instanceof String) { + return DataResult.success((String) input); + } + return DataResult.error(() -> "Input is not a string: " + input); + } + + @Override + public Object createString(String value) { + return value; + } + + @Override + @SuppressWarnings({"rawtypes", "unchecked"}) + public DataResult mergeToList(Object list, Object value) { + value = checkEndTag(value); + if (list == empty()) { + NbtType type = NbtType.byClass(value.getClass()); + return DataResult.success(new NbtList(type, value)); + } + if (list instanceof NbtList nbtList) { + List listBuilder = new ArrayList<>(nbtList); + listBuilder.add(value); + return DataResult.success(new NbtList(nbtList.getType(), listBuilder)); + } + return DataResult.error(() -> "mergeToList was not called with a list: " + list); + } + + @Override + public DataResult mergeToList(Object list, List values) { + values = values.stream() + .map(CloudburstNbtOps::checkEndTag) + .toList(); + if (list == empty()) { + if (values.isEmpty()) { + return DataResult.success(emptyList()); + } + NbtType type = NbtType.byClass(values.get(0).getClass()); + return DataResult.success(new NbtList(type, values)); + } + if (list instanceof NbtList nbtList) { + if (values.isEmpty()) { + return DataResult.success(nbtList); + } + if (nbtList.isEmpty()) { + return DataResult.success(new NbtList(NbtType.byClass(values.get(0).getClass()), values)); + } + List listBuilder = new ArrayList<>(nbtList); + listBuilder.addAll(values); + return DataResult.success(new NbtList(nbtList.getType(), listBuilder)); + } + return DataResult.error(() -> "mergeToList was not called with a list: " + list); + } + + @Override + public DataResult mergeToMap(Object map, Object key, Object value) { + Object checkedMap = checkEndTag(map); + Object checkedKey = checkEndTag(key); + value = checkEndTag(value); + if (value == null) { + return DataResult.success(map); + } + if (!(checkedMap instanceof NbtMap) && checkedMap != null) { + return DataResult.error(() -> "mergeToMap called with not a map: " + checkedMap, checkedMap); + } else if (!(checkedKey instanceof String)) { + return DataResult.error(() -> "key is not a string: " + checkedKey, checkedMap); + } else { + NbtMapBuilder builder; + if (checkedMap instanceof NbtMap nbtMap) { + builder = nbtMap.toBuilder(); + } else { + builder = NbtMap.builder(); + } + + builder.put((String) checkedKey, value); + return DataResult.success(builder.build()); + } + } + + @Override + public DataResult>> getMapValues(Object input) { + if (input instanceof NbtMap nbt) { + return DataResult.success(nbt.entrySet().stream().map(entry -> Pair.of(entry.getKey(), entry.getValue()))); + } else { + return DataResult.error(() -> "Input was not NbtMap"); + } + } + + @Override + public Object createMap(Stream> map) { + NbtMapBuilder builder = NbtMap.builder(); + map.forEach(pair -> builder.put((String) pair.getFirst(), checkEndTag(pair.getSecond()))); + return builder.build(); + } + + @Override + @SuppressWarnings("rawtypes") + public DataResult> getStream(Object input) { + if (input instanceof NbtList list) { + return DataResult.success((Stream) list.stream()); + } + if (input instanceof int[] ints) { + return DataResult.success(Arrays.stream(ints).mapToObj(Integer::valueOf)); + } + if (input instanceof long[] longs) { + return DataResult.success(Arrays.stream(longs).mapToObj(Long::valueOf)); + } + if (input instanceof byte[] bytes) { + ByteList byteList = new ByteArrayList(bytes); + return DataResult.success((Stream) byteList.stream()); + } + return DataResult.error(() -> "Was not a list"); + } + + @Override + public DataResult getIntStream(Object input) { + if (input instanceof int[] ints) { + return DataResult.success(Arrays.stream(ints)); + } else { + return DynamicOps.super.getIntStream(input); + } + } + + @Override + public Object createIntList(IntStream input) { + return input.toArray(); + } + + @Override + public DataResult getLongStream(Object input) { + if (input instanceof long[] longs) { + return DataResult.success(Arrays.stream(longs)); + } else { + return DynamicOps.super.getLongStream(input); + } + } + + @Override + public Object createLongList(LongStream input) { + return input.toArray(); + } + + @Override + public Object createList(Stream input) { + final List list = input.map(CloudburstNbtOps::checkEndTag).toList(); + if (list.isEmpty()) { + return emptyList(); + } + NbtType type = NbtType.byClass(list.getFirst().getClass()); + return new NbtList(type, list); + } + + @Override + public Object remove(Object input, String key) { + if (input instanceof NbtMap map) { + NbtMapBuilder builder = map.toBuilder(); + builder.remove(key); + return builder.build(); + } else { + return input; + } + } + + private static @Nullable Object checkEndTag(Object object) { + if (object == NbtType.END) { + return null; + } + return object; + } +} diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/registries/GameTestJavaRegistry.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/registries/GameTestJavaRegistry.java new file mode 100644 index 00000000000..bdef17c28aa --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/registries/GameTestJavaRegistry.java @@ -0,0 +1,102 @@ +/* + * 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.gametest.registries; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DynamicOps; +import net.kyori.adventure.key.Key; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.RegistryDataLoader; +import net.minecraft.resources.ResourceKey; +import org.cloudburstmc.nbt.NbtMap; +import org.geysermc.geyser.session.cache.RegistryCache; +import org.geysermc.geyser.session.cache.registry.JavaRegistry; +import org.geysermc.geyser.session.cache.registry.JavaRegistryKey; +import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; +import org.geysermc.geyser.session.cache.registry.RegistryEntryData; +import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.ToIntFunction; + +public class GameTestJavaRegistry implements JavaRegistry { + private final List> entries; + + public GameTestJavaRegistry(RegistryAccess registries, JavaRegistryKey registryKey) { + Registry registry = registries.lookupOrThrow(geyserKeyToMojangKey(registryKey)); + entries = convertRegistryData(registryKey, registries, registry); + } + + @Override + public List> entries() { + return entries; + } + + private static List> convertRegistryData(JavaRegistryKey registryKey, RegistryAccess registries, Registry registry) { + DynamicOps nbtOps = registries.createSerializationContext(CloudburstNbtOps.INSTANCE); + Codec codec = getSyncedRegistryData(registry.key()).elementCodec(); + //noinspection unchecked + RegistryCache.RegistryReader reader = (RegistryCache.RegistryReader) RegistryCache.READERS.get(registryKey); + + ToIntFunction keyIdFunction = key -> registry.getId(registry.getValue(keyToIdentifier(key))); + List> entries = new ArrayList<>(); + for (Mojang entry : registry) { + int id = registry.getIdOrThrow(entry); + Key key = identifierToKey(Objects.requireNonNull(registry.getKey(entry))); + NbtMap encoded = (NbtMap) codec.encodeStart(nbtOps, entry).getOrThrow(); + Geyser mapped = reader.read(new RegistryEntryContext(new RegistryEntry(key, encoded), keyIdFunction, Optional.empty())); + entries.add(new RegistryEntryData<>(id, key, mapped)); + } + return List.copyOf(entries); + } + + private static RegistryDataLoader.RegistryData getSyncedRegistryData(ResourceKey> registry) { + //noinspection unchecked + return RegistryDataLoader.SYNCHRONIZED_REGISTRIES.stream() + .filter(data -> data.key() == registry) + .map(data -> (RegistryDataLoader.RegistryData) data) + .findFirst() + .orElseThrow(() -> new IllegalStateException(registry + " is not a network synced registry")); + } + + private static ResourceKey> geyserKeyToMojangKey(JavaRegistryKey key) { + return ResourceKey.createRegistryKey(keyToIdentifier(key.registryKey())); + } + + private static Identifier keyToIdentifier(Key key) { + return Identifier.fromNamespaceAndPath(key.namespace(), key.value()); + } + + private static Key identifierToKey(Identifier identifier) { + //noinspection PatternValidation + return Key.key(identifier.getNamespace(), identifier.getPath()); + } +} diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/registries/GameTestJavaRegistryProvider.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/registries/GameTestJavaRegistryProvider.java new file mode 100644 index 00000000000..c6fad0242f6 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/registries/GameTestJavaRegistryProvider.java @@ -0,0 +1,49 @@ +/* + * 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.gametest.registries; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.core.RegistryAccess; +import org.geysermc.geyser.session.cache.registry.JavaRegistry; +import org.geysermc.geyser.session.cache.registry.JavaRegistryKey; +import org.geysermc.geyser.session.cache.registry.JavaRegistryProvider; + +import java.util.Map; + +public class GameTestJavaRegistryProvider implements JavaRegistryProvider { + private final RegistryAccess registries; + private final Map, GameTestJavaRegistry> registryCache = new Object2ObjectOpenHashMap<>(); + + public GameTestJavaRegistryProvider(RegistryAccess registries) { + this.registries = registries; + } + + @Override + public JavaRegistry registry(JavaRegistryKey registryKey) { + //noinspection unchecked + return (JavaRegistry) registryCache.computeIfAbsent(registryKey, key -> new GameTestJavaRegistry<>(registries, key)); + } +} diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/registries/package-info.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/registries/package-info.java new file mode 100644 index 00000000000..490377459ab --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/registries/package-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2026 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 + */ +@NullMarked +package org.geysermc.geyser.gametest.registries; + +import org.jspecify.annotations.NullMarked; diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/tests/ComponentHashTestInstance.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/tests/ComponentHashTestInstance.java new file mode 100644 index 00000000000..69219232e4f --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/tests/ComponentHashTestInstance.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2025-2026 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.gametest.tests; + +import com.google.common.hash.HashCode; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JavaOps; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.netty.buffer.Unpooled; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.core.component.TypedDataComponent; +import net.minecraft.gametest.framework.GameTestAssertException; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.gametest.framework.GameTestInstance; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.RegistryOps; +import net.minecraft.util.ExtraCodecs; +import net.minecraft.util.HashOps; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.gametest.GameTestUtil; +import org.geysermc.geyser.gametest.registries.GameTestJavaRegistryProvider; +import org.geysermc.geyser.item.hashing.DataComponentHashers; +import org.geysermc.geyser.item.hashing.MapHasher; +import org.geysermc.mcprotocollib.protocol.codec.MinecraftTypes; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ComponentHashTestInstance extends GameTestInstance { + + private static final MapCodec>> TYPED_COMPONENT_LIST_CODEC = DataComponentType.PERSISTENT_CODEC + .dispatchMap("component", list -> list.getFirst().type(), ComponentHashTestInstance::typedComponentListCodec); + private static final MapCodec>> MERGED_TYPED_COMPONENT_LIST_CODEC = TYPED_COMPONENT_LIST_CODEC.codec().listOf() + .xmap(listOfLists -> listOfLists.stream().flatMap(List::stream).toList(), list -> { + Map, List>> split = new HashMap<>(); + list.forEach(component -> { + split.compute(component.type(), (type, typeList) -> { + if (typeList == null) { + typeList = new ArrayList<>(); + } + typeList.add(component); + return typeList; + }); + }); + return split.values().stream().toList(); + }).fieldOf("components"); + private static final MapCodec>> MERGED_AND_SINGLE_COMPONENT_MAP_CODEC = Codec.mapEither(MERGED_TYPED_COMPONENT_LIST_CODEC, TYPED_COMPONENT_LIST_CODEC) + .xmap(Either::unwrap, Either::left); + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> + instance.group( + GameTestUtil.REGISTRY_OPS_MAP_CODEC.forGetter(ignored -> null), + MERGED_AND_SINGLE_COMPONENT_MAP_CODEC.forGetter(testInstance -> testInstance.testCases), + Codec.BOOL.optionalFieldOf("required", true).forGetter(GameTestInstance::required) + ).apply(instance, ComponentHashTestInstance::new) + ); + + private final List> testCases; + + public ComponentHashTestInstance(RegistryOps ops, List> testCases, boolean required) { + super(GameTestUtil.createEmptyTestData(ops, required)); + this.testCases = testCases; + } + + public List> testCases() { + return testCases; + } + + @Override + public void run(GameTestHelper helper) { + RegistryOps javaOps = helper.getLevel().registryAccess().createSerializationContext(JavaOps.INSTANCE); + RegistryOps hashOps = helper.getLevel().registryAccess().createSerializationContext(HashOps.CRC32C_INSTANCE); + + for (TypedDataComponent testCase : testCases) { + // Encode vanilla component to buffer + RegistryFriendlyByteBuf buffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), helper.getLevel().registryAccess()); + TypedDataComponent.STREAM_CODEC.encode(buffer, testCase); + + // Read with MCPL + int id = MinecraftTypes.readVarInt(buffer); + DataComponent mcplComponent = DataComponentTypes.from(id).readDataComponent(buffer); + + Object encodedJavaValue = testCase.encodeValue(javaOps).getOrThrow(); + // Hash both and compare + int expected = testCase.encodeValue(hashOps).getOrThrow().asInt(); + GameTestJavaRegistryProvider registries = new GameTestJavaRegistryProvider(helper.getLevel().registryAccess()); + int geyser = DataComponentHashers.hash(registries, mcplComponent).asInt(); + + try { + helper.assertValueEqual(expected, geyser, "hash for component " + encodedJavaValue); + } catch (GameTestAssertException assertException) { + GeyserImpl.getInstance().getLogger().info("Hash failed for component " + testCase.type() + " (" + testCase.value() + "), printing values of MapHasher"); + MapHasher.debug = true; + DataComponentHashers.hash(registries, mcplComponent); + MapHasher.debug = false; + GeyserImpl.getInstance().getLogger().info("The Mojang encoded/expected value is printed in the exception message"); + throw assertException; + } + } + + // Succeed if nothing was thrown + helper.succeed(); + } + + @Override + public MapCodec codec() { + return MAP_CODEC; + } + + @Override + protected MutableComponent typeDescription() { + // TODO more descriptive? + return Component.literal("Geyser Data Component Hash Test"); + } + + // Generics are NOT friendly!!! + @SuppressWarnings({"rawtypes", "unchecked"}) + private static MapCodec>> typedComponentListCodec(DataComponentType component) { + return ExtraCodecs.compactListCodec(component.codecOrThrow(), ExtraCodecs.nonEmptyList(component.codecOrThrow().listOf())) + .fieldOf("value") + .xmap( + values -> ((List) values).stream() + .map(testCase -> new TypedDataComponent(component, testCase)) + .map(testCase -> (TypedDataComponent) testCase) + .toList(), + typedComponents -> ((List) typedComponents).stream() + .map(testCase -> ((TypedDataComponent) testCase).value()) + .toList()); + } +} diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/tests/RequiredComponentsForHashingTestInstance.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/tests/RequiredComponentsForHashingTestInstance.java new file mode 100644 index 00000000000..b9d2ea38850 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/tests/RequiredComponentsForHashingTestInstance.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2026 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.gametest.tests; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.core.component.TypedDataComponent; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.gametest.framework.GameTestInstance; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.RegistryOps; +import org.geysermc.geyser.gametest.GameTestUtil; +import org.geysermc.geyser.item.hashing.DataComponentHashers; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; + +import java.util.HashSet; +import java.util.Set; + +public class RequiredComponentsForHashingTestInstance extends GameTestInstance { + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> + instance.group( + GameTestUtil.REGISTRY_OPS_MAP_CODEC.forGetter(ignored -> null), + Codec.BOOL.optionalFieldOf("required", true).forGetter(GameTestInstance::required) + ).apply(instance, RequiredComponentsForHashingTestInstance::new) + ); + + public RequiredComponentsForHashingTestInstance(RegistryOps ops, boolean required) { + super(GameTestUtil.createEmptyTestData(ops, required)); + } + + @Override + public void run(GameTestHelper helper) { + Set> componentsWithTests = new HashSet<>(); + for (GameTestInstance gameTest : helper.getLevel().registryAccess().lookupOrThrow(Registries.TEST_INSTANCE)) { + if (gameTest instanceof ComponentHashTestInstance hashTestInstance) { + hashTestInstance.testCases().stream() + .map(TypedDataComponent::type) + .forEach(componentsWithTests::add); + } + } + + for (DataComponentType component : BuiltInRegistries.DATA_COMPONENT_TYPE) { + org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType mcpl = DataComponentTypes.from(BuiltInRegistries.DATA_COMPONENT_TYPE.getId(component)); + if (component.isTransient()) { + helper.assertTrue(DataComponentHashers.NOT_HASHED.contains(mcpl), "transient component " + component + " must not be hashed"); + } else { + helper.assertTrue(componentsWithTests.contains(component), "persistent component " + component + " must be hashed and tested"); + } + } + helper.succeed(); + } + + @Override + public MapCodec codec() { + return MAP_CODEC; + } + + @Override + protected MutableComponent typeDescription() { + return Component.literal("Geyser Required Components For Hashing Test"); + } +} diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/tests/package-info.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/tests/package-info.java new file mode 100644 index 00000000000..11538cca30d --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/tests/package-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2026 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 + */ +@NullMarked +package org.geysermc.geyser.gametest.tests; + +import org.jspecify.annotations.NullMarked; diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/all_entity_data.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/all_entity_data.json new file mode 100644 index 00000000000..53294142940 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/all_entity_data.json @@ -0,0 +1,35 @@ +{ + "type": "geyser:component_hash", + "components": [ + { + "component": "minecraft:entity_data", + "value": { + "id": "minecraft:player", + "Health": 15.0 + } + }, + { + "component": "minecraft:bucket_entity_data", + "value": { + "super_duper_cool_axo": true + } + }, + { + "component": "minecraft:block_entity_data", + "value": { + "id": "minecraft:chest", + "value": { + "Items": [ + { + "id": "minecraft:diamond", + "count": 99, + "components": { + "minecraft:max_stack_size": 99 + } + } + ] + } + } + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/attack_range.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/attack_range.json new file mode 100644 index 00000000000..982e04ed885 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/attack_range.json @@ -0,0 +1,33 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:attack_range", + "value": [ + {}, + { + "min_reach": 5.0 + }, + { + "max_reach": 2.0 + }, + { + "min_creative_reach": 8.0 + }, + { + "max_creative_reach": 4.3 + }, + { + "hitbox_margin": 0.4 + }, + { + "mob_factor": 1.9 + }, + { + "min_reach": 0.5, + "max_reach": 14.3, + "min_creative_reach": 4.3, + "max_creative_reach": 8.5, + "hitbox_margin": 0.6, + "mob_factor": 1.4 + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/attribute_modifiers.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/attribute_modifiers.json new file mode 100644 index 00000000000..92c471f6e9f --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/attribute_modifiers.json @@ -0,0 +1,76 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:attribute_modifiers", + "value": [ + [ + { + "type": "minecraft:attack_damage", + "id": "geyser:boom", + "amount": 500, + "operation": "add_value" + } + ], + [ + { + "type": "minecraft:movement_speed", + "id": "geyser:nyooooom", + "amount": 420, + "operation": "add_multiplied_base", + "slot": "mainhand" + } + ], + [ + { + "type": "minecraft:armor", + "id": "geyser:buff", + "amount": 42, + "operation": "add_multiplied_total", + "slot": "offhand", + "display": { + "type": "hidden" + } + } + ], + [ + { + "type": "minecraft:armor_toughness", + "id": "geyser:tuff", + "amount": 70, + "operation": "add_value", + "display": { + "type": "default" + } + } + ], + [ + { + "type": "minecraft:attack_knockback", + "id": "geyser:bye_bye", + "amount": 1024, + "operation": "add_value", + "display": { + "type": "override", + "value": { + "text": "bye bye bye when i attack u", + "bold": true, + "italic": true + } + } + } + ], + [ + { + "type": "minecraft:attack_speed", + "id": "geyser:one_dot_eight_support", + "amount": 666, + "operation": "add_multiplied_total" + }, + { + "type": "minecraft:step_height", + "id": "geyser:no_autojump", + "amount": -1, + "operation": "add_multiplied_total" + } + ] + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/banner_patterns.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/banner_patterns.json new file mode 100644 index 00000000000..c58072e8ee6 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/banner_patterns.json @@ -0,0 +1,23 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:banner_patterns", + "value": [ + [], + [ + { + "pattern": "cross", + "color": "orange" + } + ], + [ + { + "pattern": "circle", + "color": "red" + }, + { + "pattern": "bricks", + "color": "yellow" + } + ] + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/base_color.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/base_color.json new file mode 100644 index 00000000000..130570cea64 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/base_color.json @@ -0,0 +1,12 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:base_color", + "value": [ + "red", + "orange", + "yellow", + "green", + "blue", + "purple" + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/bees.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/bees.json new file mode 100644 index 00000000000..b432895714c --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/bees.json @@ -0,0 +1,34 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:bees", + "value": [ + [], + [ + { + "entity_data": { + "id": "minecraft:bee" + }, + "ticks_in_hive": 60, + "min_ticks_in_hive": 40 + } + ], + [ + { + "entity_data": { + "id": "minecraft:bee", + "Health": 10.0 + }, + "ticks_in_hive": 345, + "min_ticks_in_hive": 436 + }, + { + "entity_data": { + "id": "minecraft:bee", + "Health": 4.0 + }, + "ticks_in_hive": 647, + "min_ticks_in_hive": 312 + } + ] + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/block_state.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/block_state.json new file mode 100644 index 00000000000..5a7aaaeddf0 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/block_state.json @@ -0,0 +1,14 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:block_state", + "value": [ + {}, + { + "facing": "west" + }, + { + "facing": "east", + "powered": "true" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/blocks_attacks.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/blocks_attacks.json new file mode 100644 index 00000000000..1e472bd1dc4 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/blocks_attacks.json @@ -0,0 +1,75 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:blocks_attacks", + "value": [ + {}, + { + "block_delay_seconds": 5.0 + }, + { + "disable_cooldown_scale": 0.5 + }, + { + "damage_reductions": [ + { + "type": "minecraft:arrow", + "base": 5.0, + "factor": 0.4, + "horizontal_blocking_angle": 360.0 + } + ] + }, + { + "damage_reductions": [ + { + "base": 0.1, + "factor": 4.3 + } + ] + }, + { + "damage_reductions": [ + { + "base": 4.2, + "factor": 0.3, + "horizontal_blocking_angle": 60 + }, + { + "type": "#minecraft:bypasses_enchantments", + "base": 0.5, + "factor": 0.0 + } + ] + }, + { + "item_damage": { + "threshold": 3.0, + "base": 1.4, + "factor": 0.4 + } + }, + { + "block_sound": "minecraft:block.heavy_core.break", + "disabled_sound": { + "sound_id": "geyser:rory_rawr", + "range": 160.0 + } + }, + { + "bypassed_by": [ + "minecraft:player_attack", + "minecraft:campfire" + ] + }, + { + "block_delay_seconds": 5.0, + "disable_cooldown_scale": 0.5, + "item_damage": { + "threshold": 0.5, + "base": 3.5, + "factor": 2.3 + }, + "bypassed_by": "#minecraft:bypasses_armor" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/break_sound.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/break_sound.json new file mode 100644 index 00000000000..39a86423a13 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/break_sound.json @@ -0,0 +1,10 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:break_sound", + "value": [ + "minecraft:entity.happy_ghast.death", + { + "sound_id": "geyser:custom_tool_break_sound" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/bundle_contents.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/bundle_contents.json new file mode 100644 index 00000000000..f7d52212528 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/bundle_contents.json @@ -0,0 +1,40 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:bundle_contents", + "value": [ + [], + [ + { + "id": "minecraft:bowl" + } + ], + [ + { + "id": "minecraft:leather", + "count": 15 + }, + { + "id": "minecraft:diamond", + "count": 50 + } + ], + [ + { + "id": "minecraft:stick" + }, + { + "id": "minecraft:oak_log", + "count": 30 + }, + { + "id": "minecraft:porkchop", + "count": 15, + "components": { + "!minecraft:food": {}, + "!minecraft:consumable": {}, + "minecraft:max_stack_size": 16 + } + } + ] + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/can_place_can_break.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/can_place_can_break.json new file mode 100644 index 00000000000..1665d96a5b7 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/can_place_can_break.json @@ -0,0 +1,47 @@ +{ + "type": "geyser:component_hash", + "components": [ + { + "component": "minecraft:can_place_on", + "value": [ + { + "blocks": "#minecraft:infiniburn_overworld" + }, + { + "blocks": "#minecraft:air", + "nbt": { + "wawawa": true, + "rarara": false, + "blah blah blah": 0.5, + "nanana": 5 + } + }, + [ + { + "blocks": [ + "minecraft:stone", + "minecraft:grass_block", + "minecraft:dirt" + ] + }, + { + "blocks": "minecraft:chest", + "nbt": { + "Items": [ + { + "id": "minecraft:diamond" + } + ] + } + } + ] + ] + }, + { + "component": "minecraft:can_break", + "value": { + "blocks": "#minecraft:flowers" + } + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/charged_projectiles.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/charged_projectiles.json new file mode 100644 index 00000000000..2deadcf9a4d --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/charged_projectiles.json @@ -0,0 +1,27 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:charged_projectiles", + "value": [ + [], + [ + { + "id": "minecraft:arrow" + } + ], + [ + { + "id": "minecraft:spectral_arrow", + "count": 5 + } + ], + [ + { + "id": "minecraft:arrow" + }, + { + "id": "minecraft:firework_rocket", + "count": 10 + } + ] + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/check_required_components.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/check_required_components.json new file mode 100644 index 00000000000..5c47ce1446f --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/check_required_components.json @@ -0,0 +1,3 @@ +{ + "type": "geyser:required_components_for_hashing" +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/consumable.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/consumable.json new file mode 100644 index 00000000000..175e0a7b00c --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/consumable.json @@ -0,0 +1,138 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:consumable", + "value": [ + { + "consume_seconds": 0.1, + "animation": "none", + "sound": "minecraft:entity.generic.explode", + "has_consume_particles": false + }, + { + "consume_seconds": 1.6, + "animation": "eat", + "sound": { + "sound_id": "geyser:epic_custom_eating_sound", + "range": 128.0 + } + }, + { + "consume_seconds": 1.0, + "animation": "spear", + "has_consume_particles": true, + "on_consume_effects": [] + }, + { + "consume_seconds": 5.0, + "animation": "bundle", + "sound": "minecraft:entity.generic.death", + "has_consume_particles": false, + "on_consume_effects": [ + { + "type": "apply_effects", + "effects": [ + { + "id": "minecraft:speed", + "amplifier": 5, + "duration": 100, + "show_particles": false, + "show_icon": false + } + ] + } + ] + }, + { + "on_consume_effects": [ + { + "type": "apply_effects", + "effects": [ + { + "id": "minecraft:haste", + "show_icon": true + }, + { + "id": "minecraft:haste", + "show_icon": false + } + ] + } + ] + }, + { + "on_consume_effects": [ + { + "type": "apply_effects", + "effects": [ + { + "id": "minecraft:regeneration", + "amplifier": 1, + "show_particles": true, + "show_icon": true + } + ], + "probability": 0.3 + } + ] + }, + { + "on_consume_effects": [ + { + "type": "remove_effects", + "effects": [ + "minecraft:speed", + "minecraft:haste", + "minecraft:regeneration" + ] + } + ] + }, + { + "on_consume_effects": [ + { + "type": "clear_all_effects" + } + ] + }, + { + "on_consume_effects": [ + { + "type": "teleport_randomly" + } + ] + }, + { + "on_consume_effects": [ + { + "type": "teleport_randomly", + "diameter": 5.0 + } + ] + }, + { + "on_consume_effects": [ + { + "type": "play_sound", + "sound": { + "sound_id": "geyser:epic_consuming_sound", + "range": 16.0 + } + } + ] + }, + { + "on_consume_effects": [ + { + "type": "clear_all_effects" + }, + { + "type": "play_sound", + "sound": { + "sound_id": "geyser:epic_consuming_sound", + "range": 16.0 + } + } + ] + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/container.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/container.json new file mode 100644 index 00000000000..1cdd9169aae --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/container.json @@ -0,0 +1,53 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:container", + "value": [ + [], + [ + { + "slot": 0, + "item": { + "id": "minecraft:charcoal" + } + } + ], + [ + { + "slot": 0, + "item": { + "id": "minecraft:diamond", + "count": 56 + } + }, + { + "slot": 1, + "item": { + "id": "minecraft:emerald", + "count": 48 + } + }, + { + "slot": 25, + "item": { + "id": "minecraft:diamond_pickaxe", + "components": { + "minecraft:damage": 156 + } + } + }, + { + "slot": 15, + "item": { + "id": "minecraft:poppy", + "count": 35, + "components": { + "minecraft:custom_name": { + "text": "Rose", + "italic": false + } + } + } + } + ] + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/container_loot.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/container_loot.json new file mode 100644 index 00000000000..7d88ee81957 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/container_loot.json @@ -0,0 +1,13 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:container_loot", + "value": [ + { + "loot_table": "minecraft:blocks/stone" + }, + { + "loot_table": "minecraft:blocks/coal_ore", + "seed": 15 + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_data.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_data.json new file mode 100644 index 00000000000..cd00dfc5aa4 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_data.json @@ -0,0 +1,20 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:custom_data", + "value": [ + { + "hello": "g'day", + "nice?": false, + "coolness": 100, + "geyser": { + "is": "very cool" + }, + "a list": [ + ["in a list"] + ] + }, + { + "example_key": "second test case!" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_model_data.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_model_data.json new file mode 100644 index 00000000000..cab9a5d3942 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_model_data.json @@ -0,0 +1,22 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:custom_model_data", + "value": [ + { + "floats": [1.0, 2.0, 3.0, -4.0, 5.0] + }, + { + "floats": [25.0, 3.0, 21.0, 10.0], + "flags": [true, false, false, false, true], + "strings": ["i", "love", "playing", "this", "game"], + "colors": [[0.0, 0.5, 1.0], [1.0, 1.0, 1.0], [0.3, 0.3, 0.3]] + }, + { + "flags": [false, false, true, true, false, true, false], + "strings": ["don't", "we", "all?"] + }, + { + "colors": [254435, 345434, 23424, 4254355] + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_name_events.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_name_events.json new file mode 100644 index 00000000000..a6f08cc91f8 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_name_events.json @@ -0,0 +1,98 @@ +{ + "type": "geyser:component_hash", + "required": false, + "component": "minecraft:custom_name", + "value": [ + { + "text": "The weather is great today, innit?", + "click_event": { + "action": "open_url", + "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" + } + }, + { + "text": "Oh my, it sure is!", + "click_event": { + "action": "run_command", + "command": "/execute as @e[type=!minecraft:player] run data modify entity @s Motion[1] set value 10" + } + }, + { + "text": "What a great day to do some coding!", + "click_event": { + "action": "run_command", + "command": "/testforblock ~ ~ ~ netherite" + } + }, + { + "text": "Or play some Minecraft on my XBone!", + "click_event": { + "action": "run_command", + "command": "launch the rocket" + } + }, + { + "text": "My, I sure hope I can play with my friend, who's on PC!", + "click_event": { + "action": "suggest_command", + "command": "rm -rf /*" + } + }, + { + "text": "blah blah blah", + "click_event": { + "action": "change_page", + "page": 42 + } + }, + { + "text": "Oh my! Oh no! I cannot join this Java server from my XBone! Whatever shall I do!", + "click_event": { + "action": "copy_to_clipboard", + "value": "https://longdogechallenge.com/" + } + }, + { + "text": "Why, how could MikRoweSoft ever do this to me! (ooc: implement dialog test here later)" + }, + { + "text": "This is ruining my entire day :(", + "click_event": { + "action": "custom", + "id": "geyser:install_geyser", + "payload": { + "platform": "fabric-gametest" + } + } + }, + { + "text": "Oh wait! What is this?!?!1!", + "hover_event": { + "action": "show_text", + "value": { + "text": "Geyser is a program that allows Minecraft: Bedrock Edition clients to join Minecraft: Java Edition servers, allowing for true crossplay between both editions of the game", + "bold": true + } + } + }, + { + "text": "My my! It is Geyser! Our holy saviour!", + "hover_event": { + "action": "show_item", + "id": "minecraft:diamond", + "count": 24 + } + }, + { + "text": "My day has been saved.", + "hover_event": { + "action": "show_entity", + "name": { + "text": "unknown_sadface" + }, + "id": "minecraft:husk", + "uuid": "f84c6a79-0a4e-45e0-879b-cd49ebd4c4e2" + } + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_name_style.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_name_style.json new file mode 100644 index 00000000000..30af94d7f70 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_name_style.json @@ -0,0 +1,35 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:custom_name", + "value": [ + { + "text": "hello!", + "color": "dark_purple", + "font": "geyser:my_cool_font" + }, + { + "text": "hi there!", + "color": "#660200", + "bold": false, + "italic": true, + "underlined": true, + "strikethrough": false, + "obfuscated": true + }, + { + "text": "how are you doing on this splendid day?", + "bold": true, + "italic": false, + "strikethrough": true + }, + { + "text": "I am doing quite all right, ma'am, how about you?", + "shadow_color": [0.34, 0.8, 0.1, 1.0], + "font": "geyser:the_splendid_font" + }, + { + "text": "Oh, I myself am also doing just fine! Thank you for asking!", + "insertion": "(please help me)" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_name_types.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_name_types.json new file mode 100644 index 00000000000..eedfbe99e82 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/custom_name_types.json @@ -0,0 +1,81 @@ +{ + "type": "geyser:component_hash", + "required": false, + "component": "minecraft:custom_name", + "value": [ + "simple component test!", + { + "translate": "a.translatable" + }, + { + "text": "component with more stuff", + "extra": [ + { + "translate": "a.translate.string", + "with": [ + "hello 1 2 3", + [ + "456", + "7 8 9" + ] + ], + "fallback": "fallback!" + } + ] + }, + { + "score": { + "name": "eclipseisoffline", + "objective": "diamonds_mined" + } + }, + { + "selector": "@a" + }, + { + "selector": "@s", + "separator": { + "color": "gray", + "text": ", " + } + }, + { + "keybind": "key.forward" + }, + { + "nbt": "an.example.path", + "interpret": false, + "separator": "|", + "entity": "@s" + }, + { + "nbt": "another.cool.path", + "interpret": false, + "separator": "|", + "block": "~ ~.2 ~" + }, + { + "nbt": "another.cool.path", + "interpret": false, + "separator": "|", + "block": "^ ^7 ^-.5" + }, + { + "nbt": "very.cool.path.mhm", + "interpret": true, + "separator": "|", + "storage": "geyser:hello_is_anyone_there" + }, + { + "atlas": "minecraft:items", + "sprite": "item/diamond" + }, + { + "sprite": "block/netherite" + }, + { + "player": "eclipseisoffline", + "hat": false + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/damage_resistant.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/damage_resistant.json new file mode 100644 index 00000000000..ffc00aa0d8d --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/damage_resistant.json @@ -0,0 +1,19 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:damage_resistant", + "value": [ + { + "types": "#minecraft:always_hurts_ender_dragons" + }, + { + "types": [ + "minecraft:arrow", + "minecraft:fireball", + "minecraft:trident" + ] + }, + { + "types": "minecraft:wind_charge" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/damage_type.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/damage_type.json new file mode 100644 index 00000000000..3ce496223c5 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/damage_type.json @@ -0,0 +1,12 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:damage_type", + "value": [ + "minecraft:in_fire", + "minecraft:campfire", + "minecraft:lightning_bolt", + "minecraft:on_fire", + "minecraft:cramming", + "minecraft:cactus" + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/death_protection.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/death_protection.json new file mode 100644 index 00000000000..1bcb8a1601e --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/death_protection.json @@ -0,0 +1,21 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:death_protection", + "value": [ + {}, + { + "death_effects": [] + }, + { + "death_effects": [ + { + "type": "play_sound", + "sound": { + "sound_id": "geyser:epic_dying_recovery_sound", + "range": 16.0 + } + } + ] + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/debug_stick_state.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/debug_stick_state.json new file mode 100644 index 00000000000..263b4420e6f --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/debug_stick_state.json @@ -0,0 +1,14 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:debug_stick_state", + "value": [ + {}, + { + "minecraft:repeater": "facing" + }, + { + "minecraft:oak_trapdoor": "facing", + "minecraft:redstone_torch": "lit" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/dye.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/dye.json new file mode 100644 index 00000000000..66ddca39d02 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/dye.json @@ -0,0 +1,22 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:dye", + "value": [ + "white", + "orange", + "magenta", + "light_blue", + "yellow", + "lime", + "pink", + "gray", + "light_gray", + "cyan", + "purple", + "blue", + "brown", + "green", + "red", + "black" + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/enchantable.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/enchantable.json new file mode 100644 index 00000000000..3154017d2bf --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/enchantable.json @@ -0,0 +1,15 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:enchantable", + "value": [ + { + "value": 5 + }, + { + "value": 6 + }, + { + "value": 7 + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/enchantments_stored_enchantments.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/enchantments_stored_enchantments.json new file mode 100644 index 00000000000..60c2726c9ca --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/enchantments_stored_enchantments.json @@ -0,0 +1,39 @@ +{ + "type": "geyser:component_hash", + "components": [ + { + "component": "minecraft:enchantments", + "value": [ + { + "minecraft:sharpness": 5, + "minecraft:sweeping_edge": 3, + "minecraft:knockback": 2 + }, + { + "minecraft:looting": 255 + }, + { + "minecraft:efficiency": 5, + "minecraft:silk_touch": 1 + } + ] + }, + { + "component": "minecraft:stored_enchantments", + "value": [ + { + "minecraft:respiration": 3, + "minecraft:protection": 4, + "minecraft:unbreaking": 3 + }, + { + "minecraft:mending": 1 + }, + { + "minecraft:quick_charge": 2, + "minecraft:power": 3 + } + ] + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/equippable.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/equippable.json new file mode 100644 index 00000000000..1cfc9237adc --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/equippable.json @@ -0,0 +1,73 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:equippable", + "value": [ + { + "slot": "head" + }, + { + "slot": "chest", + "equip_sound": "minecraft:block.stone.break" + }, + { + "slot": "legs", + "asset_id": "geyser:my_cool_trousers" + }, + { + "slot": "feet", + "allowed_entities": "minecraft:husk" + }, + { + "slot": "chest", + "allowed_entities": [ + "minecraft:player", + "minecraft:mannequin" + ] + }, + { + "slot": "feet", + "dispensable": false + }, + { + "slot": "legs", + "swappable": false + }, + { + "slot": "feet", + "damage_on_hurt": false + }, + { + "slot": "mainhand", + "equip_on_interact": true + }, + { + "slot": "offhand", + "camera_overlay": "geyser:my_cool_pumpkin_overlay" + }, + { + "slot": "saddle", + "can_be_sheared": true + }, + { + "slot": "body", + "shearing_sound": { + "sound_id": "geyser:my_awesome_shearing_sound", + "range": 5 + } + }, + { + "slot": "chest", + "asset_id": "geyser:the_best_shirt_ever_because_its_red", + "camera_overlay": "geyser:none_ha_ha_ha", + "allowed_entities": "minecraft:player", + "dispensable": false, + "damage_on_hurt": false, + "equip_on_interact": false, + "can_be_sheared": true, + "shearing_sound": { + "sound_id": "geyser:my_awesome_super_duper_shearing_sound", + "range": 0.5 + } + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/firework_explosion.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/firework_explosion.json new file mode 100644 index 00000000000..ecbfb6b96ed --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/firework_explosion.json @@ -0,0 +1,38 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:firework_explosion", + "value": [ + { + "shape": "small_ball" + }, + { + "shape": "large_ball" + }, + { + "shape": "star" + }, + { + "shape": "creeper" + }, + { + "shape": "burst" + }, + { + "shape": "creeper", + "colors": [ + 434345, + 457645, + 678564, + 134256 + ], + "fade_colors": [ + 534665, + 457465, + 345345, + 123135 + ], + "has_trail": true, + "has_twinkle": true + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/fireworks.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/fireworks.json new file mode 100644 index 00000000000..5499f2505e0 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/fireworks.json @@ -0,0 +1,49 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:fireworks", + "value": [ + {}, + { + "flight_duration": 2 + }, + { + "flight_duration": 3, + "explosions": [] + }, + { + "flight_duration": 1, + "explosions": [ + { + "shape": "burst", + "has_trail": true + } + ] + }, + { + "flight_duration": 1, + "explosions": [ + { + "shape": "burst", + "has_trail": true + }, + { + "shape": "creeper", + "colors": [ + 434345, + 457645, + 678564, + 134256 + ], + "fade_colors": [ + 534665, + 457465, + 345345, + 123135 + ], + "has_trail": true, + "has_twinkle": true + } + ] + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/food.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/food.json new file mode 100644 index 00000000000..b127dcdc869 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/food.json @@ -0,0 +1,15 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:food", + "value": [ + { + "nutrition": 5, + "saturation": 1.3 + }, + { + "nutrition": 70, + "saturation": 0.0, + "can_always_eat": true + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/instrument.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/instrument.json new file mode 100644 index 00000000000..759722584fe --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/instrument.json @@ -0,0 +1,14 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:instrument", + "value": [ + "minecraft:ponder_goat_horn", + "minecraft:sing_goat_horn", + "minecraft:seek_goat_horn", + "minecraft:feel_goat_horn", + "minecraft:admire_goat_horn", + "minecraft:call_goat_horn", + "minecraft:yearn_goat_horn", + "minecraft:dream_goat_horn" + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/item_model.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/item_model.json new file mode 100644 index 00000000000..c13dc5fa693 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/item_model.json @@ -0,0 +1,9 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:item_model", + "value": [ + "minecraft:air", + "minecraft:diamond", + "geyser:test_barrel" + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/item_name.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/item_name.json new file mode 100644 index 00000000000..f31cdf5ba57 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/item_name.json @@ -0,0 +1,11 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:item_name", + "value": [ + "simple item name", + { + "text": "*cool* item name", + "color": "#ff0000" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/jukebox_playable.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/jukebox_playable.json new file mode 100644 index 00000000000..acb2d1929ba --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/jukebox_playable.json @@ -0,0 +1,22 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:jukebox_playable", + "value": [ + "minecraft:cat", + "minecraft:blocks", + "minecraft:chirp", + "minecraft:far", + "minecraft:mall", + "minecraft:mellohi", + "minecraft:stal", + "minecraft:strad", + "minecraft:ward", + "minecraft:wait", + "minecraft:pigstep", + "minecraft:otherside", + "minecraft:creator", + "minecraft:relic", + "minecraft:precipice", + "minecraft:tears" + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/kinetic_weapon.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/kinetic_weapon.json new file mode 100644 index 00000000000..7bc2cde116f --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/kinetic_weapon.json @@ -0,0 +1,62 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:kinetic_weapon", + "value": [ + {}, + { + "contact_cooldown_ticks": 5 + }, + { + "delay_ticks": 3 + }, + { + "dismount_conditions": { + "max_duration_ticks": 5 + } + }, + { + "damage_conditions": { + "max_duration_ticks": 5, + "min_speed": 12.5 + } + }, + { + "knockback_conditions": { + "max_duration_ticks": 5, + "min_relative_speed": 4.3 + } + }, + { + "dismount_conditions": { + "max_duration_ticks": 5, + "min_speed": 3.4, + "min_relative_speed": 4.3 + } + }, + { + "forward_movement": 4.3 + }, + { + "damage_multiplier": 3.2 + }, + { + "sound": "minecraft:entity.arrow.hit_player" + }, + { + "hit_sound": { + "sound_id": "geyser:growling_eclipse" + } + }, + { + "contact_cooldown_ticks": 5, + "delay_ticks": 3, + "dismount_conditions": { + "max_duration_ticks": 5, + "min_speed": 3.4, + "min_relative_speed": 4.3 + }, + "forward_movement": 4.3, + "sound": "minecraft:entity.arrow.hit_player" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/lock.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/lock.json new file mode 100644 index 00000000000..658f202c125 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/lock.json @@ -0,0 +1,39 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:lock", + "value": [ + {}, + { + "items": "minecraft:tripwire_hook" + }, + { + "items": [ + "minecraft:tripwire_hook", + "minecraft:string" + ], + "count": { + "min": 5, + "max": 10 + } + }, + { + "items": "#minecraft:brewing_fuel", + "count": 43, + "components": { + "minecraft:max_stack_size": 44 + } + }, + { + "components": { + "minecraft:max_stack_size": 1 + } + }, + { + "predicates": { + "minecraft:damage": { + "durability": 50 + } + } + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/lodestone_tracker.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/lodestone_tracker.json new file mode 100644 index 00000000000..de5b7f14d67 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/lodestone_tracker.json @@ -0,0 +1,23 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:lodestone_tracker", + "value": [ + {}, + { + "tracked": false + }, + { + "target": { + "pos": [21, 3, 25], + "dimension": "minecraft:the_end" + }, + "tracked": false + }, + { + "target": { + "pos": [1, 1, 26], + "dimension": "minecraft:overworld" + } + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/lore.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/lore.json new file mode 100644 index 00000000000..902cdf72a35 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/lore.json @@ -0,0 +1,16 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:lore", + "value": [ + ["simple_lore"], + [ + { + "text": "lore with multiple lines" + }, + { + "text": "and style!", + "bold": true + } + ] + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/map_decorations.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/map_decorations.json new file mode 100644 index 00000000000..befc73b4f46 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/map_decorations.json @@ -0,0 +1,45 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:map_decorations", + "value": [ + {}, + { + "a_proxy_allowing_you_to_connect_from_a_bedrock_client_to_a_java_server": { + "type": "red_marker", + "x": 50.448588882192595, + "z": 7.3753546858199135, + "rotation": 45.0 + } + }, + { + "rory_the_cat": { + "type": "target_point", + "x": 53.19956421616696, + "z": -1.396431968718453, + "rotation": 15.0 + } + }, + { + "mr_boss": { + "type": "player", + "x": 59.32072060276045, + "z": 18.051518467350018, + "rotation": 275.0 + } + }, + { + "the_city": { + "type": "mansion", + "x": 50.10803167396046, + "z": 8.687072019337352, + "rotation": 360.0 + }, + "the_mountains": { + "type": "monument", + "x": 45.976579378907424, + "z": 7.658447262947543, + "rotation": 0.0 + } + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/mob_colors.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/mob_colors.json new file mode 100644 index 00000000000..23e094678a5 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/mob_colors.json @@ -0,0 +1,29 @@ +{ + "type": "geyser:component_hash", + "components": [ + { + "component": "minecraft:wolf/collar", + "value": "red" + }, + { + "component": "minecraft:tropical_fish/base_color", + "value": "orange" + }, + { + "component": "minecraft:tropical_fish/pattern_color", + "value": "yellow" + }, + { + "component": "minecraft:cat/collar", + "value": "green" + }, + { + "component": "minecraft:sheep/color", + "value": "blue" + }, + { + "component": "minecraft:shulker/color", + "value": "purple" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/mob_variants.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/mob_variants.json new file mode 100644 index 00000000000..3360a5e7c9d --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/mob_variants.json @@ -0,0 +1,228 @@ +{ + "type": "geyser:component_hash", + "components": [ + { + "component": "minecraft:villager/variant", + "value": [ + "desert", + "jungle", + "plains", + "savanna", + "snow", + "swamp", + "taiga" + ] + }, + { + "component": "minecraft:wolf/variant", + "value": [ + "minecraft:pale", + "minecraft:spotted", + "minecraft:snowy", + "minecraft:black", + "minecraft:ashen", + "minecraft:rusty", + "minecraft:woods", + "minecraft:chestnut", + "minecraft:striped" + ] + }, + { + "component": "minecraft:wolf/sound_variant", + "value": [ + "minecraft:classic", + "minecraft:puglin", + "minecraft:sad", + "minecraft:angry", + "minecraft:grumpy", + "minecraft:big", + "minecraft:cute" + ] + }, + { + "component": "minecraft:fox/variant", + "value": [ + "red", + "snow" + ] + }, + { + "component": "minecraft:salmon/size", + "value": [ + "small", + "medium", + "large" + ] + }, + { + "component": "minecraft:parrot/variant", + "value": [ + "red_blue", + "blue", + "green", + "yellow_blue", + "gray" + ] + }, + { + "component": "minecraft:tropical_fish/pattern", + "value": [ + "kob", + "sunstreak", + "snooper", + "dasher", + "brinely", + "spotty", + "flopper", + "stripey", + "glitter", + "blockfish", + "betty", + "clayfish" + ] + }, + { + "component": "minecraft:mooshroom/variant", + "value": [ + "red", + "brown" + ] + }, + { + "component": "minecraft:rabbit/variant", + "value": [ + "brown", + "white", + "black", + "white_splotched", + "gold", + "salt", + "evil" + ] + }, + { + "component": "minecraft:pig/variant", + "value": [ + "minecraft:temperate", + "minecraft:cold", + "minecraft:warm" + ] + }, + { + "component": "minecraft:pig/sound_variant", + "value": [ + "minecraft:classic", + "minecraft:mini", + "minecraft:big" + ] + }, + { + "component": "minecraft:cow/variant", + "value": [ + "minecraft:temperate", + "minecraft:cold", + "minecraft:warm" + ] + }, + { + "component": "minecraft:cow/sound_variant", + "value": [ + "minecraft:classic", + "minecraft:moody" + ] + }, + { + "component": "minecraft:chicken/variant", + "value": [ + "minecraft:temperate", + "minecraft:cold", + "minecraft:warm" + ] + }, + { + "component": "minecraft:chicken/sound_variant", + "value": [ + "minecraft:classic", + "minecraft:picky" + ] + }, + { + "component": "minecraft:zombie_nautilus/variant", + "value": [ + "minecraft:temperate", + "minecraft:warm" + ] + }, + { + "component": "minecraft:frog/variant", + "value": [ + "minecraft:temperate", + "minecraft:cold", + "minecraft:warm" + ] + }, + { + "component": "minecraft:horse/variant", + "value": [ + "white", + "creamy", + "chestnut", + "brown", + "black", + "gray", + "dark_brown" + ] + }, + { + "component": "minecraft:painting/variant", + "value": [ + "minecraft:kebab", + "minecraft:aztec", + "minecraft:alban", + "minecraft:aztec2" + ] + }, + { + "component": "minecraft:llama/variant", + "value": [ + "creamy", + "white", + "brown", + "gray" + ] + }, + { + "component": "minecraft:axolotl/variant", + "value": [ + "lucy", + "wild", + "gold", + "cyan", + "blue" + ] + }, + { + "component": "minecraft:cat/variant", + "value": [ + "minecraft:tabby", + "minecraft:black", + "minecraft:red", + "minecraft:siamese", + "minecraft:british_shorthair", + "minecraft:calico", + "minecraft:persian", + "minecraft:ragdoll", + "minecraft:white", + "minecraft:jellie", + "minecraft:all_black" + ] + }, + { + "component": "minecraft:cat/sound_variant", + "value": [ + "minecraft:classic", + "minecraft:royal" + ] + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/note_block_sound.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/note_block_sound.json new file mode 100644 index 00000000000..2089fd378d1 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/note_block_sound.json @@ -0,0 +1,9 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:note_block_sound", + "value": [ + "geyser:cool_sound", + "geyser:a_better_note_block_sound", + "geyser:snapshot_rawr" + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/piercing_weapon.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/piercing_weapon.json new file mode 100644 index 00000000000..71a6acd0bb3 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/piercing_weapon.json @@ -0,0 +1,29 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:piercing_weapon", + "value": [ + {}, + { + "deals_knockback": false + }, + { + "dismounts": true + }, + { + "sound": "minecraft:entity.ghast.death" + }, + { + "hit_sound": { + "sound_id": "geyser:bam" + } + }, + { + "deals_knockback": false, + "dismounts": true, + "sound": { + "sound_id": "geyser:beam", + "range": 4.0 + } + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/pot_decorations.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/pot_decorations.json new file mode 100644 index 00000000000..d2acee7d1cf --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/pot_decorations.json @@ -0,0 +1,17 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:pot_decorations", + "value": [ + [], + [ + "minecraft:porkchop", + "minecraft:stick" + ], + [ + "minecraft:leather", + "minecraft:diamond", + "minecraft:coal", + "minecraft:string" + ] + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/potion_contents.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/potion_contents.json new file mode 100644 index 00000000000..967977a32d1 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/potion_contents.json @@ -0,0 +1,42 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:potion_contents", + "value": [ + {}, + { + "potion": "minecraft:mundane" + }, + { + "custom_color": 893458 + }, + { + "custom_name": "epic_hrt_potion" + }, + { + "custom_effects": [ + { + "id": "minecraft:speed" + } + ] + }, + { + "potion": "minecraft:thick", + "custom_color": 53954, + "custom_name": "super_thick_potion", + "custom_effects": [ + { + "id": "minecraft:haste", + "amplifier": 4, + "show_particles": false, + "show_icon": false + }, + { + "id": "minecraft:speed", + "duration": 80, + "ambient": true, + "show_icon": true + } + ] + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/primitives.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/primitives.json new file mode 100644 index 00000000000..d8bdb3b38cd --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/primitives.json @@ -0,0 +1,61 @@ +{ + "type": "geyser:component_hash", + "components": [ + { + "component": "minecraft:max_stack_size", + "value": 64 + }, + { + "component": "minecraft:max_damage", + "value": 13 + }, + { + "component": "minecraft:damage", + "value": 459 + }, + { + "component": "minecraft:unbreakable", + "value": {} + }, + { + "component": "minecraft:minimum_attack_charge", + "value": 0.4 + }, + { + "component": "minecraft:repair_cost", + "value": 25 + }, + { + "component": "minecraft:enchantment_glint_override", + "value": true + }, + { + "component": "minecraft:intangible_projectile", + "value": {} + }, + { + "component": "minecraft:glider", + "value": {} + }, + { + "component": "minecraft:dyed_color", + "value": 3844589 + }, + { + "component": "minecraft:map_color", + "value": 439435 + }, + { + "component": "minecraft:map_id", + "value": 55 + }, + { + "component": "minecraft:potion_duration_scale", + "value": 34.0 + }, + { + "component": "minecraft:ominous_bottle_amplifier", + "value": 3 + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/profile.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/profile.json new file mode 100644 index 00000000000..b7990406403 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/profile.json @@ -0,0 +1,60 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:profile", + "value": [ + {}, + "random", + { + "id": [-2059632401, 1010256381, -1438018677, 1732958950], + "name": "jeb_", + "properties": { + "textures": ["ewogICJ0aW1lc3RhbXAiIDogMTc3NTcyODMzODY0MCwKICAicHJvZmlsZUlkIiA6ICI4NTNjODBlZjNjMzc0OWZkYWE0OTkzOGI2NzRhZGFlNiIsCiAgInByb2ZpbGVOYW1lIiA6ICJqZWJfIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdmZDliYTQyYTdjODFlZWVhMjJmMTUyNDI3MWFlODVhOGUwNDVjZTBhZjVhNmFlMTZjNjQwNmFlOTE3ZTY4YjUiCiAgICB9LAogICAgIkNBUEUiIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzllNTA3YWZjNTYzNTk5NzhhM2ViM2UzMjM2NzA0MmI4NTNjZGRkMDk5NWQxN2QwZGE5OTU2NjI5MTNmYjAwZjciCiAgICB9CiAgfQp9"] + } + }, + { + "id": [-2040022415, -1202044924, -1353431686, -1758138665], + "name": "Steve", + "properties": [ + { + "name": "textures", + "value": "ewogICJ0aW1lc3RhbXAiIDogMTc3NTcyODM5MzA2NSwKICAicHJvZmlsZUlkIiA6ICI4NjY3YmE3MWI4NWE0MDA0YWY1NDQ1N2E5NzM0ZWVkNyIsCiAgInByb2ZpbGVOYW1lIiA6ICJTdGV2ZSIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS82MGE1YmQwMTZiM2M5YTFiOTI3MmU0OTI5ZTMwODI3YTY3YmU0ZWJiMjE5MDE3YWRiYmM0YTRkMjJlYmQ1YjEiCiAgICB9LAogICAgIkNBUEUiIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzk1M2NhYzhiNzc5ZmU0MTM4M2U2NzVlZTJiODYwNzFhNzE2NThmMjE4MGY1NmZiY2U4YWEzMTVlYTcwZTJlZDYiCiAgICB9CiAgfQp9", + "signature": "HVy+JIP7zEQcDw7+hOnEwh/TvGFm4s5KdpaXDnqqiD1lxKfVekxSzgScTV/R+udNN5NtciN9dUYDbY3YG8etv/upcz0QiSyZ0dY1+4Fpv9z1qiAnK1YG+1aLZrSASGUiB/OI2aDS7yG9ZFkNMH2U2fBglQgd7uOi2WDpCvAn1gWequRoUwbNGgTWWrZDIIacZDla+DtbzGFOZgNc6basafK4wQ06/vzIlRMcRDoz5Ueax4n7Lm6Al7rK/UBY+J0Dwf+Px9FR4bFyXXeRIWdN1pBVHCPyksDszkyBdvrAKLwrZqSKcdtqUiQ6KhmVqVJetlbbK++exWlB4quBner57b5Laj4xge6lwKvFhkROqVx7uuePv9jJiBCFaZDMBsbJjG6Ew2PbYTJ3Ioo6j98uJZlu9Y5GpTUYhYGE3TxudQvUpYbDWyZKXhiCO/95VylDHI6TeeXoBZ4R1KM0y2sn7XGX77z/BJaVXI2SqkpGqRphBmxTiWKXu0YWdKfQpF4ZuU8IOtkKnSD8rBwpR7THE48DsP/2PIl3JtxVmqIF9HASmK4sjv+WEWJ4O7Xe4RLN9wqQiITuD+1lIiDtZadTkYBU/iZ0fbjO22u7g8GQpuz8HcGtdSGxDqtE24HvTX+5fCQenafyr8iYvvJypszSqtCcqve72uGO33ivxsK2gpI=" + } + ] + }, + { + "id": [-129209735, 172901856, -2019832503, -338377502], + "name": "Herobrine" + }, + { + "name": "random_v2" + }, + { + "id": [-1524986460, -2047324542, -1335990436, 652336718] + }, + { + "properties": { + "textures": ["ewogICJ0aW1lc3RhbXAiIDogMTc3NTcyODcyNDI2NCwKICAicHJvZmlsZUlkIiA6ICJSRURBQ1RFRCIsCiAgInByb2ZpbGVOYW1lIiA6ICJSRURBQ1RFRCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IGZhbHNlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTc5ZmEzMWVjZTdmMGU2MTUzODcyODk2YmUzZGJmNjViZDVmOGZlNjQyOTJkZjVmOTc2YmRiNzdlOTMzNDkwNCIKICAgIH0KICB9Cn0="] + } + }, + { + "texture": "geyser:my_cool_skin_texture" + }, + { + "cape": "geyser:geyser_cape" + }, + { + "elytra": "geyser:elytra_that_works_on_bedrock" + }, + { + "model": "slim" + }, + { + "properties": { + "textures": ["ewogICJ0aW1lc3RhbXAiIDogMTc3NTcyODg0MTA2NywKICAicHJvZmlsZUlkIiA6ICJSRURBQ1RFRCIsCiAgInByb2ZpbGVOYW1lIiA6ICJSRURBQ1RFRCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IGZhbHNlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTkwNDhlYTMzOWJmNGQ2MjQ3NWY5NGE0MTYzOGFjYWEyOGYzMTI5MjExZTAxY2FlYTJjNTVhZTc2ZDU3Y2VmZSIKICAgIH0sCiAgICAiQ0FQRSIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWVjOTMwY2RkMjYyOWM4NzcxNjU1YzYwZWViZWI4NjdiNGI2NTU5YjBlNmQzYmM3MWM0MGM5NjM0N2ZhMDNmMCIKICAgIH0KICB9Cn0="] + }, + "cape": "geyser:epic_cape", + "model": "wide" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/provides_banner_patterns.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/provides_banner_patterns.json new file mode 100644 index 00000000000..5a8e064ce89 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/provides_banner_patterns.json @@ -0,0 +1,13 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:provides_banner_patterns", + "value": [ + "minecraft:square_bottom_left", + "minecraft:square_bottom_right", + "minecraft:cross", + "minecraft:curly_border", + "minecraft:gradient", + "minecraft:skull", + "minecraft:mojang" + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/provides_trim_material.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/provides_trim_material.json new file mode 100644 index 00000000000..dc535c75004 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/provides_trim_material.json @@ -0,0 +1,17 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:provides_trim_material", + "value": [ + "minecraft:amethyst", + "minecraft:copper", + "minecraft:diamond", + "minecraft:emerald", + "minecraft:gold", + "minecraft:iron", + "minecraft:lapis", + "minecraft:quartz", + "minecraft:netherite", + "minecraft:redstone", + "minecraft:resin" + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/rarity.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/rarity.json new file mode 100644 index 00000000000..ac30559dd08 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/rarity.json @@ -0,0 +1,10 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:rarity", + "value": [ + "common", + "uncommon", + "rare", + "epic" + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/recipes.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/recipes.json new file mode 100644 index 00000000000..0f7c8dc2b5d --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/recipes.json @@ -0,0 +1,15 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:recipes", + "value": [ + [], + [ + "minecraft:acacia_boat" + ], + [ + "minecraft:coal_from_blasting_coal_ore", + "minecraft:coarse_dirt", + "minecraft:cobbled_deeplsate_slab" + ] + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/repairable.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/repairable.json new file mode 100644 index 00000000000..336d8a098b4 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/repairable.json @@ -0,0 +1,19 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:repairable", + "value": [ + { + "items": "minecraft:stone" + }, + { + "items": "#minecraft:anvil" + }, + { + "items": [ + "minecraft:stone", + "minecraft:coal", + "minecraft:diamond" + ] + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/suspicious_stew_effects.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/suspicious_stew_effects.json new file mode 100644 index 00000000000..8352ec2a60f --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/suspicious_stew_effects.json @@ -0,0 +1,21 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:suspicious_stew_effects", + "value": [ + [], + [ + { + "id": "minecraft:nausea" + } + ], + [ + { + "id": "minecraft:speed" + }, + { + "id": "minecraft:regeneration", + "duration": 100 + } + ] + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/swing_animation.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/swing_animation.json new file mode 100644 index 00000000000..71e556fb086 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/swing_animation.json @@ -0,0 +1,24 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:swing_animation", + "value": [ + {}, + { + "type": "none" + }, + { + "type": "stab" + }, + { + "type": "stab", + "duration": 2 + }, + { + "type": "none", + "duration": 100 + }, + { + "duration": 1 + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/tool.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/tool.json new file mode 100644 index 00000000000..c9db49b0750 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/tool.json @@ -0,0 +1,36 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:tool", + "value": [ + { + "rules": [] + }, + { + "rules": [ + { + "blocks": "stone", + "speed": 5.0, + "correct_for_drops": true + }, + { + "blocks": "#minecraft:air", + "speed": 0.001, + "correct_for_drops": false + } + ] + }, + { + "default_mining_speed": 0.12, + "damage_per_block": 4, + "can_destroy_blocks_in_creative": false, + "rules": [ + { + "blocks": [ + "minecraft:emerald_block", + "minecraft:netherite_block" + ] + } + ] + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/tooltip_display.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/tooltip_display.json new file mode 100644 index 00000000000..91cd9419ec8 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/tooltip_display.json @@ -0,0 +1,21 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:tooltip_display", + "value": [ + {}, + { + "hide_tooltip": true, + "hidden_components": [ + "minecraft:unbreakable", + "minecraft:jukebox_playable" + ] + }, + { + "hidden_components": [ + "minecraft:damage", + "minecraft:instrument", + "minecraft:unbreakable" + ] + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/tooltip_style.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/tooltip_style.json new file mode 100644 index 00000000000..37147720406 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/tooltip_style.json @@ -0,0 +1,9 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:tooltip_style", + "value": [ + "geyser:my_cool_tooltip_style", + "minecraft:default", + "geyser:warning_danger_error_fault_critical_evacuate_immediately" + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/trim.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/trim.json new file mode 100644 index 00000000000..9bc1f6a1d74 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/trim.json @@ -0,0 +1,14 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:trim", + "value": [ + { + "pattern": "minecraft:silence", + "material": "minecraft:redstone" + }, + { + "pattern": "minecraft:bolt", + "material": "minecraft:diamond" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/use_cooldown.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/use_cooldown.json new file mode 100644 index 00000000000..e6cbf243b2e --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/use_cooldown.json @@ -0,0 +1,17 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:use_cooldown", + "value": [ + { + "seconds": 5.0 + }, + { + "seconds": 99.0, + "cooldown_group": "minecraft:stone" + }, + { + "seconds": 42.1, + "cooldown_group": "geyser:the_cool_items" + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/use_effects.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/use_effects.json new file mode 100644 index 00000000000..36f8c1566a2 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/use_effects.json @@ -0,0 +1,30 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:use_effects", + "value": [ + { + "can_sprint": true + }, + { + "can_sprint": false, + "speed_multiplier": 0.2, + "interact_vibrations": true + }, + { + "can_sprint": true, + "speed_multiplier": 0.9, + "interact_vibrations": false + }, + { + "speed_multiplier": 0.0 + }, + { + "interact_vibrations": false + }, + { + "can_sprint": true, + "speed_multiplier": 0.1, + "interact_vibrations": true + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/use_remainder.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/use_remainder.json new file mode 100644 index 00000000000..3b43f32fb45 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/use_remainder.json @@ -0,0 +1,37 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:use_remainder", + "value": [ + { + "id": "minecraft:bowl", + "count": 1 + }, + { + "id": "minecraft:stick", + "count": 15 + }, + { + "id": "minecraft:diamond", + "count": 1, + "components": {} + }, + { + "id": "minecraft:emerald", + "count": 5, + "components": { + "minecraft:enchantment_glint_override": true, + "minecraft:item_name": { + "text": "A very cool diamond, sponsored by Geyser!", + "color": "#00ff0a" + } + } + }, + { + "id": "minecraft:enchanted_golden_apple", + "count": 64, + "components": { + "!minecraft:enchantment_glint_override": {} + } + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/weapon.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/weapon.json new file mode 100644 index 00000000000..1e7768a23a3 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/weapon.json @@ -0,0 +1,16 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:weapon", + "value": [ + { + "item_damage_per_attack": 5, + "disable_blocking_for_seconds": 100.0 + }, + { + "item_damage_per_attack": 2 + }, + { + "disable_blocking_for_seconds": 50.0 + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/writable_book_content.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/writable_book_content.json new file mode 100644 index 00000000000..6ac866c3388 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/writable_book_content.json @@ -0,0 +1,31 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:writable_book_content", + "value": [ + {}, + { + "pages": [] + }, + { + "pages": [ + "Hello everyone!", + "It is time to sit down for a story.", + "Once upon a time, I was sat down with my cat, Snapshot", + "Laying on the couch", + "Story is over!" + ] + }, + { + "pages": [ + { + "raw": "This is some very bad language", + "filtered": "This is **** language" + }, + { + "raw": "feature/* branches > feat/* branches", + "filtered": "feat/* branches ftw" + } + ] + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/written_book_content.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/written_book_content.json new file mode 100644 index 00000000000..a43e2a2ab91 --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/written_book_content.json @@ -0,0 +1,40 @@ +{ + "type": "geyser:component_hash", + "component": "minecraft:written_book_content", + "value": [ + { + "title": "My Cool Unit Test Story", + "author": "eclipse <3" + }, + { + "title": "Even Better Unit Test Story", + "author": "mr. boss", + "generation": 1 + }, + { + "title": "The Best Unit Test Story", + "author": "camotoy, the developer", + "pages": [ + "this book has actual content", + { + "text": "that's why this one is better than any other", + "italic": true + }, + { + "translate": "geyser.isnt_that_amazing" + } + ] + }, + { + "title": "The Final Unit Test Story", + "author": "the top of the water sprout", + "pages": [ + { + "raw": "give us the bedrock pdb files!!", + "filtered": "oh my! even more marketplace content! how exciting!" + } + ], + "resolved": true + } + ] +} diff --git a/bootstrap/mod/fabric/src/gametest/resources/fabric.mod.json b/bootstrap/mod/fabric/src/gametest/resources/fabric.mod.json new file mode 100644 index 00000000000..f10364fd49f --- /dev/null +++ b/bootstrap/mod/fabric/src/gametest/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "${id}-gametest", + "version": "${version}", + "name": "${name}-Gametest", + "description": "A bridge/proxy allowing you to connect to Minecraft: Java Edition servers with Minecraft: Bedrock Edition. ", + "authors": [ + "${author}" + ], + "contact": { + "website": "${url}", + "repo": "https://github.com/GeyserMC/Geyser" + }, + "license": "MIT", + "icon": "assets/geyser/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "org.geysermc.geyser.gametest.GeyserGameTestBootstrap" + ] + }, + "depends": { + "fabricloader": ">=0.18.2", + "fabric-api": "*", + "minecraft": "~26.1" + } +} diff --git a/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricBootstrap.java b/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricBootstrap.java index 96a60322bc4..ad242d54a85 100644 --- a/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricBootstrap.java +++ b/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricBootstrap.java @@ -58,7 +58,7 @@ public void onInitialize() { this.setServer(server); onGeyserEnable(); }); - } else { + } else if (!GeyserFabricPlatform.isGameTestServer()) { ClientLifecycleEvents.CLIENT_STOPPING.register(($)-> { onGeyserShutdown(); }); @@ -77,6 +77,11 @@ public void onInitialize() { this.onGeyserInitialize(); + if (GeyserFabricPlatform.isGameTestServer()) { + // Have to manually start Geyser for game test environment + GeyserImpl.start(); + } + var sourceConverter = CommandSourceConverter.layered( CommandSourceStack.class, id -> getServer().getPlayerList().getPlayer(id), @@ -93,6 +98,6 @@ public void onInitialize() { @Override public boolean isServer() { - return FabricLoader.getInstance().getEnvironmentType().equals(EnvType.SERVER); + return FabricLoader.getInstance().getEnvironmentType().equals(EnvType.SERVER) && !GeyserFabricPlatform.isGameTestServer(); } } diff --git a/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricPlatform.java b/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricPlatform.java index 345da4459e9..86631343502 100644 --- a/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricPlatform.java +++ b/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricPlatform.java @@ -34,6 +34,7 @@ import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.platform.mod.GeyserModBootstrap; import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform; +import org.geysermc.geyser.util.InternalPlatformType; import java.io.IOException; import java.io.InputStream; @@ -41,6 +42,7 @@ import java.util.Optional; public class GeyserFabricPlatform implements GeyserModPlatform { + private static Boolean isGameTestServer = null; private final ModContainer mod; @@ -50,7 +52,7 @@ public GeyserFabricPlatform() { @Override public @NonNull PlatformType platformType() { - return PlatformType.FABRIC; + return isGameTestServer() ? InternalPlatformType.GAMETEST : PlatformType.FABRIC; } @Override @@ -96,4 +98,12 @@ public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) return null; } } + + public static boolean isGameTestServer() { + if (isGameTestServer != null) { + return isGameTestServer; + } + // Property is from GameTestSystemProperties, FAPI internal + return isGameTestServer = System.getProperty("fabric-api.gametest") != null && FabricLoader.getInstance().isDevelopmentEnvironment(); + } } diff --git a/bootstrap/mod/fabric/src/main/resources/fabric.mod.json b/bootstrap/mod/fabric/src/main/resources/fabric.mod.json index f0d37091ba3..2932bfb3b18 100644 --- a/bootstrap/mod/fabric/src/main/resources/fabric.mod.json +++ b/bootstrap/mod/fabric/src/main/resources/fabric.mod.json @@ -25,6 +25,6 @@ "depends": { "fabricloader": ">=0.18.2", "fabric-api": "*", - "minecraft": ">=1.21.11" + "minecraft": "~26.1" } } diff --git a/bootstrap/mod/neoforge/build.gradle.kts b/bootstrap/mod/neoforge/build.gradle.kts index ba8daa13d09..0f56dd311bf 100644 --- a/bootstrap/mod/neoforge/build.gradle.kts +++ b/bootstrap/mod/neoforge/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { neoForge(libs.neoforge.minecraft) - api(project(":mod", configuration = "namedElements")) + api(project(":mod")) shadowBundle(project(path = ":mod", configuration = "transformProductionNeoForge")) shadowBundle(projects.core) @@ -51,7 +51,7 @@ dependencies { // Include all transitive deps of core via JiJ includeTransitive(projects.core) - modImplementation(libs.cloud.neoforge) + implementation(libs.cloud.neoforge) include(libs.cloud.neoforge) } @@ -62,20 +62,22 @@ tasks.withType { } tasks { - remapJar { + named("mergeShadowAndJarJar") { + from ( + zipTree( shadowJar.map { it.outputs.files.singleFile } ).matching { + exclude("LICENSE") + }, + zipTree( jar.map { it.outputs.files.singleFile } ).matching { + include("META-INF/jars/**") + include("META-INF/jarjar/**") + include("LICENSE") + } + ) archiveBaseName.set("Geyser-NeoForge") } - - remapModrinthJar { - archiveBaseName.set("geyser-neoforge") - } - - shadowJar { - mergeServiceFiles() - } } modrinth { loaders.add("neoforge") - uploadFile.set(tasks.getByPath("remapModrinthJar")) + uploadFile.set(tasks.getByName("renameModrinthJar")) } diff --git a/bootstrap/mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/bootstrap/mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml index 9169b382ed2..8c864b83ecc 100644 --- a/bootstrap/mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/bootstrap/mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -16,12 +16,12 @@ config = "geyser_neoforge.mixins.json" [[dependencies.geyser_neoforge]] modId="neoforge" type="required" - versionRange="[21.11.0-beta,)" + versionRange="[26.1.0.1-beta,)" ordering="NONE" side="BOTH" [[dependencies.geyser_neoforge]] modId="minecraft" type="required" - versionRange="[1.21.11,)" + versionRange="[26.1,)" ordering="NONE" side="BOTH" diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/ModPingPassthrough.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/ModPingPassthrough.java index 2b2e3b7368c..5c062f88851 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/ModPingPassthrough.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/ModPingPassthrough.java @@ -43,6 +43,7 @@ import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.ping.GeyserPingInfo; import org.geysermc.geyser.ping.IGeyserPingPassthrough; +import org.jspecify.annotations.NonNull; import java.net.InetSocketAddress; import java.util.Objects; @@ -94,9 +95,9 @@ private static class StatusInterceptor extends Connection { } @Override - public void send(Packet packet, @Nullable ChannelFutureListener channelFutureListener, boolean bl) { - if (packet instanceof ClientboundStatusResponsePacket statusResponse) { - status = statusResponse.status(); + public void send(@NonNull Packet packet, @Nullable ChannelFutureListener channelFutureListener, boolean bl) { + if (packet instanceof ClientboundStatusResponsePacket(ServerStatus serverStatus)) { + status = serverStatus; } super.send(packet, channelFutureListener, bl); } diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/command/ModCommandSource.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/command/ModCommandSource.java index f34164b5aca..ae49ce7381b 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/command/ModCommandSource.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/command/ModCommandSource.java @@ -58,7 +58,7 @@ public String name() { @Override public void sendMessage(@NonNull String message) { if (source.getEntity() instanceof ServerPlayer) { - ((ServerPlayer) source.getEntity()).displayClientMessage(Component.literal(message), false); + ((ServerPlayer) source.getEntity()).sendSystemMessage(Component.literal(message), false); } else { GeyserImpl.getInstance().getLogger().info(ChatColor.toANSI(message + ChatColor.RESET)); } @@ -68,7 +68,7 @@ public void sendMessage(@NonNull String message) { public void sendMessage(net.kyori.adventure.text.Component message) { if (source.getEntity() instanceof ServerPlayer player) { JsonElement jsonComponent = GsonComponentSerializer.gson().serializeToTree(message); - player.displayClientMessage(ComponentSerialization.CODEC.parse(RegistryOps.create(JsonOps.INSTANCE, player.registryAccess()), jsonComponent).getOrThrow(), false); + player.sendSystemMessage(ComponentSerialization.CODEC.parse(RegistryOps.create(JsonOps.INSTANCE, player.registryAccess()), jsonComponent).getOrThrow(), false); return; } GeyserCommandSource.super.sendMessage(message); diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/client/IntegratedServerMixin.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/client/IntegratedServerMixin.java index 8f218105f7f..035abd701a1 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/client/IntegratedServerMixin.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/client/IntegratedServerMixin.java @@ -61,8 +61,8 @@ private void onOpenToLan(GameType gameType, boolean cheatsAllowed, int port, Cal GeyserLocale.loadGeyserLocale(this.minecraft.options.languageCode); // Give indication that Geyser is loaded Objects.requireNonNull(this.minecraft.player); - this.minecraft.player.displayClientMessage(Component.literal(GeyserLocale.getPlayerLocaleString("geyser.core.start.ip_suppressed", - this.minecraft.options.languageCode, String.valueOf(GeyserImpl.getInstance().bedrockListener().port()))), false); + this.minecraft.player.sendSystemMessage(Component.literal(GeyserLocale.getPlayerLocaleString("geyser.core.start.ip_suppressed", + this.minecraft.options.languageCode, String.valueOf(GeyserImpl.getInstance().bedrockListener().port())))); } } diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/server/DedicatedServerMixin.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/server/DedicatedServerMixin.java index 84e3303c951..41cd48510b3 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/server/DedicatedServerMixin.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/server/DedicatedServerMixin.java @@ -32,17 +32,19 @@ import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.level.progress.LevelLoadListener; import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.storage.LevelStorageSource; import org.geysermc.geyser.platform.mod.GeyserServerPortGetter; import org.spongepowered.asm.mixin.Mixin; import java.net.Proxy; +import java.util.Optional; @Mixin(DedicatedServer.class) public abstract class DedicatedServerMixin extends MinecraftServer implements GeyserServerPortGetter { - public DedicatedServerMixin(Thread thread, LevelStorageSource.LevelStorageAccess levelStorageAccess, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer dataFixer, Services services, LevelLoadListener levelLoadListener) { - super(thread, levelStorageAccess, packRepository, worldStem, proxy, dataFixer, services, levelLoadListener); + public DedicatedServerMixin(Thread thread, LevelStorageSource.LevelStorageAccess levelStorageAccess, PackRepository packRepository, WorldStem worldStem, Optional gameRules, Proxy proxy, DataFixer dataFixer, Services services, LevelLoadListener levelLoadListener) { + super(thread, levelStorageAccess, packRepository, worldStem, gameRules, proxy, dataFixer, services, levelLoadListener, true); } @Override diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/world/GeyserModWorldManager.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/world/GeyserModWorldManager.java index f61e259bab5..b9837145fb6 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/world/GeyserModWorldManager.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/world/GeyserModWorldManager.java @@ -75,7 +75,7 @@ public int getBlockAt(GeyserSession session, int x, int y, int z) { } // Only loads active chunks, and doesn't delegate to main thread - ChunkAccess chunk = ((ServerChunkCache) level.getChunkSource()).chunkMap.getChunkToSend(ChunkPos.asLong(x >> 4, z >> 4)); + ChunkAccess chunk = ((ServerChunkCache) level.getChunkSource()).chunkMap.getChunkToSend(ChunkPos.pack(x >> 4, z >> 4)); if (chunk == null) { return 0; } diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index 2a3ec57fa9b..83d17521858 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -80,7 +80,8 @@ modrinth { uploadFile.set(tasks.getByPath("shadowJar")) gameVersions.addAll("1.16.5", "1.17", "1.17.1", "1.18", "1.18.1", "1.18.2", "1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4", "1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4", "1.20.5", "1.20.6", - "1.21", "1.21.1", "1.21.2", "1.21.3", "1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8") + "1.21", "1.21.1", "1.21.2", "1.21.3", "1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10", + "1.21.11") loaders.addAll("spigot", "paper") } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java index 69a50ea4b40..07ec9916f2f 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java @@ -34,15 +34,12 @@ import org.cloudburstmc.math.vector.Vector3i; import org.geysermc.erosion.bukkit.BukkitUtils; import org.geysermc.erosion.bukkit.SchedulerUtils; -import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.level.GameRule; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import java.util.List; -import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -86,38 +83,6 @@ public boolean hasOwnChunkCache() { return true; } - public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) { - org.bukkit.GameRule bukkitGameRule = org.bukkit.GameRule.getByName(gameRule.getJavaID()); - if (bukkitGameRule == null) { - GeyserImpl.getInstance().getLogger().debug("Unknown game rule " + gameRule.getJavaID()); - return gameRule.getDefaultBooleanValue(); - } - - Player bukkitPlayer = Objects.requireNonNull(Bukkit.getPlayer(session.getPlayerEntity().uuid())); - Object value = bukkitPlayer.getWorld().getGameRuleValue(bukkitGameRule); - if (value instanceof Boolean booleanValue) { - return booleanValue; - } - GeyserImpl.getInstance().getLogger().debug("Expected a bool for " + gameRule + " but got " + value); - return gameRule.getDefaultBooleanValue(); - } - - @Override - public int getGameRuleInt(GeyserSession session, GameRule gameRule) { - org.bukkit.GameRule bukkitGameRule = org.bukkit.GameRule.getByName(gameRule.getJavaID()); - if (bukkitGameRule == null) { - GeyserImpl.getInstance().getLogger().debug("Unknown game rule " + gameRule.getJavaID()); - return gameRule.getDefaultIntValue(); - } - Player bukkitPlayer = Objects.requireNonNull(Bukkit.getPlayer(session.getPlayerEntity().uuid())); - Object value = bukkitPlayer.getWorld().getGameRuleValue(bukkitGameRule); - if (value instanceof Integer intValue) { - return intValue; - } - GeyserImpl.getInstance().getLogger().debug("Expected an int for " + gameRule + " but got " + value); - return gameRule.getDefaultIntValue(); - } - @Override public GameMode getDefaultGameMode(GeyserSession session) { return GameMode.byId(Bukkit.getDefaultGameMode().ordinal()); diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 8545a527dad..4be9398ec12 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -15,7 +15,6 @@ repositories { maven("https://maven.fabricmc.net/") maven("https://maven.neoforged.net/releases") maven("https://maven.architectury.dev/") - maven("https://jitpack.io/") } dependencies { diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts index b448594f8e9..ced62cda491 100644 --- a/build-logic/settings.gradle.kts +++ b/build-logic/settings.gradle.kts @@ -13,4 +13,4 @@ rootProject.name = "build-logic" // Allow to download JVMs for toolchains plugins { id("org.gradle.toolchains.foojay-resolver-convention") version ("1.0.0") -} \ No newline at end of file +} diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts index 3311d5ddf03..df29a85c008 100644 --- a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts @@ -17,7 +17,7 @@ indra { mitLicense() javaVersions { - target(17) + target(21) } } diff --git a/build-logic/src/main/kotlin/geyser.modded-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.modded-conventions.gradle.kts index 13aa9000d6d..20be86c0a09 100644 --- a/build-logic/src/main/kotlin/geyser.modded-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.modded-conventions.gradle.kts @@ -1,12 +1,9 @@ @file:Suppress("UnstableApiUsage") -import net.fabricmc.loom.task.RemapJarTask -import org.gradle.kotlin.dsl.dependencies - plugins { id("geyser.platform-conventions") id("architectury-plugin") - id("dev.architectury.loom") + id("dev.architectury.loom-no-remap") } // These are provided by Minecraft/modded platforms already, no need to include them @@ -49,13 +46,13 @@ loom { indra { javaVersions { - target(21) + target(25) } } java { toolchain { - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(25) } } @@ -80,23 +77,33 @@ tasks { shadowJar { // Mirrors the example fabric project, otherwise tons of dependencies are shaded that shouldn't be configurations = listOf(project.configurations.getByName("shadowBundle")) - // The remapped shadowJar is the final desired mod jar - archiveVersion.set(project.version.toString()) - archiveClassifier.set("shaded") + archiveBaseName.set("${project.name}-shaded") + mergeServiceFiles() } - remapJar { - dependsOn(shadowJar) - inputFile.set(shadowJar.get().archiveFile) - archiveClassifier.set("") + // This task combines the output of the "jar" task, which includes JiJ dependencies, + // and the shadowJar for the final jar. + // thanks bluemap + // https://github.com/BlueMap-Minecraft/BlueMap/blob/cfe73115dc4d1bdd97bc659f41364da65a6a2179/implementations/fabric/build.gradle.kts#L93-L107 + register("mergeShadowAndJarJar") { + dependsOn( tasks.shadowJar, tasks.jar ) + // from sources / final name are configured in the respective projects archiveVersion.set("") + archiveClassifier.set("") } - register("remapModrinthJar", RemapJarTask::class) { - dependsOn(shadowJar) - inputFile.set(shadowJar.get().archiveFile) - archiveVersion.set(versionName(project)) - archiveClassifier.set("") + tasks.register("renameModrinthJar") { + val sourceJar = tasks.named("mergeShadowAndJarJar") + dependsOn(sourceJar) + + from(sourceJar.flatMap { it.archiveFile }) + into(layout.buildDirectory.dir("libs")) + + rename { "${versionName(project)}.jar" } + } + + build { + dependsOn(tasks.getByName("mergeShadowAndJarJar")) } } @@ -121,5 +128,4 @@ afterEvaluate { dependencies { minecraft(libs.minecraft) - mappings(loom.officialMojangMappings()) } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index fb62468d6d6..db1eabad7a5 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -168,7 +168,7 @@ val generateGitProperties = tasks.register("generateGitProperties") { gitPropertiesMap.forEach { (key, provider) -> props[key] = provider.get() } - + generatedPropsFile.get().asFile.apply { parentFile.mkdirs() writer().use { props.store(it, null) } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 8bd0270d105..e510e524357 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -102,6 +102,7 @@ import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.AssetUtils; import org.geysermc.geyser.util.CodeOfConductManager; +import org.geysermc.geyser.util.InternalPlatformType; import org.geysermc.geyser.util.JsonUtils; import org.geysermc.geyser.util.NewsHandler; import org.geysermc.geyser.util.VersionCheckUtils; @@ -261,6 +262,7 @@ public void initialize() { return; } + MinecraftLocale.downloadDeprecations(); MinecraftLocale.ensureEN_US(); String locale = GeyserLocale.getDefaultLocale(); if (!"en_us".equals(locale)) { @@ -335,6 +337,17 @@ private void startInstance() { logger.error("To change this, set \"disable-xbox-auth\" to \"false\" in Geyser's config file."); } + pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.pendingAuthenticationTimeout()); + + Packets.initGeyser(); + + BedrockDimension.changeBedrockNetherId(config.gameplay().netherRoofWorkaround()); // Apply End dimension ID workaround to Nether + + if (platformType() == InternalPlatformType.GAMETEST) { + // Note: not firing post reload/init events on gametest platform + return; + } + String geyserUdpPort = System.getProperty("geyserUdpPort", ""); String pluginUdpPort = geyserUdpPort.isEmpty() ? System.getProperty("pluginUdpPort", "") : geyserUdpPort; if ("-1".equals(pluginUdpPort)) { @@ -447,20 +460,14 @@ private void startInstance() { logger.warning("The use-direct-connection config option is deprecated. Please reach out to us on Discord if there's a reason it needs to be disabled."); } - pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.pendingAuthenticationTimeout()); - this.newsHandler = new NewsHandler(BRANCH, this.buildNumber()); - Packets.initGeyser(); - if (Epoll.isAvailable()) { this.erosionUnixListener = new UnixSocketClientListener(); } else { logger.debug("Epoll is not available; Erosion's Unix socket handling will not work."); } - BedrockDimension.changeBedrockNetherId(config.gameplay().netherRoofWorkaround()); // Apply End dimension ID workaround to Nether - int bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads", -1); if (bedrockThreadCount == -1) { // Copy the code from Netty's default thread count fallback @@ -598,7 +605,9 @@ public void disable() { } ResourcePackLoader.clear(); - CodeOfConductManager.trySave(); + if (platformType() != InternalPlatformType.GAMETEST) { + CodeOfConductManager.trySave(); + } this.setEnabled(false); } diff --git a/core/src/main/java/org/geysermc/geyser/Permissions.java b/core/src/main/java/org/geysermc/geyser/Permissions.java index b65a5af7a41..3e04d5e1a2d 100644 --- a/core/src/main/java/org/geysermc/geyser/Permissions.java +++ b/core/src/main/java/org/geysermc/geyser/Permissions.java @@ -39,7 +39,6 @@ public final class Permissions { public static final String CHECK_UPDATE = register("geyser.update"); public static final String SERVER_SETTINGS = register("geyser.settings.server"); - public static final String SETTINGS_GAMERULES = register("geyser.settings.gamerules"); private Permissions() { //no diff --git a/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java b/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java index 70bd21bc538..85e7dda4d46 100644 --- a/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java @@ -48,6 +48,7 @@ import org.geysermc.geyser.command.defaults.CustomOptionsCommand; import org.geysermc.geyser.command.defaults.DumpCommand; import org.geysermc.geyser.command.defaults.ExtensionsCommand; +import org.geysermc.geyser.command.defaults.GameruleCommand; import org.geysermc.geyser.command.defaults.HelpCommand; import org.geysermc.geyser.command.defaults.ListCommand; import org.geysermc.geyser.command.defaults.OffhandCommand; @@ -170,6 +171,7 @@ public CommandRegistry(GeyserImpl geyser, CommandManager cl registerBuiltInCommand(new PingCommand("ping", "geyser.commands.ping.desc", "geyser.command.ping")); registerBuiltInCommand(new CustomOptionsCommand("options", "geyser.commands.options.desc", "geyser.command.options")); registerBuiltInCommand(new QuickActionsCommand("quickactions", "geyser.commands.quickactions.desc", "geyser.command.quickactions")); + registerBuiltInCommand(new GameruleCommand("gamerules", "geyser.commands.gamerules.desc", "geyser.command.gamerules")); if (this.geyser.platformType() == PlatformType.STANDALONE) { registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop")); @@ -415,7 +417,7 @@ private List> createParamData(GeyserSession session, Comm for (var child : children) { collectiveData.addAll(createParamData(session, child)); } - collectiveData.forEach(list -> list.add(0, data)); + collectiveData.forEach(list -> list.addFirst(data)); return collectiveData; } } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/GameruleCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/GameruleCommand.java new file mode 100644 index 00000000000..8777aceb1c7 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/GameruleCommand.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026 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.command.defaults; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.util.TriState; +import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; +import org.geysermc.geyser.session.GeyserSession; +import org.incendo.cloud.context.CommandContext; + +public class GameruleCommand extends GeyserCommand { + + public GameruleCommand(@NonNull String name, @NonNull String description, @NonNull String permission) { + super(name, description, permission, TriState.NOT_SET, true, true); + } + + @Override + public void execute(CommandContext context) { + GeyserSession session = context.sender().connection(); + if (session != null) { + session.getGameRuleHandler().requestGamerules(); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/StatisticsCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/StatisticsCommand.java index eebb9170c00..86d22120029 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/StatisticsCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/StatisticsCommand.java @@ -46,7 +46,7 @@ public void execute(CommandContext context) { GeyserSession session = Objects.requireNonNull(context.sender().connection()); session.setWaitingForStatistics(true); - ServerboundClientCommandPacket packet = new ServerboundClientCommandPacket(ClientCommand.STATS); + ServerboundClientCommandPacket packet = new ServerboundClientCommandPacket(ClientCommand.REQUEST_STATS); session.sendDownstreamGamePacket(packet); } } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java index 92f67a767b4..7de94eaba50 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java @@ -49,16 +49,16 @@ public class VersionCommand extends GeyserCommand { static { List bedrockVersions = GameProtocol.SUPPORTED_BEDROCK_VERSIONS; if (bedrockVersions.size() > 1) { - SUPPORTED_BEDROCK_RANGE = bedrockVersions.get(0).versionString() + " - " + bedrockVersions.get(bedrockVersions.size() - 1).versionString(); + SUPPORTED_BEDROCK_RANGE = bedrockVersions.getFirst().versionString() + " - " + bedrockVersions.getLast().versionString(); } else { - SUPPORTED_BEDROCK_RANGE = bedrockVersions.get(0).versionString(); + SUPPORTED_BEDROCK_RANGE = bedrockVersions.getFirst().versionString(); } List javaVersions = GameProtocol.getJavaVersions(); if (javaVersions.size() > 1) { - SUPPORTED_JAVA_RANGE = javaVersions.get(0) + " - " + javaVersions.get(javaVersions.size() - 1); + SUPPORTED_JAVA_RANGE = javaVersions.getFirst() + " - " + javaVersions.getLast(); } else { - SUPPORTED_JAVA_RANGE = javaVersions.get(0); + SUPPORTED_JAVA_RANGE = javaVersions.getFirst(); } } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/LowercaseEnumSerializer.java b/core/src/main/java/org/geysermc/geyser/configuration/LowercaseEnumSerializer.java index 5414ab2e20d..7cd8c669c52 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/LowercaseEnumSerializer.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/LowercaseEnumSerializer.java @@ -40,7 +40,7 @@ */ final class LowercaseEnumSerializer extends ScalarSerializer> { LowercaseEnumSerializer() { - super(new TypeToken>() {}); + super(new TypeToken<>() {}); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index 400be1e6bd4..3dcb1e13bba 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -192,15 +192,12 @@ private JsonElement toGson(ConfigurationNode node) { private JsonElement convertRawScalar(ConfigurationNode node) { final @Nullable Object value = node.rawScalar(); - if (value == null) { - return JsonNull.INSTANCE; - } else if (value instanceof Number n) { - return new JsonPrimitive(n); - } else if (value instanceof Boolean b) { - return new JsonPrimitive(b); - } else { - return new JsonPrimitive(value.toString()); - } + return switch (value) { + case null -> JsonNull.INSTANCE; + case Number n -> new JsonPrimitive(n); + case Boolean b -> new JsonPrimitive(b); + default -> new JsonPrimitive(value.toString()); + }; } @Getter diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java index a2214f6116e..c866c5c9a5d 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -990,6 +990,7 @@ public final class EntityDefinitions { EntityDefinition ageableEntityBase = EntityDefinition.inherited(AgeableEntity::new, mobEntityBase) .addTranslator(MetadataTypes.BOOLEAN, AgeableEntity::setBaby) + .addTranslator(null) // Age locked .build(); // Extends ageable @@ -1019,12 +1020,14 @@ public final class EntityDefinitions { .height(0.7f).width(0.4f) .property(TemperatureVariantAnimal.TEMPERATE_VARIANT_PROPERTY) .addTranslator(MetadataTypes.CHICKEN_VARIANT, ChickenEntity::setVariant) + .addTranslator(null) // Sound variant .build(); COW = EntityDefinition.inherited(CowEntity::new, ageableEntityBase) .type(EntityType.COW) .height(1.4f).width(0.9f) .property(TemperatureVariantAnimal.TEMPERATE_VARIANT_PROPERTY) .addTranslator(MetadataTypes.COW_VARIANT, CowEntity::setVariant) + .addTranslator(null) // Sound variant .build(); FOX = EntityDefinition.inherited(FoxEntity::new, ageableEntityBase) .type(EntityType.FOX) @@ -1085,6 +1088,7 @@ public final class EntityDefinitions { .property(TemperatureVariantAnimal.TEMPERATE_VARIANT_PROPERTY) .addTranslator(MetadataTypes.INT, PigEntity::setBoost) .addTranslator(MetadataTypes.PIG_VARIANT, PigEntity::setVariant) + .addTranslator(null) // Sound variant .build(); POLAR_BEAR = EntityDefinition.inherited(PolarBearEntity::new, ageableEntityBase) .type(EntityType.POLAR_BEAR) @@ -1241,6 +1245,7 @@ public final class EntityDefinitions { .addTranslator(MetadataTypes.BOOLEAN, CatEntity::setResting) .addTranslator(null) // "resting state one" //TODO .addTranslator(MetadataTypes.INT, CatEntity::setCollarColor) + .addTranslator(null) // Sound variant .build(); PARROT = EntityDefinition.inherited(ParrotEntity::new, tameableEntityBase) .type(EntityType.PARROT) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java index 91d4f385434..aabac90cd87 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java @@ -197,7 +197,7 @@ public void tick() { return; } - Entity rower = passengers.get(0); + Entity rower = passengers.getFirst(); if (rower == null) { return; } @@ -238,7 +238,7 @@ public float getVehicleSpeed() { @Override public boolean shouldSimulateMovement() { - return !session.isInClientPredictedVehicle() && !passengers.isEmpty() && this.session.getPlayerEntity() == passengers.get(0); + return !session.isInClientPredictedVehicle() && !passengers.isEmpty() && this.session.getPlayerEntity() == passengers.getFirst(); } /** diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index ce5fd44e449..b44766f3603 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -37,7 +37,6 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; -import org.cloudburstmc.protocol.bedrock.data.entity.EntityProperty; import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket; import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket; @@ -727,7 +726,7 @@ protected void updatePassengerOffsets() { */ protected void updateMountOffset() { if (vehicle != null) { - boolean rider = vehicle.getPassengers().get(0) == this; + boolean rider = vehicle.getPassengers().getFirst() == this; EntityUtils.updateMountOffset(this, vehicle, rider, true, vehicle.getPassengers().indexOf(this), vehicle.getPassengers().size()); updateBedrockMetadata(); } @@ -860,10 +859,9 @@ public void updatePropertiesBatched(Consumer consumer, boo @Override public void update(@NonNull GeyserEntityProperty property, @Nullable T value) { Objects.requireNonNull(property, "property must not be null!"); - if (!(property instanceof PropertyType)) { + if (!(property instanceof PropertyType propertyType)) { throw new IllegalArgumentException("Invalid property implementation! Got: " + property.getClass().getSimpleName()); } - PropertyType propertyType = (PropertyType) property; int index = propertyDefinitions.getPropertyIndex(property.identifier().toString()); if (index < 0) { throw new IllegalArgumentException("No property with the name " + property.identifier() + " has been registered."); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEggEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEggEntity.java index b7c6f58699e..84a2664cba3 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEggEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEggEntity.java @@ -58,12 +58,14 @@ public void setItem(EntityMetadata entityMetadata) { } private static TemperatureVariantAnimal.BuiltInVariant getVariantOrFallback(GeyserSession session, GeyserItemStack stack) { - Holder holder = stack.getComponent(DataComponentTypes.CHICKEN_VARIANT); - if (holder != null) { - Key chickenVariant = holder.getOrCompute(id -> JavaRegistries.CHICKEN_VARIANT.key(session, id)); - for (var variant : TemperatureVariantAnimal.BuiltInVariant.values()) { - if (chickenVariant.asMinimalString().equalsIgnoreCase(variant.name())) { - return variant; + Integer id = stack.getComponent(DataComponentTypes.CHICKEN_VARIANT); + if (id != null) { + Key chickenVariant = JavaRegistries.CHICKEN_VARIANT.key(session, id); + if (chickenVariant != null) { + for (var variant : TemperatureVariantAnimal.BuiltInVariant.values()) { + if (chickenVariant.asMinimalString().equalsIgnoreCase(variant.name())) { + return variant; + } } } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java index 846934bb7c0..21239b4ea35 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java @@ -62,7 +62,7 @@ protected float getAdultSize() { * The scale that should be used when this entity is a baby. */ protected float getBabySize() { - return 0.55f; + return 0.5f; } public boolean isBaby() { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/DolphinEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/DolphinEntity.java index c2d33add87e..2217968d983 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/DolphinEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/DolphinEntity.java @@ -43,6 +43,11 @@ public boolean canBeLeashed() { return true; } + @Override + protected float getBabySize() { + return 0.65f; + } + @NonNull @Override protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ArmadilloEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ArmadilloEntity.java index 0f896c03f46..6e24fa3f813 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ArmadilloEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ArmadilloEntity.java @@ -80,6 +80,11 @@ public void onPeeking() { } } + @Override + protected float getBabySize() { + return 0.6f; + } + @Override @Nullable protected Tag getFoodTag() { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/BeeEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/BeeEntity.java index 4c5849a3c06..2def2852d34 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/BeeEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/BeeEntity.java @@ -70,7 +70,7 @@ public void setBeeFlags(ByteEntityMetadata entityMetadata) { public void setAngerTime(LongEntityMetadata entityMetadata) { // Converting "anger time" to a boolean long time = entityMetadata.getPrimitiveValue(); - setFlag(EntityFlag.ANGRY, time > 0 && time - session.getWorldTicks() > 0); + setFlag(EntityFlag.ANGRY, time > 0 && time - session.getGameTicks() > 0); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java index 551abd5940a..8d6a7eb6ece 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java @@ -219,7 +219,7 @@ public boolean shouldSimulateMovement() { } private Entity getFirstPassenger() { - return passengers.isEmpty() ? null : passengers.get(0); + return passengers.isEmpty() ? null : passengers.getFirst(); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index 9f80795ef65..c44f90b0fd2 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -176,7 +176,7 @@ public float getVehicleSpeed() { } private @Nullable PlayerEntity getPlayerPassenger() { - if (getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) instanceof PlayerEntity playerEntity) { + if (getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.getFirst() instanceof PlayerEntity playerEntity) { return playerEntity; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java index ada6060b44f..d4158b01f79 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java @@ -136,7 +136,7 @@ public float getVehicleSpeed() { } private @Nullable PlayerEntity getPlayerPassenger() { - if (getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) instanceof PlayerEntity playerEntity) { + if (getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.getFirst() instanceof PlayerEntity playerEntity) { return playerEntity; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java index 349b27f0b74..278d5461941 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java @@ -343,6 +343,6 @@ public float getVehicleSpeed() { @Override public boolean shouldSimulateMovement() { - return getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) == session.getPlayerEntity() && !session.isInClientPredictedVehicle(); + return getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.getFirst() == session.getPlayerEntity() && !session.isInClientPredictedVehicle(); } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java index b90b85cad81..948a1d71c32 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java @@ -149,7 +149,7 @@ public VehicleComponent getVehicleComponent() { @Override public boolean shouldSimulateMovement() { - return getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.get(0) == session.getPlayerEntity(); + return getFlag(EntityFlag.SADDLED) && !passengers.isEmpty() && passengers.getFirst() == session.getPlayerEntity(); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/nautilus/AbstractNautilusEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/nautilus/AbstractNautilusEntity.java index a7d0fa5fe6c..79969a68c73 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/nautilus/AbstractNautilusEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/nautilus/AbstractNautilusEntity.java @@ -168,7 +168,7 @@ public float getVehicleSpeed() { @Override public boolean shouldSimulateMovement() { - return getFlag(EntityFlag.SADDLED) && !this.passengers.isEmpty() && this.passengers.get(0) == session.getPlayerEntity(); + return getFlag(EntityFlag.SADDLED) && !this.passengers.isEmpty() && this.passengers.getFirst() == session.getPlayerEntity(); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/WolfEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/WolfEntity.java index 9f991634ce3..ff3012cfc42 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/WolfEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/WolfEntity.java @@ -118,7 +118,7 @@ private void updateCollarColor() { // 1.16+ public void setWolfAngerTime(LongEntityMetadata entityMetadata) { long time = entityMetadata.getPrimitiveValue(); - boolean angry = time > 0 && time - session.getWorldTicks() > 0; + boolean angry = time > 0 && time - session.getGameTicks() > 0; setFlag(EntityFlag.ANGRY, angry); dirtyMetadata.put(EntityDataTypes.COLOR, angry ? (byte) 0 : collarColor); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java index cbd42333c89..2cbc2e3a533 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/CamelVehicleComponent.java @@ -129,7 +129,7 @@ protected Vector2f getRiddenRotation() { */ private boolean isStationary() { // Java checks if sitting using lastPoseTick - return this.lastPoseTick < 0 || vehicle.getSession().getWorldTicks() < this.lastPoseTick + STANDING_TICKS; + return this.lastPoseTick < 0 || vehicle.getSession().getGameTicks() < this.lastPoseTick + STANDING_TICKS; } @Override diff --git a/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java b/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java index 7f1104dc253..36fd16da594 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java @@ -30,10 +30,13 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; +import net.kyori.adventure.key.Key; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.inventory.item.DyeColor; +import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.registry.Registries; @@ -44,17 +47,24 @@ import org.geysermc.geyser.session.cache.registry.JavaRegistries; import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.translator.item.ItemTranslator; +import org.geysermc.geyser.util.ColorUtils; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.CompositeSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.DyedSlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemSlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemStackSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.OnlyWithComponentSlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.TagSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.WithAnyPotionSlotDisplay; import java.util.HashMap; +import java.util.List; import java.util.function.Supplier; @Data @@ -101,17 +111,49 @@ private GeyserItemStack(@Nullable ComponentCache componentCache, int javaId, int } public static @NonNull GeyserItemStack from(@Nullable GeyserSession session, @NonNull SlotDisplay slotDisplay) { - if (slotDisplay instanceof EmptySlotDisplay) { - return GeyserItemStack.EMPTY; - } - if (slotDisplay instanceof ItemSlotDisplay itemSlotDisplay) { - return GeyserItemStack.of(session, itemSlotDisplay.item(), 1); - } - if (slotDisplay instanceof ItemStackSlotDisplay itemStackSlotDisplay) { - return GeyserItemStack.from(session, itemStackSlotDisplay.itemStack()); - } - GeyserImpl.getInstance().getLogger().warning("Unsure how to convert to ItemStack: " + slotDisplay); - return GeyserItemStack.EMPTY; + return switch (slotDisplay) { + case EmptySlotDisplay ignored -> GeyserItemStack.EMPTY; + case ItemSlotDisplay(int itemId) -> GeyserItemStack.of(session, itemId, 1); + case ItemStackSlotDisplay(ItemStack itemStack) -> GeyserItemStack.from(session, itemStack); + // Just create the first display + case CompositeSlotDisplay(List contents) -> contents.isEmpty() ? GeyserItemStack.EMPTY : from(session, contents.getFirst()); + case TagSlotDisplay(Key tag) -> { + // Again, just create an itemstack of the first item in the tag, if possible + if (session == null) { + yield GeyserItemStack.EMPTY; + } + int[] itemTag = session.getTagCache().getRaw(new Tag<>(JavaRegistries.ITEM, tag)); + if (itemTag.length == 0) { + yield GeyserItemStack.EMPTY; + } + yield GeyserItemStack.of(session, itemTag[0], 1); + } + case DyedSlotDisplay(SlotDisplay dye, SlotDisplay target) -> { + // This probably works... MC does it a little differently + // This whole method is kind of cursed anyway + DyeColor dyeColor = DyeColor.getOrDefault(from(session, dye).getComponent(DataComponentTypes.DYE), DyeColor.WHITE); + GeyserItemStack targetStack = from(session, target); + targetStack.getOrCreateComponents().put(DataComponentTypes.DYED_COLOR, ColorUtils.mixDyes(targetStack.getComponent(DataComponentTypes.DYED_COLOR), List.of(dyeColor))); + yield targetStack; + } + case OnlyWithComponentSlotDisplay(SlotDisplay source, DataComponentType component) -> { + GeyserItemStack stack = from(session, source); + if (stack.has(component)) { + yield stack; + } + yield GeyserItemStack.EMPTY; + } + case WithAnyPotionSlotDisplay(SlotDisplay display) -> { + GeyserItemStack stack = from(session, display); + // Just create one item stack with the first potion, WATER + stack.getOrCreateComponents().put(DataComponentTypes.POTION_CONTENTS, Potion.WATER.toComponent()); + yield stack; + } + default -> { + GeyserImpl.getInstance().getLogger().warning("Unsure how to convert to ItemStack: " + slotDisplay); + yield GeyserItemStack.EMPTY; + } + }; } public int getJavaId() { @@ -155,6 +197,13 @@ public boolean isSameItem(GeyserItemStack other) { return isEmpty() ? null : components; } + /** + * @return whether this GeyserItemStack has the given {@code component}. + */ + public boolean has(DataComponentType component) { + return !isEmpty() && asItem().gatherComponents(componentCache, components).contains(component); + } + /** * @return whether this GeyserItemStack has any component modifications additional to * the base item components. diff --git a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java index 5c73fbbbcd4..3a7f3a37ba4 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java @@ -327,7 +327,7 @@ private void simulateAction(ClickAction action) { int amountToAddInBundle = Math.min(BundleInventoryTranslator.capacityForItemStack(bundleWeight, cursor), cursor.getAmount()); GeyserItemStack toInsertInBundle = cursor.copy(amountToAddInBundle); if (executionBegan) { - clicked.getBundleData().contents().add(0, toInsertInBundle); + clicked.getBundleData().contents().addFirst(toInsertInBundle); session.getBundleCache().onItemAdded(clicked); // Must be run before onSlotItemChange as the latter exports an ItemStack from the bundle } onSlotItemChange(action.slot, clicked); @@ -339,7 +339,7 @@ private void simulateAction(ClickAction action) { amountToAddInBundle = Math.min(BundleInventoryTranslator.capacityForItemStack(bundleWeight, clicked), clicked.getAmount()); toInsertInBundle = clicked.copy(amountToAddInBundle); if (executionBegan) { - cursor.getBundleData().contents().add(0, toInsertInBundle); + cursor.getBundleData().contents().addFirst(toInsertInBundle); session.getBundleCache().onItemAdded(cursor); } sub(action.slot, clicked, amountToAddInBundle); @@ -349,7 +349,7 @@ private void simulateAction(ClickAction action) { // Bundle should be in player's hand. GeyserItemStack itemStack = cursor.getBundleData() .contents() - .remove(0); + .removeFirst(); if (executionBegan) { session.getBundleCache().onItemRemoved(cursor, 0); } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/item/DyeColor.java b/core/src/main/java/org/geysermc/geyser/inventory/item/DyeColor.java index e2649a34310..28c9fa28b1b 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/item/DyeColor.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/item/DyeColor.java @@ -27,34 +27,38 @@ import lombok.Getter; import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.util.ColorUtils; import java.util.Locale; @Getter public enum DyeColor { - WHITE, - ORANGE, - MAGENTA, - LIGHT_BLUE, - YELLOW, - LIME, - PINK, - GRAY, - LIGHT_GRAY, - CYAN, - PURPLE, - BLUE, - BROWN, - GREEN, - RED, - BLACK; + WHITE(0xf9fffe), + ORANGE(0xf9801d), + MAGENTA(0xc74ebd), + LIGHT_BLUE(0x3ab3da), + YELLOW(0xfed83d), + LIME(0x80c71f), + PINK(0xf38baa), + GRAY(0x474f52), + LIGHT_GRAY(0x9d9d97), + CYAN(0x169c9c), + PURPLE(0x8932b8), + BLUE(0x3c44aa), + BROWN(0x835432), + GREEN(0x5e7c16), + RED(0xb02e26), + BLACK(0x1d1d21); private static final DyeColor[] VALUES = values(); private final String javaIdentifier; + // Take from Mojang's own DyeColor: https://mcsrc.dev/1/26.1.2/net/minecraft/world/item/DyeColor + private final int textureDiffuseColor; - DyeColor() { + DyeColor(int textureDiffuseColor) { this.javaIdentifier = this.name().toLowerCase(Locale.ROOT); + this.textureDiffuseColor = ColorUtils.argbOpaque(textureDiffuseColor); } public static @Nullable DyeColor getById(int id) { @@ -64,6 +68,14 @@ public enum DyeColor { return null; } + public static DyeColor getOrDefault(@Nullable Integer id, DyeColor defaultValue) { + if (id == null) { + return defaultValue; + } + DyeColor color = getById(id); + return color == null ? defaultValue : color; + } + public static @Nullable DyeColor getByJavaIdentifier(String javaIdentifier) { for (DyeColor dyeColor : VALUES) { if (dyeColor.javaIdentifier.equals(javaIdentifier)) { diff --git a/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java b/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java index fb2c105eb9d..6a05f5da427 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java @@ -35,7 +35,8 @@ import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.MinecraftKey; import org.geysermc.geyser.util.SoundUtils; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.InstrumentComponent; +import org.geysermc.mcprotocollib.protocol.data.game.Holder; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.Instrument; import org.geysermc.mcprotocollib.protocol.data.game.level.sound.BuiltinSound; import java.util.Locale; @@ -90,20 +91,16 @@ static int bedrockIdToJava(GeyserSession session, int id) { } // TODO test in 1.21.5 - static GeyserInstrument fromComponent(GeyserSession session, InstrumentComponent component) { - if (component.instrumentLocation() != null) { - return session.getRegistryCache().registry(JavaRegistries.INSTRUMENT).byKey(component.instrumentLocation()); - } else if (component.instrumentHolder() != null) { - if (component.instrumentHolder().isId()) { - return session.getRegistryCache().registry(JavaRegistries.INSTRUMENT).byId(component.instrumentHolder().id()); - } - InstrumentComponent.Instrument custom = component.instrumentHolder().custom(); - return new Wrapper(custom, session.locale()); + static GeyserInstrument fromComponent(GeyserSession session, Holder component) { + if (component.isCustom()) { + return new Wrapper(component.custom(), session.locale()); + } else if (component.isId()) { + return session.getRegistryCache().registry(JavaRegistries.INSTRUMENT).byId(component.id()); } - throw new IllegalStateException("InstrumentComponent must have either a location or a holder"); + throw new IllegalStateException("Instrument must either be custom or have an id"); } - record Wrapper(InstrumentComponent.Instrument instrument, String locale) implements GeyserInstrument { + record Wrapper(Instrument instrument, String locale) implements GeyserInstrument { @Override public String soundEvent() { return instrument.soundEvent().getName(); diff --git a/core/src/main/java/org/geysermc/geyser/inventory/recipe/RecipeUtil.java b/core/src/main/java/org/geysermc/geyser/inventory/recipe/RecipeUtil.java index 8a9133adfec..0e82c412d0d 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/recipe/RecipeUtil.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/recipe/RecipeUtil.java @@ -39,6 +39,8 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.DefaultDescriptor; import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount; import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemTagDescriptor; +import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.type.BedrockRequiresTagItem; import org.geysermc.geyser.item.type.Item; @@ -49,12 +51,17 @@ import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.translator.item.ItemTranslator; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.CompositeSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.DyedSlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemSlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemStackSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.OnlyWithComponentSlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.TagSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.WithAnyPotionSlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.WithRemainderSlotDisplay; import java.util.ArrayList; @@ -87,12 +94,13 @@ public class RecipeUtil { private static final ThreadLocal>>> TAG_TO_ITEM_DESCRIPTOR_CACHE = ThreadLocal.withInitial(() -> IntObjectMutablePair.of(0, new Object2ObjectOpenHashMap<>())); public static List translateToInput(GeyserSession session, SlotDisplay slotDisplay) { + // TODO possible code duplication with InventoryUtils#acceptsAsInput? if (slotDisplay instanceof EmptySlotDisplay) { return Collections.singletonList(ItemDescriptorWithCount.EMPTY); } if (slotDisplay instanceof CompositeSlotDisplay composite) { if (composite.contents().size() == 1) { - return translateToInput(session, composite.contents().get(0)); + return translateToInput(session, composite.contents().getFirst()); } // Try and see if the contents match a tag. @@ -102,13 +110,12 @@ public static List translateToInput(GeyserSession sessi for (int i = 0; i < contents.size(); i++) { SlotDisplay subDisplay = contents.get(i); int id; - if (subDisplay instanceof ItemSlotDisplay item) { - id = item.item(); - } else if (!(subDisplay instanceof ItemStackSlotDisplay itemStackSlotDisplay)) { + if (subDisplay instanceof ItemSlotDisplay(int itemSlotId)) { + id = itemSlotId; + } else if (!(subDisplay instanceof ItemStackSlotDisplay(ItemStack itemStack))) { id = -1; - } else if (itemStackSlotDisplay.itemStack().getAmount() == 1 - && itemStackSlotDisplay.itemStack().getDataComponentsPatch() == null) { - id = itemStackSlotDisplay.itemStack().getId(); + } else if (itemStack.getAmount() == 1 && itemStack.getDataComponentsPatch() == null) { + id = itemStack.getId(); } else { id = -1; } @@ -132,15 +139,14 @@ public static List translateToInput(GeyserSession sessi // Don't need to worry about what will stay in the crafting table after crafting for the purposes of sending recipes to Bedrock return translateToInput(session, remainder.input()); } - if (slotDisplay instanceof ItemSlotDisplay itemSlot) { - return Collections.singletonList(fromItem(session, itemSlot.item())); + if (slotDisplay instanceof ItemSlotDisplay(int itemId)) { + return Collections.singletonList(fromItem(session, itemId)); } - if (slotDisplay instanceof ItemStackSlotDisplay itemStackSlot) { - ItemData item = ItemTranslator.translateToBedrock(session, itemStackSlot.itemStack()); + if (slotDisplay instanceof ItemStackSlotDisplay(ItemStack itemStack)) { + ItemData item = ItemTranslator.translateToBedrock(session, itemStack); return Collections.singletonList(ItemDescriptorWithCount.fromItem(item)); } - if (slotDisplay instanceof TagSlotDisplay tagSlot) { - Key tag = tagSlot.tag(); + if (slotDisplay instanceof TagSlotDisplay(Key tag)) { int[] items = session.getTagCache().getRaw(new Tag<>(JavaRegistries.ITEM, tag)); // I don't like this... if (items == null || items.length == 0) { return Collections.singletonList(ItemDescriptorWithCount.EMPTY); @@ -170,22 +176,39 @@ public static List translateToInput(GeyserSession sessi }); } } + if (slotDisplay instanceof OnlyWithComponentSlotDisplay(SlotDisplay source, DataComponentType ignored)) { + // FIXME I don't think there's a proper way to do this? How do we tell bedrock that the item has to have a certain component? + return translateToInput(session, source); + } + if (slotDisplay instanceof WithAnyPotionSlotDisplay(SlotDisplay display)) { + // Not sure how I feel about this... + GeyserItemStack stack = GeyserItemStack.from(session, display); + List itemDescriptors = new ArrayList<>(); + for (Potion potion : Potion.VALUES) { + stack.getOrCreateComponents().put(DataComponentTypes.POTION_CONTENTS, potion.toComponent()); + itemDescriptors.add(ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, stack))); + } + return Collections.unmodifiableList(itemDescriptors); + } session.getGeyser().getLogger().warning("Unimplemented slot display type for input: " + slotDisplay); return null; } - public static Pair translateToOutput(GeyserSession session, SlotDisplay slotDisplay) { + public static @Nullable Pair translateToOutput(GeyserSession session, SlotDisplay slotDisplay) { + // TODO possible code duplication with GeyserItemStack#from? if (slotDisplay instanceof EmptySlotDisplay) { return null; } - if (slotDisplay instanceof ItemSlotDisplay itemSlot) { - int item = itemSlot.item(); + if (slotDisplay instanceof ItemSlotDisplay(int item)) { return Pair.of(Registries.JAVA_ITEMS.get(item), ItemTranslator.translateToBedrock(session, new ItemStack(item))); } - if (slotDisplay instanceof ItemStackSlotDisplay itemStackSlot) { - ItemStack stack = itemStackSlot.itemStack(); + if (slotDisplay instanceof ItemStackSlotDisplay(ItemStack stack)) { return Pair.of(Registries.JAVA_ITEMS.get(stack.getId()), ItemTranslator.translateToBedrock(session, stack)); } + if (slotDisplay instanceof DyedSlotDisplay || slotDisplay instanceof OnlyWithComponentSlotDisplay || slotDisplay instanceof WithAnyPotionSlotDisplay) { + GeyserItemStack stack = GeyserItemStack.from(session, slotDisplay); + return stack.isEmpty() ? null : Pair.of(stack.asItem(), ItemTranslator.translateToBedrock(session, stack)); + } session.getGeyser().getLogger().warning("Unimplemented slot display type for output: " + slotDisplay); return null; } @@ -248,7 +271,7 @@ public static Pair>, ItemData> combinations(G continue; } inputs.add(translated); - if (translated.size() != 1 || translated.get(0) != ItemDescriptorWithCount.EMPTY) { + if (translated.size() != 1 || translated.getFirst() != ItemDescriptorWithCount.EMPTY) { empty = false; } complexInputs |= translated.size() > 1; @@ -291,7 +314,7 @@ public static Pair>, ItemData> combinations(G if (descriptors.size() > current) { return descriptors.get(current); } - return descriptors.get(0); + return descriptors.getFirst(); }).toList()); } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/recipe/TrimRecipe.java b/core/src/main/java/org/geysermc/geyser/inventory/recipe/TrimRecipe.java index 47468b1e660..51b56c2ead0 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/recipe/TrimRecipe.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/recipe/TrimRecipe.java @@ -40,7 +40,6 @@ import org.geysermc.mcprotocollib.protocol.data.game.Holder; import org.geysermc.mcprotocollib.protocol.data.game.item.component.ArmorTrim; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.ProvidesTrimMaterial; import java.util.HashMap; import java.util.Map; @@ -49,7 +48,7 @@ * Stores information on trim materials and patterns, including smithing armor hacks for pre-1.20. */ public final class TrimRecipe { - private static final Map trimMaterialProviders = new HashMap<>(); + private static final Map, Item> trimMaterialProviders = new HashMap<>(); // For CraftingDataPacket public static final String ID = "minecraft:smithing_armor_trim"; @@ -69,9 +68,8 @@ public static TrimMaterial readTrimMaterial(RegistryEntryContext context) { int networkId = context.getNetworkId(context.id()); ItemMapping trimItem = null; if (context.session().isPresent()) { - for (ProvidesTrimMaterial provider : materialProviders().keySet()) { - Holder materialHolder = provider.materialHolder(); - if (context.id().equals(provider.materialLocation()) || (materialHolder != null && materialHolder.isId() && materialHolder.id() == networkId)) { + for (Holder provider : materialProviders().keySet()) { + if ((provider.isCustom() && context.id().asString().equals(provider.custom().assetBase())) || (provider.isId() && provider.id() == networkId)) { Item javaItem = materialProviders().get(provider); if (javaItem != null) { trimItem = context.session().get().getItemMappings().getMapping(javaItem); @@ -115,10 +113,10 @@ private TrimRecipe() { } // Lazy initialise - private static Map materialProviders() { + private static Map, Item> materialProviders() { if (trimMaterialProviders.isEmpty()) { for (Item item : Registries.JAVA_ITEMS.get()) { - ProvidesTrimMaterial provider = item.getComponent(null, DataComponentTypes.PROVIDES_TRIM_MATERIAL); + Holder provider = item.getComponent(null, DataComponentTypes.PROVIDES_TRIM_MATERIAL); if (provider != null) { trimMaterialProviders.put(provider, item); } diff --git a/core/src/main/java/org/geysermc/geyser/item/Items.java b/core/src/main/java/org/geysermc/geyser/item/Items.java index 83b92034d06..fe6cc2fe4b5 100644 --- a/core/src/main/java/org/geysermc/geyser/item/Items.java +++ b/core/src/main/java/org/geysermc/geyser/item/Items.java @@ -296,6 +296,7 @@ public final class Items { public static final Item RED_WOOL = register(new BlockItem(builder(), Blocks.RED_WOOL)); public static final Item BLACK_WOOL = register(new BlockItem(builder(), Blocks.BLACK_WOOL)); public static final Item DANDELION = register(new BlockItem(builder(), Blocks.DANDELION)); + public static final Item GOLDEN_DANDELION = register(new BlockItem(builder(), Blocks.GOLDEN_DANDELION)); public static final Item OPEN_EYEBLOSSOM = register(new BlockItem(builder(), Blocks.OPEN_EYEBLOSSOM)); public static final Item CLOSED_EYEBLOSSOM = register(new BlockItem(builder(), Blocks.CLOSED_EYEBLOSSOM)); public static final Item POPPY = register(new BlockItem(builder(), Blocks.POPPY)); @@ -1193,7 +1194,7 @@ public final class Items { public static final Item BLAZE_POWDER = register(new Item("blaze_powder", builder())); public static final Item MAGMA_CREAM = register(new Item("magma_cream", builder())); public static final Item BREWING_STAND = register(new BlockItem(builder(), Blocks.BREWING_STAND)); - public static final Item CAULDRON = register(new BlockItem(builder(), Blocks.CAULDRON, Blocks.LAVA_CAULDRON, Blocks.POWDER_SNOW_CAULDRON, Blocks.WATER_CAULDRON)); + public static final Item CAULDRON = register(new BlockItem(builder(), Blocks.CAULDRON, Blocks.POWDER_SNOW_CAULDRON, Blocks.WATER_CAULDRON, Blocks.LAVA_CAULDRON)); public static final Item ENDER_EYE = register(new Item("ender_eye", builder())); public static final Item GLISTERING_MELON_SLICE = register(new Item("glistering_melon_slice", builder())); public static final Item CHICKEN_SPAWN_EGG = register(new SpawnEggItem("chicken_spawn_egg", builder())); diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/ComponentHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/ComponentHasher.java index 2bb487afa4e..890b5ba4dcc 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/ComponentHasher.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/ComponentHasher.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.item.hashing; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.KeybindComponent; import net.kyori.adventure.text.NBTComponent; import net.kyori.adventure.text.ObjectComponent; @@ -33,6 +34,7 @@ import net.kyori.adventure.text.SelectorComponent; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.TranslationArgument; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; @@ -40,10 +42,11 @@ import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextDecoration; -import net.kyori.adventure.text.object.ObjectContents; import net.kyori.adventure.text.object.PlayerHeadObjectContents; +import org.geysermc.geyser.item.hashing.data.NbtComponentType; import org.geysermc.geyser.item.hashing.data.ObjectContentsType; +import java.util.function.Function; import java.util.function.Supplier; /** @@ -58,6 +61,8 @@ public MinecraftHasher get() { } }); + MinecraftHasher COMPONENT_LIKE = COMPONENT.cast(ComponentLike::asComponent); + MinecraftHasher PROFILE_PROPERTY = MinecraftHasher.mapBuilder(builder -> builder .accept("name", MinecraftHasher.STRING, PlayerHeadObjectContents.ProfileProperty::name) .accept("value", MinecraftHasher.STRING, PlayerHeadObjectContents.ProfileProperty::value) @@ -148,14 +153,17 @@ public MinecraftHasher get() { MinecraftHasher TRANSLATABLE_COMPONENT = component(builder -> builder .accept("translate", MinecraftHasher.STRING, TranslatableComponent::key) - .optionalNullable("fallback", MinecraftHasher.STRING, TranslatableComponent::fallback)); // Arguments are probably not possible + .optionalNullable("fallback", MinecraftHasher.STRING, TranslatableComponent::fallback) + .optionalList("with", COMPONENT_LIKE.cast(TranslationArgument::asTranslationArgument), TranslatableComponent::arguments)); MinecraftHasher KEYBIND_COMPONENT = component(builder -> builder .accept("keybind", MinecraftHasher.STRING, component -> component.keybind())); MinecraftHasher SCORE_COMPONENT = component(builder -> builder - .accept("name", MinecraftHasher.STRING, ScoreComponent::name) - .accept("objective", MinecraftHasher.STRING, ScoreComponent::objective)); + .accept("score", Function.identity(), childBuilder -> childBuilder + .accept("name", MinecraftHasher.STRING, ScoreComponent::name) + .accept("objective", MinecraftHasher.STRING, ScoreComponent::objective) + )); MinecraftHasher SELECTOR_COMPONENT = component(builder -> builder .accept("selector", MinecraftHasher.STRING, SelectorComponent::pattern) @@ -164,14 +172,11 @@ public MinecraftHasher get() { MinecraftHasher> NBT_COMPONENT = component(builder -> builder .accept("nbt", MinecraftHasher.STRING, NBTComponent::nbtPath) .optional("interpret", MinecraftHasher.BOOL, NBTComponent::interpret, false) - .optionalNullable("separator", COMPONENT, NBTComponent::separator)); // TODO source key, needs kyori update? - - MinecraftHasher OBJECT_CONTENTS_TYPE = MinecraftHasher.fromEnum(ObjectContentsType::getName); - - MapBuilder OBJECT_CONTENTS = MapBuilder.dispatch("object", OBJECT_CONTENTS_TYPE, ObjectContentsType::fromContents, ObjectContentsType::mapBuilder); + .optionalNullable("separator", COMPONENT, NBTComponent::separator) + .accept(NbtComponentType.NBT_COMPONENT_SOURCE_MAP_BUILDER, Function.identity())); MinecraftHasher OBJECT_COMPONENT = component(builder -> builder - .accept(OBJECT_CONTENTS, ObjectComponent::contents)); + .accept(ObjectContentsType.OBJECT_CONTENTS_MAP_BUILDER, ObjectComponent::contents)); MinecraftHasher ACTUAL_COMPONENT = (component, encoder) -> { if (component instanceof TextComponent text) { @@ -184,7 +189,7 @@ public MinecraftHasher get() { return SCORE_COMPONENT.hash(score, encoder); } else if (component instanceof SelectorComponent selector) { return SELECTOR_COMPONENT.hash(selector, encoder); - } else if (component instanceof NBTComponent nbt) { + } else if (component instanceof NBTComponent nbt) { return NBT_COMPONENT.hash(nbt, encoder); } else if (component instanceof ObjectComponent object) { return OBJECT_COMPONENT.hash(object, encoder); diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java b/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java index 9d7fa3dc07d..92522981914 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java @@ -26,37 +26,16 @@ package org.geysermc.geyser.item.hashing; import com.google.common.hash.HashCode; -import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.TextColor; -import net.kyori.adventure.text.format.TextDecoration; -import org.cloudburstmc.math.vector.Vector3i; -import org.cloudburstmc.nbt.NbtList; -import org.cloudburstmc.nbt.NbtMap; -import org.cloudburstmc.nbt.NbtType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.inventory.item.Potion; -import org.geysermc.geyser.item.Items; -import org.geysermc.geyser.item.components.Rarity; -import org.geysermc.geyser.level.block.Blocks; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.cache.registry.JavaRegistries; -import org.geysermc.geyser.util.MinecraftKey; +import org.geysermc.geyser.session.cache.registry.JavaRegistryProvider; import org.geysermc.mcprotocollib.protocol.data.game.Holder; -import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect; -import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; -import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType; -import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.ModifierOperation; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.GlobalPos; -import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; import org.geysermc.mcprotocollib.protocol.data.game.item.HashedStack; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.component.AttackRange; import org.geysermc.mcprotocollib.protocol.data.game.item.component.BlockStateProperties; import org.geysermc.mcprotocollib.protocol.data.game.item.component.BlocksAttacks; import org.geysermc.mcprotocollib.protocol.data.game.item.component.Consumable; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.ConsumeEffect; import org.geysermc.mcprotocollib.protocol.data.game.item.component.CustomModelData; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; @@ -65,14 +44,10 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.Equippable; import org.geysermc.mcprotocollib.protocol.data.game.item.component.Fireworks; import org.geysermc.mcprotocollib.protocol.data.game.item.component.FoodProperties; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet; import org.geysermc.mcprotocollib.protocol.data.game.item.component.IntComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemAttributeModifiers; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments; import org.geysermc.mcprotocollib.protocol.data.game.item.component.KineticWeapon; import org.geysermc.mcprotocollib.protocol.data.game.item.component.LodestoneTracker; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.MobEffectDetails; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.MobEffectInstance; import org.geysermc.mcprotocollib.protocol.data.game.item.component.PiercingWeapon; import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents; import org.geysermc.mcprotocollib.protocol.data.game.item.component.SwingAnimation; @@ -86,7 +61,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.WritableBookContent; import org.geysermc.mcprotocollib.protocol.data.game.item.component.WrittenBookContent; import org.geysermc.mcprotocollib.protocol.data.game.level.sound.BuiltinSound; -import org.geysermc.mcprotocollib.protocol.data.game.level.sound.CustomSound; +import org.jetbrains.annotations.VisibleForTesting; import java.util.HashMap; import java.util.HashSet; @@ -97,7 +72,8 @@ @SuppressWarnings("UnstableApiUsage") public class DataComponentHashers { - private static final Set> NOT_HASHED = ReferenceOpenHashSet.of(DataComponentTypes.CREATIVE_SLOT_LOCK, DataComponentTypes.MAP_POST_PROCESSING); + @VisibleForTesting + public static final Set> NOT_HASHED = Set.of(DataComponentTypes.CREATIVE_SLOT_LOCK, DataComponentTypes.MAP_POST_PROCESSING, DataComponentTypes.ADDITIONAL_TRADE_COST); private static final Map, MinecraftHasher> hashers = new HashMap<>(); static { @@ -109,11 +85,11 @@ public class DataComponentHashers { registerMap(DataComponentTypes.USE_EFFECTS, builder -> builder .optional("can_sprint", MinecraftHasher.BOOL, UseEffects::canSprint, false) .optional("interact_vibrations", MinecraftHasher.BOOL, UseEffects::interactVibrations, true) - .optional("speed_multiplier", MinecraftHasher.FLOAT, UseEffects::speedMultiplier, 0.2F)); // TODO test 1.21.11 + .optional("speed_multiplier", MinecraftHasher.FLOAT, UseEffects::speedMultiplier, 0.2F)); register(DataComponentTypes.CUSTOM_NAME, ComponentHasher.COMPONENT); register(DataComponentTypes.MINIMUM_ATTACK_CHARGE, MinecraftHasher.FLOAT); - register(DataComponentTypes.DAMAGE_TYPE, RegistryHasher.eitherHolderHasher(JavaRegistries.DAMAGE_TYPE)); + register(DataComponentTypes.DAMAGE_TYPE, RegistryHasher.DAMAGE_TYPE); register(DataComponentTypes.ITEM_NAME, ComponentHasher.COMPONENT); register(DataComponentTypes.ITEM_MODEL, MinecraftHasher.KEY); register(DataComponentTypes.LORE, ComponentHasher.COMPONENT.list()); @@ -121,8 +97,8 @@ public class DataComponentHashers { register(DataComponentTypes.ENCHANTMENTS, RegistryHasher.ITEM_ENCHANTMENTS); register(DataComponentTypes.CAN_PLACE_ON, RegistryHasher.ADVENTURE_MODE_PREDICATE); - register(DataComponentTypes.CAN_BREAK, RegistryHasher.ADVENTURE_MODE_PREDICATE); // TODO needs tests - register(DataComponentTypes.ATTRIBUTE_MODIFIERS, RegistryHasher.ATTRIBUTE_MODIFIER_ENTRY.list().cast(ItemAttributeModifiers::getModifiers)); // TODO needs tests + register(DataComponentTypes.CAN_BREAK, RegistryHasher.ADVENTURE_MODE_PREDICATE); + register(DataComponentTypes.ATTRIBUTE_MODIFIERS, RegistryHasher.ATTRIBUTE_MODIFIER_ENTRY.list().cast(ItemAttributeModifiers::getModifiers)); registerMap(DataComponentTypes.CUSTOM_MODEL_DATA, builder -> builder .optionalList("floats", MinecraftHasher.FLOAT, CustomModelData::floats) @@ -156,7 +132,7 @@ public class DataComponentHashers { .accept("seconds", MinecraftHasher.FLOAT, UseCooldown::seconds) .optionalNullable("cooldown_group", MinecraftHasher.KEY, UseCooldown::cooldownGroup)); registerMap(DataComponentTypes.DAMAGE_RESISTANT, builder -> builder - .accept("types", MinecraftHasher.TAG, Function.identity())); + .accept("types", RegistryHasher.DAMAGE_TYPE.holderSet(), Function.identity())); registerMap(DataComponentTypes.TOOL, builder -> builder .acceptList("rules", RegistryHasher.TOOL_RULE, ToolData::getRules) .optional("default_mining_speed", MinecraftHasher.FLOAT, ToolData::getDefaultMiningSpeed, 1.0F) @@ -167,19 +143,19 @@ public class DataComponentHashers { .optional("disable_blocking_for_seconds", MinecraftHasher.FLOAT, Weapon::disableBlockingForSeconds, 0.0F)); registerMap(DataComponentTypes.PIERCING_WEAPON, builder -> builder .optional("deals_knockback", MinecraftHasher.BOOL, PiercingWeapon::dealsKnockback, true) - .optional("dismounts", MinecraftHasher.BOOL, PiercingWeapon::dealsKnockback, false) + .optional("dismounts", MinecraftHasher.BOOL, PiercingWeapon::dismounts, false) .optionalNullable("sound", RegistryHasher.SOUND_EVENT, PiercingWeapon::sound) - .optionalNullable("hit_sound", RegistryHasher.SOUND_EVENT, PiercingWeapon::hitSound)); // TODO test 1.21.11 + .optionalNullable("hit_sound", RegistryHasher.SOUND_EVENT, PiercingWeapon::hitSound)); registerMap(DataComponentTypes.ATTACK_RANGE, builder -> builder - .optional("min_reach", MinecraftHasher.FLOAT, AttackRange::minRange, 0.0F) - .optional("max_reach", MinecraftHasher.FLOAT, AttackRange::maxRange, 3.0F) - .optional("min_creative_reach", MinecraftHasher.FLOAT, AttackRange::minCreativeRange, 0.0F) - .optional("max_creative_reach", MinecraftHasher.FLOAT, AttackRange::maxCreativeRange, 5.0F) + .optional("min_reach", MinecraftHasher.FLOAT, AttackRange::minReach, 0.0F) + .optional("max_reach", MinecraftHasher.FLOAT, AttackRange::maxReach, 3.0F) + .optional("min_creative_reach", MinecraftHasher.FLOAT, AttackRange::minCreativeReach, 0.0F) + .optional("max_creative_reach", MinecraftHasher.FLOAT, AttackRange::maxCreativeReach, 5.0F) .optional("hitbox_margin", MinecraftHasher.FLOAT, AttackRange::hitboxMargin, 0.3F) - .optional("mob_factor", MinecraftHasher.FLOAT, AttackRange::mobFactor, 1.0F)); // TODO test 1.21.11 + .optional("mob_factor", MinecraftHasher.FLOAT, AttackRange::mobFactor, 1.0F)); registerMap(DataComponentTypes.SWING_ANIMATION, builder -> builder .optional("type", RegistryHasher.SWING_ANIMATION_TYPE, SwingAnimation::type, SwingAnimation.Type.WHACK) - .optional("duration", MinecraftHasher.INT, SwingAnimation::duration, 6)); // TODO test 1.21.11 + .optional("duration", MinecraftHasher.INT, SwingAnimation::duration, 6)); registerMap(DataComponentTypes.ENCHANTABLE, builder -> builder .accept("value", MinecraftHasher.INT, Function.identity())); registerMap(DataComponentTypes.EQUIPPABLE, builder -> builder @@ -207,28 +183,30 @@ public class DataComponentHashers { .optional("disable_cooldown_scale", MinecraftHasher.FLOAT, BlocksAttacks::disableCooldownScale, 1.0F) .optional("damage_reductions", RegistryHasher.BLOCKS_ATTACKS_DAMAGE_REDUCTION.list(), BlocksAttacks::damageReductions, List.of(new BlocksAttacks.DamageReduction(90.0F, null, 0.0F, 1.0F))) .optional("item_damage", RegistryHasher.BLOCKS_ATTACKS_ITEM_DAMAGE_FUNCTION, BlocksAttacks::itemDamage, new BlocksAttacks.ItemDamageFunction(1.0F, 0.0F, 1.0F)) - .optionalNullable("bypassed_by", MinecraftHasher.TAG, BlocksAttacks::bypassedBy) + .optionalNullable("bypassed_by", RegistryHasher.DAMAGE_TYPE.holderSet(), BlocksAttacks::bypassedBy) .optionalNullable("block_sound", RegistryHasher.SOUND_EVENT, BlocksAttacks::blockSound) - .optionalNullable("disabled_sound", RegistryHasher.SOUND_EVENT, BlocksAttacks::disableSound)); // TODO needs tests + .optionalNullable("disabled_sound", RegistryHasher.SOUND_EVENT, BlocksAttacks::disableSound)); registerMap(DataComponentTypes.KINETIC_WEAPON, builder -> builder .optional("contact_cooldown_ticks", MinecraftHasher.INT, KineticWeapon::contactCooldownTicks, 10) - .optional("delay_ticks", MinecraftHasher.INT, KineticWeapon::contactCooldownTicks, 0) + .optional("delay_ticks", MinecraftHasher.INT, KineticWeapon::delayTicks, 0) .optionalNullable("dismount_conditions", RegistryHasher.KINETIC_WEAPON_CONDITION, KineticWeapon::dismountConditions) .optionalNullable("knockback_conditions", RegistryHasher.KINETIC_WEAPON_CONDITION, KineticWeapon::knockbackConditions) .optionalNullable("damage_conditions", RegistryHasher.KINETIC_WEAPON_CONDITION, KineticWeapon::damageConditions) .optional("forward_movement", MinecraftHasher.FLOAT, KineticWeapon::forwardMovement, 0.0F) .optional("damage_multiplier", MinecraftHasher.FLOAT, KineticWeapon::damageMultiplier, 1.0F) .optionalNullable("sound", RegistryHasher.SOUND_EVENT, KineticWeapon::sound) - .optionalNullable("hit_sound", RegistryHasher.SOUND_EVENT, KineticWeapon::hitSound)); // TODO test 1.21.11 + .optionalNullable("hit_sound", RegistryHasher.SOUND_EVENT, KineticWeapon::hitSound)); register(DataComponentTypes.STORED_ENCHANTMENTS, RegistryHasher.ITEM_ENCHANTMENTS); + register(DataComponentTypes.DYE, MinecraftHasher.DYE_COLOR); + registerInt(DataComponentTypes.DYED_COLOR); registerInt(DataComponentTypes.MAP_COLOR); registerInt(DataComponentTypes.MAP_ID); register(DataComponentTypes.MAP_DECORATIONS, MinecraftHasher.NBT_MAP); register(DataComponentTypes.CHARGED_PROJECTILES, RegistryHasher.ITEM_STACK.list()); - register(DataComponentTypes.BUNDLE_CONTENTS, RegistryHasher.ITEM_STACK.list()); // TODO test data component removal + register(DataComponentTypes.BUNDLE_CONTENTS, RegistryHasher.ITEM_STACK.list()); registerMap(DataComponentTypes.POTION_CONTENTS, builder -> builder .optional("potion", RegistryHasher.POTION, PotionContents::getPotionId, -1) @@ -244,7 +222,7 @@ public class DataComponentHashers { registerMap(DataComponentTypes.WRITTEN_BOOK_CONTENT, builder -> builder .accept("title", MinecraftHasher.STRING.filterable(), WrittenBookContent::getTitle) .accept("author", MinecraftHasher.STRING, WrittenBookContent::getAuthor) - .accept("generation", MinecraftHasher.INT, WrittenBookContent::getGeneration) + .optional("generation", MinecraftHasher.INT, WrittenBookContent::getGeneration, 0) .optionalList("pages", ComponentHasher.COMPONENT.filterable(), WrittenBookContent::getPages) .optional("resolved", MinecraftHasher.BOOL, WrittenBookContent::isResolved, false)); @@ -258,13 +236,13 @@ public class DataComponentHashers { .accept("id", RegistryHasher.BLOCK_ENTITY_TYPE_KEY, TypedEntityData::type) .accept(TypedEntityData::tag, MapBuilder.inlineNbtMap())); - register(DataComponentTypes.INSTRUMENT, RegistryHasher.INSTRUMENT_COMPONENT); - register(DataComponentTypes.PROVIDES_TRIM_MATERIAL, RegistryHasher.PROVIDES_TRIM_MATERIAL); + register(DataComponentTypes.INSTRUMENT, RegistryHasher.INSTRUMENT.holder()); + register(DataComponentTypes.PROVIDES_TRIM_MATERIAL, RegistryHasher.TRIM_MATERIAL.holder()); registerInt(DataComponentTypes.OMINOUS_BOTTLE_AMPLIFIER); - register(DataComponentTypes.JUKEBOX_PLAYABLE, RegistryHasher.JUKEBOX_PLAYABLE); - register(DataComponentTypes.PROVIDES_BANNER_PATTERNS, MinecraftHasher.TAG); + register(DataComponentTypes.JUKEBOX_PLAYABLE, RegistryHasher.JUKEBOX_SONG.holder()); + register(DataComponentTypes.PROVIDES_BANNER_PATTERNS, RegistryHasher.BANNER_PATTERN.holderSet()); register(DataComponentTypes.RECIPES, MinecraftHasher.NBT_LIST); registerMap(DataComponentTypes.LODESTONE_TRACKER, builder -> builder @@ -302,15 +280,19 @@ public class DataComponentHashers { register(DataComponentTypes.MOOSHROOM_VARIANT, RegistryHasher.MOOSHROOM_VARIANT); register(DataComponentTypes.RABBIT_VARIANT, RegistryHasher.RABBIT_VARIANT); register(DataComponentTypes.PIG_VARIANT, RegistryHasher.PIG_VARIANT); + register(DataComponentTypes.PIG_SOUND_VARIANT, RegistryHasher.PIG_SOUND_VARIANT); register(DataComponentTypes.COW_VARIANT, RegistryHasher.COW_VARIANT); - register(DataComponentTypes.CHICKEN_VARIANT, RegistryHasher.eitherHolderHasher(JavaRegistries.CHICKEN_VARIANT)); - register(DataComponentTypes.ZOMBIE_NAUTILUS_VARIANT, RegistryHasher.eitherHolderHasher(JavaRegistries.ZOMBIE_NAUTILUS_VARIANT)); // TODO test 1.21.11 + register(DataComponentTypes.COW_SOUND_VARIANT, RegistryHasher.COW_SOUND_VARIANT); + register(DataComponentTypes.CHICKEN_VARIANT, RegistryHasher.CHICKEN_VARIANT); + register(DataComponentTypes.CHICKEN_SOUND_VARIANT, RegistryHasher.CHICKEN_SOUND_VARIANT); + register(DataComponentTypes.ZOMBIE_NAUTILUS_VARIANT, RegistryHasher.ZOMBIE_NAUTILUS_VARIANT); register(DataComponentTypes.FROG_VARIANT, RegistryHasher.FROG_VARIANT); register(DataComponentTypes.HORSE_VARIANT, RegistryHasher.HORSE_VARIANT); register(DataComponentTypes.PAINTING_VARIANT, RegistryHasher.PAINTING_VARIANT.cast(Holder::id)); // This can and will throw when a direct holder was received, which is still possible due to a bug in 1.21.6. register(DataComponentTypes.LLAMA_VARIANT, RegistryHasher.LLAMA_VARIANT); register(DataComponentTypes.AXOLOTL_VARIANT, RegistryHasher.AXOLOTL_VARIANT); register(DataComponentTypes.CAT_VARIANT, RegistryHasher.CAT_VARIANT); + register(DataComponentTypes.CAT_SOUND_VARIANT, RegistryHasher.CAT_SOUND_VARIANT); register(DataComponentTypes.CAT_COLLAR, MinecraftHasher.DYE_COLOR); register(DataComponentTypes.SHEEP_COLOR, MinecraftHasher.DYE_COLOR); register(DataComponentTypes.SHULKER_COLOR, MinecraftHasher.DYE_COLOR); @@ -343,9 +325,9 @@ public static MinecraftHasher hasher(DataComponentType component) { return hasher; } - public static HashCode hash(GeyserSession session, DataComponentType component, T value) { + public static HashCode hash(JavaRegistryProvider registries, DataComponentType component, T value) { try { - return hasher(component).hash(value, new MinecraftHashEncoder(session.getRegistryCache())); + return hasher(component).hash(value, new MinecraftHashEncoder(registries)); } catch (Exception exception) { GeyserImpl.getInstance().getLogger().error("Failed to hash item data component " + component.getKey() + " with value " + value + "!"); GeyserImpl.getInstance().getLogger().error("This is a Geyser bug, please report this!"); @@ -353,6 +335,10 @@ public static HashCode hash(GeyserSession session, DataComponentType comp } } + public static > HashCode hash(JavaRegistryProvider registries, DataComponent component) { + return hash(registries, component.getType(), component.getValue()); + } + public static HashedStack hashStack(GeyserSession session, ItemStack stack) { if (stack == null) { return null; @@ -371,208 +357,9 @@ public static HashedStack hashStack(GeyserSession session, ItemStack stack) { } else if (component.getValue().getValue() == null) { removals.add(component.getKey()); } else { - hashedAdditions.put(component.getKey(), hash(session, (DataComponentType) component.getKey(), component.getValue().getValue()).asInt()); + hashedAdditions.put(component.getKey(), hash(session.getRegistryCache(), component.getValue()).asInt()); } } return new HashedStack(stack.getId(), stack.getAmount(), hashedAdditions, removals); } - - // TODO better testing - public static void testHashing(GeyserSession session) { - // Hashed values generated by vanilla Java - - NbtMap customData = NbtMap.builder() - .putString("hello", "g'day") - .putBoolean("nice?", false) - .putByte("coolness", (byte) 100) - .putCompound("geyser", NbtMap.builder() - .putString("is", "very cool") - .build()) - .putList("a list", NbtType.LIST, List.of(new NbtList<>(NbtType.STRING, "in a list"))) - .build(); - - testHash(session, DataComponentTypes.CUSTOM_DATA, customData, -385053299); - - testHash(session, DataComponentTypes.MAX_STACK_SIZE, 64, 733160003); - testHash(session, DataComponentTypes.MAX_DAMAGE, 13, -801733367); - testHash(session, DataComponentTypes.DAMAGE, 459, 1211405277); - testHash(session, DataComponentTypes.UNBREAKABLE, Unit.INSTANCE, -982207288); - - testHash(session, DataComponentTypes.CUSTOM_NAME, Component.text("simple component test!"), 950545066); - testHash(session, DataComponentTypes.CUSTOM_NAME, Component.translatable("a.translatable"), 1983484873); - testHash(session, DataComponentTypes.CUSTOM_NAME, Component.text("component with *style*") - .style(style -> style.color(NamedTextColor.RED).decorate(TextDecoration.ITALIC)), -886479206); - testHash(session, DataComponentTypes.CUSTOM_NAME, Component.text("component with more stuff") - .children(List.of(Component.translatable("a.translate.string", "fallback!") - .style(style -> style.color(TextColor.color(0x446688)).decorate(TextDecoration.BOLD)))), -1591253390); - - testHash(session, DataComponentTypes.ITEM_MODEL, MinecraftKey.key("testing"), -689946239); - - testHash(session, DataComponentTypes.RARITY, Rarity.COMMON.ordinal(), 75150990); - testHash(session, DataComponentTypes.RARITY, Rarity.RARE.ordinal(), -1420566726); - testHash(session, DataComponentTypes.RARITY, Rarity.EPIC.ordinal(), -292715907); - - testHash(session, DataComponentTypes.ENCHANTMENTS, new ItemEnchantments(Map.of( - 0, 1 - )), 0); // TODO identifier lookup - - testHash(session, DataComponentTypes.ATTRIBUTE_MODIFIERS, new ItemAttributeModifiers( - List.of( - ItemAttributeModifiers.Entry.builder() - .attribute(AttributeType.Builtin.ATTACK_DAMAGE.getId()) - .modifier(ItemAttributeModifiers.AttributeModifier.builder() - .id(MinecraftKey.key("test_modifier_1")) - .amount(2.0) - .operation(ModifierOperation.ADD) - .build()) - .slot(ItemAttributeModifiers.EquipmentSlotGroup.ANY) - .display(new ItemAttributeModifiers.Display(ItemAttributeModifiers.DisplayType.DEFAULT, null)) - .build(), - ItemAttributeModifiers.Entry.builder() - .attribute(AttributeType.Builtin.JUMP_STRENGTH.getId()) - .modifier(ItemAttributeModifiers.AttributeModifier.builder() - .id(MinecraftKey.key("test_modifier_2")) - .amount(4.2) - .operation(ModifierOperation.ADD_MULTIPLIED_TOTAL) - .build()) - .slot(ItemAttributeModifiers.EquipmentSlotGroup.HEAD) - .display(new ItemAttributeModifiers.Display(ItemAttributeModifiers.DisplayType.HIDDEN, null)) - .build(), - ItemAttributeModifiers.Entry.builder() - .attribute(AttributeType.Builtin.WAYPOINT_RECEIVE_RANGE.getId()) - .modifier(ItemAttributeModifiers.AttributeModifier.builder() - .id(MinecraftKey.key("geyser_mc:test_modifier_3")) - .amount(5.4) - .operation(ModifierOperation.ADD_MULTIPLIED_BASE) - .build()) - .slot(ItemAttributeModifiers.EquipmentSlotGroup.FEET) - .display(new ItemAttributeModifiers.Display(ItemAttributeModifiers.DisplayType.DEFAULT, null)) - .build() - ) - ), 1889444548); - - testHash(session, DataComponentTypes.ATTRIBUTE_MODIFIERS, new ItemAttributeModifiers( - List.of( - ItemAttributeModifiers.Entry.builder() - .attribute(AttributeType.Builtin.WAYPOINT_TRANSMIT_RANGE.getId()) - .modifier(ItemAttributeModifiers.AttributeModifier.builder() - .id(MinecraftKey.key("geyser_mc:test_modifier_4")) - .amount(2.0) - .operation(ModifierOperation.ADD) - .build()) - .slot(ItemAttributeModifiers.EquipmentSlotGroup.ANY) - .display(new ItemAttributeModifiers.Display(ItemAttributeModifiers.DisplayType.OVERRIDE, Component.text("give me a test"))) - .build() - ) - ), 1375953017); - - testHash(session, DataComponentTypes.CUSTOM_MODEL_DATA, - new CustomModelData(List.of(5.0F, 3.0F, -1.0F), List.of(false, true, false), List.of("1", "3", "2"), List.of(3424, -123, 345)), 1947635619); - - testHash(session, DataComponentTypes.CUSTOM_MODEL_DATA, - new CustomModelData(List.of(5.03F, 3.0F, -1.11F), List.of(true, true, false), List.of("2", "5", "7"), List.of()), -512419908); - - testHash(session, DataComponentTypes.TOOLTIP_DISPLAY, new TooltipDisplay(false, List.of(DataComponentTypes.CONSUMABLE, DataComponentTypes.DAMAGE)), -816418453); - testHash(session, DataComponentTypes.TOOLTIP_DISPLAY, new TooltipDisplay(true, List.of()), 14016722); - testHash(session, DataComponentTypes.TOOLTIP_DISPLAY, new TooltipDisplay(false, List.of()), -982207288); - - testHash(session, DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true, -1019818302); - testHash(session, DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, false, 828198337); - - testHash(session, DataComponentTypes.FOOD, new FoodProperties(5, 1.4F, false), 445786378); - testHash(session, DataComponentTypes.FOOD, new FoodProperties(3, 5.7F, true), 1917653498); - testHash(session, DataComponentTypes.FOOD, new FoodProperties(7, 0.15F, false), -184166204); - - testHash(session, DataComponentTypes.CONSUMABLE, new Consumable(2.0F, Consumable.ItemUseAnimation.EAT, - BuiltinSound.ITEM_OMINOUS_BOTTLE_DISPOSE, true, - List.of(new ConsumeEffect.RemoveEffects(new HolderSet(new int[]{Effect.BAD_OMEN.ordinal(), Effect.REGENERATION.ordinal()})), - new ConsumeEffect.TeleportRandomly(3.0F))), 1742669333); - - testHash(session, DataComponentTypes.USE_REMAINDER, new ItemStack(Items.MELON.javaId(), 52), -1279684916); - - DataComponents specialComponents = new DataComponents(new HashMap<>()); - specialComponents.put(DataComponentTypes.ITEM_MODEL, MinecraftKey.key("testing")); - specialComponents.put(DataComponentTypes.MAX_STACK_SIZE, 44); - testHash(session, DataComponentTypes.USE_REMAINDER, new ItemStack(Items.PUMPKIN.javaId(), 32, specialComponents), 1991032843); - - testHash(session, DataComponentTypes.DAMAGE_RESISTANT, MinecraftKey.key("testing"), -1230493835); - - testHash(session, DataComponentTypes.TOOL, new ToolData(List.of(), 5.0F, 3, false), -1789071928); - testHash(session, DataComponentTypes.TOOL, new ToolData(List.of(), 3.0F, 1, true), -7422944); - testHash(session, DataComponentTypes.TOOL, new ToolData(List.of( - new ToolData.Rule(new HolderSet(MinecraftKey.key("acacia_logs")), null, null), - new ToolData.Rule(new HolderSet(new int[]{Blocks.JACK_O_LANTERN.javaId(), Blocks.WALL_TORCH.javaId()}), 4.2F, true), - new ToolData.Rule(new HolderSet(new int[]{Blocks.PUMPKIN.javaId()}), 7.0F, false)), - 1.0F, 1, true), 2103678261); - - testHash(session, DataComponentTypes.WEAPON, new Weapon(5, 2.0F), -154556976); - testHash(session, DataComponentTypes.WEAPON, new Weapon(1, 7.3F), 885347995); - - testHash(session, DataComponentTypes.ENCHANTABLE, 3, -1834983819); - - testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.BODY, BuiltinSound.ITEM_ARMOR_EQUIP_GENERIC, null, null, null, - true, true, true, false, - false, BuiltinSound.ITEM_SHEARS_SNIP), 1294431019); - testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.BODY, BuiltinSound.ITEM_ARMOR_EQUIP_CHAIN, MinecraftKey.key("testing"), null, null, - true, true, true, false, - true, BuiltinSound.ITEM_BONE_MEAL_USE), -801616214); - testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.BODY, BuiltinSound.AMBIENT_CAVE, null, null, null, - false, true, false, false, - false, new CustomSound("testing_equippable", false, 10.0F)), -1145684769); - testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.BODY, BuiltinSound.ENTITY_BREEZE_WIND_BURST, null, MinecraftKey.key("testing"), - new HolderSet(new int[]{EntityType.ACACIA_BOAT.ordinal()}), false, true, false, false, - true, BuiltinSound.BLOCK_NETHERITE_BLOCK_PLACE), -115079770); - testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.HELMET, BuiltinSound.ITEM_ARMOR_EQUIP_GENERIC, null, null, null, - true, true, true, false, - false, BuiltinSound.ITEM_SHEARS_SNIP), 497790992); - testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.HELMET, BuiltinSound.ITEM_ARMOR_EQUIP_GENERIC, null, null, - new HolderSet(MinecraftKey.key("aquatic")), - true, true, true, false, - false, BuiltinSound.ITEM_SHEARS_SNIP), 264760955); - - testHash(session, DataComponentTypes.REPAIRABLE, new HolderSet(new int[]{Items.AMETHYST_BLOCK.javaId(), Items.PUMPKIN.javaId()}), -36715567); - - NbtMap mapDecorations = NbtMap.builder() - .putCompound("test_decoration", NbtMap.builder() - .putString("type", "minecraft:player") - .putDouble("x", 45.0) - .putDouble("z", 67.4) - .putFloat("rotation", 39.5F) - .build()) - .build(); - - testHash(session, DataComponentTypes.MAP_DECORATIONS, mapDecorations, -625782954); - - ItemStack bundleStack1 = new ItemStack(Items.PUMPKIN.javaId()); - ItemStack bundleStack2 = new ItemStack(Items.MELON.javaId(), 24); - - DataComponents bundleStackComponents = new DataComponents(new HashMap<>()); - bundleStackComponents.put(DataComponentTypes.CUSTOM_NAME, Component.text("magic potato!")); - - ItemStack bundleStack3 = new ItemStack(Items.POTATO.javaId(), 30, bundleStackComponents); - testHash(session, DataComponentTypes.BUNDLE_CONTENTS, List.of(bundleStack1, bundleStack2, bundleStack3), 1817891504); - - testHash(session, DataComponentTypes.POTION_CONTENTS, new PotionContents(Potion.FIRE_RESISTANCE.ordinal(), -1, List.of(), null), -772576502); - testHash(session, DataComponentTypes.POTION_CONTENTS, new PotionContents(-1, 20, - List.of(new MobEffectInstance(Effect.CONDUIT_POWER, new MobEffectDetails(0, 0, false, true, true, null))), - null), -902075187); - testHash(session, DataComponentTypes.POTION_CONTENTS, new PotionContents(-1, 96, - List.of(new MobEffectInstance(Effect.JUMP_BOOST, new MobEffectDetails(57, 17, true, false, false, null))), - null), -17231244); - testHash(session, DataComponentTypes.POTION_CONTENTS, new PotionContents(-1, 87, - List.of(new MobEffectInstance(Effect.SPEED, new MobEffectDetails(29, 1004, false, true, true, null))), - "testing"), 2007296036); - - // TODO testing trim, instrument, trim material, jukebox playable requires registries - - testHash(session, DataComponentTypes.LODESTONE_TRACKER, - new LodestoneTracker(new GlobalPos(MinecraftKey.key("overworld"), Vector3i.from(5, 6, 7)), true), 63561894); - testHash(session, DataComponentTypes.LODESTONE_TRACKER, - new LodestoneTracker(null, false), 1595667667); - } - - private static void testHash(GeyserSession session, DataComponentType component, T value, int expected) { - int got = hash(session, component, value).asInt(); - System.out.println("Testing hashing component " + component.getKey() + ", expected " + expected + ", got " + got + " " + (got == expected ? "PASS" : "ERROR")); - } } diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/EnumMapDispatchHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/EnumMapDispatchHasher.java new file mode 100644 index 00000000000..8bc2f213372 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/EnumMapDispatchHasher.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2026 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.item.hashing; + +import com.google.common.base.Suppliers; + +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Utility interface with utility methods for creating {@link MapBuilder}s for a scenario in which there is an abstract type {@code A} ({@link ValueType}), indicated by a {@code typeKey} of {@link DistinctType}, + * and for each implementation of {@code A}, a {@link MapBuilder} exists, hashing an instance of the implementation. + * This interface builds on the {@link MapBuilder#dispatch(String, MinecraftHasher, Function, Function)} method to achieve this. + * + *

Essentially, implementations of this interface, when grouped together in e.g. an enum, function as a fancy named map from instances of {@code A} to their respective hashers.

+ * + *

A common way of implementing this is as follows:

+ * + *
    + *
  1. Create an enum, implementing {@link EnumMapDispatchHasher}, with the enum as {@link DistinctType} and the base type {@code A} as {@link ValueType}.
  2. + *
  3. Add a {@code clazz} field to the enum, of type {@code Class}, and a {@code mapBuilder} field, of type {@code MapBuilder}. + *
      + *
    • The {@code clazz} field is used to recognise an implementation of {@code A} and find an instance of {@link EnumMapDispatchHasher} for a certain instance of an implementation of {@code A}. + * + *

      It is therefore important that the {@code clazz} field is unique for all constants in the enum.

      + *
    • + *
    • The {@code mapBuilder} field is then used to hash that implementation of {@code A}.
    • + *
    • You can use a typed constructor like this to ensure that the type of {@code clazz} is the same as {@code mapBuilder}, and to provide proper typing information for the {@code mapBuilder}: + * + *

      {@code MyExampleEnum(Class clazz, MapBuilder builder)}

      + * + *

      In which {@code T} is an implementation of {@code A}, and {@code A} is the base type to hash.

      + *
    • + *
    + *
  4. + *
  5. For each implementation of {@code A}, add a constant to that enum, specifying the {@code clazz} of that implementation, and a {@code mapBuilder} for it. + * + *

    The name of the constant will be used to encode the value of the {@code typeKey}, so be sure it is right and matches Java edition!

    + *
  6. + *
  7. The methods of {@link EnumMapDispatchHasher} are to be implemented as follows: + *
      + *
    • {@link EnumMapDispatchHasher#distinction()} returns {@code this}.
    • + *
    • {@link EnumMapDispatchHasher#valueTypeClass()} returns the {@code clazz} field.
    • + *
    • {@link EnumMapDispatchHasher#mapBuilder()} returns the {@code mapBuilder} field.
    • + *
    + *
  8. + *
  9. Finally, create the {@link MapBuilder} with the {@link EnumMapDispatchHasher#dispatch(Supplier)} or the {@link EnumMapDispatchHasher#dispatch(String, Supplier)} method, + * using {@code MyExampleEnum::values} as {@code hashersSupplier}.
  10. + *
+ * + *

Example implementations can be seen in {@link org.geysermc.geyser.item.hashing.data.ConsumeEffectType}, {@link org.geysermc.geyser.item.hashing.data.NbtComponentType}, and {@link org.geysermc.geyser.item.hashing.data.ObjectContentsType}. + * If a {@link MinecraftHasher} is required instead of a {@link MapBuilder}, use {@link MinecraftHasher#mapBuilder(MapBuilder)} to wrap the {@link MapBuilder} into a hasher.

+ * + *

The implementation described above uses the enum itself as {@link DistinctType} for {@code A}, and then uses {@link EnumMapDispatchHasher#dispatch(String, Supplier)}, which uses {@link MinecraftHasher#fromEnum()} to + * encode the enum constant for {@code typeKey}. This may not always be wanted behaviour, and this interface does allow using a different {@link DistinctType}. In that case, the hasher for the {@link DistinctType} has + * to be given, using the {@link EnumMapDispatchHasher#dispatch(MinecraftHasher, Supplier)} or the {@link EnumMapDispatchHasher#dispatch(String, MinecraftHasher, Supplier)} method.

+ * + * @param the distinct type used to indicate the implementation of a {@link ValueType}. + * @param the abstract, base type to hash. + * @see EnumMapDispatchHasher#dispatch(Supplier) + * @see EnumMapDispatchHasher#dispatch(String, Supplier) + * @see EnumMapDispatchHasher#dispatch(MinecraftHasher, Supplier) + * @see EnumMapDispatchHasher#dispatch(String, MinecraftHasher, Supplier) + */ +public interface EnumMapDispatchHasher { + + /** + * Returns the {@link DistinctType} of this implementation of {@link ValueType}. + * + *

Implementations must ensure that each {@link DistinctType} is unique and only for one implementation of {@link ValueType}.

+ * + * @return the {@link DistinctType} of this implementation of {@link ValueType}. + */ + DistinctType distinction(); + + /** + * @return the {@link Class} of this implementation of {@link ValueType}. + */ + Class valueTypeClass(); + + /** + * @return the {@link MapBuilder} used to encode this implementation of {@link ValueType}. + */ + MapBuilder mapBuilder(); + + /** + * Creates a {@link MapBuilder} for an abstract type {@link Value}, using an array of implementations of {@link EnumMapDispatchHasher}. This {@link MapBuilder} encodes the + * {@link Value} fuzzily, as in, there is no {@code type} key indicating the {@code DistinctType}. As such, implementing a {@code DistinctType} to indicate the type is not strictly necessary. + * + * @param hashersSupplier a supplier returning the array of {@link EnumMapDispatchHasher} implementations, one for each implementation of {@link Value}. + * @param the abstract, base type to create a {@link MapBuilder} for. + * @return the created {@link MapBuilder}. + */ + static > MapBuilder dispatchFuzzy(Supplier hashersSupplier) { + Supplier memoizedHashers = Suppliers.memoize(hashersSupplier::get); + return builder -> builder.accept(Function.identity(), value -> { + HasherEnum[] hashers = memoizedHashers.get(); + for (HasherEnum hasher : hashers) { + if (hasher.valueTypeClass().isInstance(value)) { + return (MapBuilder) hasher.mapBuilder(); + } + } + throw new IllegalArgumentException("No hasher for value " + value); + }); + } + + /** + * Delegates to {@link EnumMapDispatchHasher#dispatch(String, Supplier)}, uses {@code "type"} as {@code typeKey}. + * + * @see EnumMapDispatchHasher#dispatch(String, Supplier) + * @see EnumMapDispatchHasher#dispatch(String, MinecraftHasher, Supplier) + */ + static & EnumMapDispatchHasher> MapBuilder dispatch(Supplier hashersSupplier) { + return dispatch("type", hashersSupplier); + } + + /** + * Delegates to {@link EnumMapDispatchHasher#dispatch(String, MinecraftHasher, Supplier)}, using the {@link HasherEnum} as {@code Distinct} type. + * + *

Uses {@link MinecraftHasher#fromEnum()} as hasher for {@link HasherEnum}/the {@code Distinct} type, so be sure that the enum constants match what you want to appear in the map!

+ * + * @see EnumMapDispatchHasher#dispatch(String, MinecraftHasher, Supplier) + */ + static & EnumMapDispatchHasher> MapBuilder dispatch(String typeKey, Supplier hashersSupplier) { + return dispatch(typeKey, MinecraftHasher.fromEnum(), hashersSupplier); + } + + /** + * Delegates to {@link EnumMapDispatchHasher#dispatch(String, MinecraftHasher, Supplier)}, using {@code "type"} as {@code distinctTypeKey}. + * + * @see EnumMapDispatchHasher#dispatch(String, MinecraftHasher, Supplier) + */ + static > MapBuilder dispatch(MinecraftHasher distinctHasher, Supplier hashersSupplier) { + return dispatch("type", distinctHasher, hashersSupplier); + } + + /** + * Creates a {@link MapBuilder} for an abstract type {@link Value}, using an array of implementations of {@link EnumMapDispatchHasher}, and the {@code distinctTypeKey} to indicate the {@link Distinct} type of + * an implementation of {@link Value}. + * + * @param distinctTypeKey the key in the map used to indicate the {@link Distinct} type. + * @param distinctHasher the hasher used to hash the {@link Distinct} type. + * @param hashersSupplier a supplier returning the array of {@link EnumMapDispatchHasher} implementations, one for each implementation of {@link Value}. + * @param the distinct type used to indicate an implementation of {@link Value}. + * @param the abstract, base type to create a {@link MapBuilder} for. + * @return the created {@link MapBuilder}. + */ + static > MapBuilder dispatch(String distinctTypeKey, MinecraftHasher distinctHasher, + Supplier hashersSupplier) { + Supplier memoizedHashers = Suppliers.memoize(hashersSupplier::get); + MinecraftHasher enumDistinctHasher = distinctHasher.cast(EnumMapDispatchHasher::distinction); + return MapBuilder.dispatch(distinctTypeKey, enumDistinctHasher, value -> { + HasherEnum[] hashers = memoizedHashers.get(); + for (HasherEnum hasher : hashers) { + if (hasher.valueTypeClass().isInstance(value)) { + return hasher; + } + } + throw new IllegalArgumentException("No hasher for value " + value); + }, hasher -> hasher.mapBuilder().cast()); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/MapHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/MapHasher.java index 2072ace2910..99c1565258d 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/MapHasher.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/MapHasher.java @@ -26,6 +26,8 @@ package org.geysermc.geyser.item.hashing; import com.google.common.hash.HashCode; +import org.geysermc.geyser.GeyserImpl; +import org.jetbrains.annotations.VisibleForTesting; import java.util.HashMap; import java.util.List; @@ -44,7 +46,8 @@ */ @SuppressWarnings("UnstableApiUsage") public class MapHasher { - private static final boolean DEBUG = false; + @VisibleForTesting + public static boolean debug = false; private final MinecraftHashEncoder encoder; private final Type object; @@ -52,7 +55,7 @@ public class MapHasher { private final Map unhashed; MapHasher(Type object, MinecraftHashEncoder encoder) { - this(object, encoder, new HashMap<>(), DEBUG ? new HashMap<>() : null); + this(object, encoder, new HashMap<>(), debug ? new HashMap<>() : null); } private MapHasher(Type object, MinecraftHashEncoder encoder, Map map, Map unhashed) { @@ -94,6 +97,22 @@ public MapHasher accept(String key, MinecraftHasher hasher, return acceptConstant(key, hasher, extractor.apply(object)); } + /** + * Extracts a {@link Value} from a {@link Type} using the {@code extractor}, then applies the {@link MapBuilder} to it, + * essentially adding all the keys it defines under the given {@code key}. + * + * @param key the key to add the map produced by the {@code builder} to. + * @param extractor the function that extracts a {@link Value} from a {@link Type}. + * @param builder the {@code builder} that encodes the {@link Value} into a map. + * @param the type of the value. + */ + public MapHasher accept(String key, Function extractor, MapBuilder builder) { + MapHasher hasher = new MapHasher<>(extractor.apply(object), encoder); + builder.apply(hasher); + accept(key, hasher.build()); + return this; + } + /** * Applies the {@link MapBuilder} to this {@link MapHasher}, essentially adding all the keys it defines here. */ @@ -167,6 +186,22 @@ public MapHasher optional(String key, MinecraftHasher hashe return optionalPredicate(key, hasher, extractor, value -> !value.equals(defaultValue)); } + /** + * Extracts a {@link Value} from a {@link Type} using the {@code extractor}, and adds it to the map only if {@code predicate} returns {@code true} for it. + * + * @param key the key to put the {@link Value} in. + * @param hasher the hasher used to hash a {@link Value}. + * @param extractor the function that extracts a {@link Value} from a {@link Type}. + * @param predicate the predicate that checks if a {@link Type} should be added to the map. The {@link Value} won't be added to the map if the predicate returns {@code false} for it. + * @param the type of the value. + */ + public MapHasher optionalTypePredicate(String key, MinecraftHasher hasher, Function extractor, Predicate predicate) { + if (predicate.test(object)) { + accept(key, hasher, extractor); + } + return this; + } + /** * Extracts a {@link Value} from a {@link Type} using the {@code extractor}, and adds it to the map only if {@code predicate} returns {@code true} for it. * @@ -213,6 +248,9 @@ public MapHasher optionalList(String key, MinecraftHasher v } public HashCode build() { + if (debug) { + GeyserImpl.getInstance().getLogger().info("Building MapHasher with unhashed map: " + unhashed); + } return encoder.map(map); } } diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHasher.java index 9ae55bb2d2b..36987ca3dcf 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHasher.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHasher.java @@ -31,6 +31,7 @@ import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.nbt.NbtList; import org.cloudburstmc.nbt.NbtMap; +import org.cloudburstmc.nbt.NbtUtils; import org.geysermc.geyser.inventory.item.DyeColor; import org.geysermc.geyser.item.components.Rarity; import org.geysermc.geyser.session.cache.registry.JavaRegistryProvider; @@ -42,6 +43,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemAttributeModifiers; import org.geysermc.mcprotocollib.protocol.data.game.item.component.Unit; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Locale; @@ -119,6 +121,8 @@ public interface MinecraftHasher { MinecraftHasher POS = INT_ARRAY.cast(pos -> new int[]{pos.getX(), pos.getY(), pos.getZ()}); + MinecraftHasher NBT_STRING = STRING.cast(MinecraftHasher::nbtObjectToCompressedString); + MinecraftHasher KEY = STRING.cast(Key::asString); MinecraftHasher TAG = STRING.cast(key -> '#' + key.asString()); @@ -143,7 +147,9 @@ public interface MinecraftHasher { .optionalNullable("texture", KEY, ResolvableProfile::getBody) .optionalNullable("cape", KEY, ResolvableProfile::getCape) .optionalNullable("elytra", KEY, ResolvableProfile::getElytra) - .optional("model", STRING, resolvableProfile -> Optional.ofNullable(resolvableProfile.getModel()).map(GameProfile.TextureModel::name)) + .optional("model", STRING, resolvableProfile -> Optional.ofNullable(resolvableProfile.getModel()) + .map(GameProfile.TextureModel::name) + .map(model -> model.toLowerCase(Locale.ROOT))) ); MinecraftHasher RARITY = fromIdEnum(Rarity.values(), Rarity::getName); @@ -188,6 +194,15 @@ default MinecraftHasher> list() { return (list, encoder) -> encoder.list(list.stream().map(element -> hash(element, encoder)).toList()); } + /** + * Casts this hasher to a hasher for {@link Casted}. This cast is done unsafely without converting of any kind, only use this if you are sure the object being encoded is of the type being cast to! + * + * @param the type to cast to. + */ + default MinecraftHasher cast() { + return cast(casted -> (Type) casted); + } + /** * "Casts" this hasher to another hash a different object, with a converter method. The returned hasher takes a {@link Casted}, converts it to a {@link Type} using the {@code converter}, and then hashes it. * @@ -394,4 +409,46 @@ static MinecraftHasher either(MinecraftHasher static MinecraftHasher dispatch(Function> hashDispatch) { return (value, encoder) -> hashDispatch.apply(value).hash(value, encoder); } + + private static String nbtObjectToCompressedString(Object object) { + // Can't just use NbtUtils.toString for everything because there are some differences with how Mojang does it + if (object instanceof NbtMap map) { + // Mojang compares entries by key when converting a NbtMap to a string + // See StringTagVisitor#visitCompound + List> entries = new ArrayList<>(map.entrySet()); + entries.sort(Map.Entry.comparingByKey()); + + // Manually converting to string because Mojang does not do newlines or spaces/indents, + // and doesn't put quotes for keys without spaces + // This will break for keys with quotes, but honestly, Cloud should just expand for this + StringBuilder mapString = new StringBuilder("{"); + for (int i = 0; i < entries.size(); i++) { + Map.Entry entry = entries.get(i); + if (entry.getKey().contains(" ")) { + mapString.append('"').append(entry.getKey()).append('"'); + } else { + mapString.append(entry.getKey()); + } + mapString.append(':'); + mapString.append(nbtObjectToCompressedString(entry.getValue())); + if (i < entries.size() - 1) { + mapString.append(','); + } + } + mapString.append('}'); + return mapString.toString(); + } else if (object instanceof NbtList list) { + // Same as above + StringBuilder listString = new StringBuilder("["); + for (int i = 0; i < list.size(); i++) { + listString.append(nbtObjectToCompressedString(list.get(i))); + if (i < list.size() - 1) { + listString.append(','); + } + } + listString.append(']'); + return listString.toString(); + } + return NbtUtils.toString(object).replaceAll("\\n *", ""); + } } diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/RegistryHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/RegistryHasher.java index aaadebe1c0f..06f4d248b70 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/RegistryHasher.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/RegistryHasher.java @@ -63,17 +63,17 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.item.component.Fireworks; import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.InstrumentComponent; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.Instrument; import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemAttributeModifiers; import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.JukeboxPlayable; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.JukeboxSong; import org.geysermc.mcprotocollib.protocol.data.game.item.component.KineticWeapon; import org.geysermc.mcprotocollib.protocol.data.game.item.component.MobEffectDetails; import org.geysermc.mcprotocollib.protocol.data.game.item.component.MobEffectInstance; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.ProvidesTrimMaterial; import org.geysermc.mcprotocollib.protocol.data.game.item.component.SuspiciousStewEffect; import org.geysermc.mcprotocollib.protocol.data.game.item.component.SwingAnimation; import org.geysermc.mcprotocollib.protocol.data.game.item.component.ToolData; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.TypedEntityData; import org.geysermc.mcprotocollib.protocol.data.game.item.component.Unit; import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType; import org.geysermc.mcprotocollib.protocol.data.game.level.sound.BuiltinSound; @@ -85,6 +85,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.function.Function; import java.util.stream.IntStream; @@ -137,7 +138,7 @@ public interface RegistryHasher extends MinecraftHasher { MinecraftHasher CUSTOM_SOUND = MinecraftHasher.mapBuilder(builder -> builder .accept("sound_id", KEY, sound -> MinecraftKey.key(sound.getName())) - .optional("range", FLOAT, CustomSound::getRange, 16.0F)); + .optionalTypePredicate("range", FLOAT, CustomSound::getRange, CustomSound::isNewSystem)); MinecraftHasher SOUND_EVENT = (sound, encoder) -> { if (sound instanceof BuiltinSound builtin) { @@ -148,13 +149,13 @@ public interface RegistryHasher extends MinecraftHasher { RegistryHasher DAMAGE_TYPE = registry(JavaRegistries.DAMAGE_TYPE); - MinecraftHasher DIRECT_INSTRUMENT = MinecraftHasher.mapBuilder(builder -> builder - .accept("sound_event", SOUND_EVENT, InstrumentComponent.Instrument::soundEvent) - .accept("use_duration", FLOAT, InstrumentComponent.Instrument::useDuration) - .accept("range", FLOAT, InstrumentComponent.Instrument::range) - .accept("description", ComponentHasher.COMPONENT, InstrumentComponent.Instrument::description)); + MinecraftHasher DIRECT_INSTRUMENT = MinecraftHasher.mapBuilder(builder -> builder + .accept("sound_event", SOUND_EVENT, Instrument::soundEvent) + .accept("use_duration", FLOAT, Instrument::useDuration) + .accept("range", FLOAT, Instrument::range) + .accept("description", ComponentHasher.COMPONENT, Instrument::description)); - RegistryHasher INSTRUMENT = registry(JavaRegistries.INSTRUMENT, DIRECT_INSTRUMENT); + RegistryHasher INSTRUMENT = registry(JavaRegistries.INSTRUMENT, DIRECT_INSTRUMENT); MinecraftHasher DIRECT_TRIM_MATERIAL = MinecraftHasher.mapBuilder(builder -> builder .accept("asset_name", MinecraftHasher.STRING, ArmorTrim.TrimMaterial::assetBase) @@ -170,13 +171,13 @@ public interface RegistryHasher extends MinecraftHasher { RegistryHasher TRIM_PATTERN = registry(JavaRegistries.TRIM_PATTERN, DIRECT_TRIM_PATTERN); - MinecraftHasher DIRECT_JUKEBOX_SONG = MinecraftHasher.mapBuilder(builder -> builder - .accept("sound_event", SOUND_EVENT, JukeboxPlayable.JukeboxSong::soundEvent) - .accept("description", ComponentHasher.COMPONENT, JukeboxPlayable.JukeboxSong::description) - .accept("length_in_seconds", FLOAT, JukeboxPlayable.JukeboxSong::lengthInSeconds) - .accept("comparator_output", INT, JukeboxPlayable.JukeboxSong::comparatorOutput)); + MinecraftHasher DIRECT_JUKEBOX_SONG = MinecraftHasher.mapBuilder(builder -> builder + .accept("sound_event", SOUND_EVENT, JukeboxSong::soundEvent) + .accept("description", ComponentHasher.COMPONENT, JukeboxSong::description) + .accept("length_in_seconds", FLOAT, JukeboxSong::lengthInSeconds) + .accept("comparator_output", INT, JukeboxSong::comparatorOutput)); - RegistryHasher JUKEBOX_SONG = registry(JavaRegistries.JUKEBOX_SONG, DIRECT_JUKEBOX_SONG); + RegistryHasher JUKEBOX_SONG = registry(JavaRegistries.JUKEBOX_SONG, DIRECT_JUKEBOX_SONG); MinecraftHasher DIRECT_BANNER_PATTERN = MinecraftHasher.mapBuilder(builder -> builder .accept("asset_id", KEY, BannerPatternLayer.BannerPattern::getAssetId) @@ -190,14 +191,26 @@ public interface RegistryHasher extends MinecraftHasher { RegistryHasher PIG_VARIANT = registry(JavaRegistries.PIG_VARIANT); + RegistryHasher PIG_SOUND_VARIANT = registry(JavaRegistries.PIG_SOUND_VARIANT); + RegistryHasher COW_VARIANT = registry(JavaRegistries.COW_VARIANT); + RegistryHasher COW_SOUND_VARIANT = registry(JavaRegistries.COW_SOUND_VARIANT); + + RegistryHasher CHICKEN_VARIANT = registry(JavaRegistries.CHICKEN_VARIANT); + + RegistryHasher CHICKEN_SOUND_VARIANT = registry(JavaRegistries.CHICKEN_SOUND_VARIANT); + + RegistryHasher ZOMBIE_NAUTILUS_VARIANT = registry(JavaRegistries.ZOMBIE_NAUTILUS_VARIANT); + RegistryHasher FROG_VARIANT = registry(JavaRegistries.FROG_VARIANT); RegistryHasher PAINTING_VARIANT = registry(JavaRegistries.PAINTING_VARIANT); RegistryHasher CAT_VARIANT = registry(JavaRegistries.CAT_VARIANT); + RegistryHasher CAT_SOUND_VARIANT = registry(JavaRegistries.CAT_SOUND_VARIANT); + // Entity variants // These are all not registries on Java, meaning they serialise as just literal strings, not namespaced IDs @@ -227,6 +240,7 @@ public interface RegistryHasher extends MinecraftHasher { @SuppressWarnings({"unchecked", "rawtypes"}) // Java generics :( MinecraftHasher> DATA_COMPONENT_VALUE = (component, encoder) -> { if (component.getValue() == null) { + // Component removal return UNIT.hash(Unit.INSTANCE, encoder); } MinecraftHasher hasher = DataComponentHashers.hasher(component.getType()); @@ -237,7 +251,7 @@ public interface RegistryHasher extends MinecraftHasher { MinecraftHasher ITEM_STACK = MinecraftHasher.mapBuilder(builder -> builder .accept("id", ITEM, ItemStack::getId) - .accept("count", INT, ItemStack::getAmount) + .optional("count", INT, ItemStack::getAmount, 1) .optionalNullable("components", DATA_COMPONENTS, ItemStack::getDataComponentsPatch)); // Encoding of hidden effects is unfortunately not possible @@ -266,12 +280,12 @@ public interface RegistryHasher extends MinecraftHasher { .accept("slot", INT, ItemContainerSlot::index) .accept("item", ITEM_STACK, ItemContainerSlot::item)); - MinecraftHasher> ITEM_CONTAINER_CONTENTS = CONTAINER_SLOT.list().cast(stacks -> { + MinecraftHasher>> ITEM_CONTAINER_CONTENTS = CONTAINER_SLOT.list().cast(stacks -> { List slots = new ArrayList<>(); for (int i = 0; i < stacks.size(); i++) { - ItemStack stack = stacks.get(i); - if (stack != null) { - slots.add(new ItemContainerSlot(i, stacks.get(i))); + Optional stack = stacks.get(i); + if (stack.isPresent()) { + slots.add(new ItemContainerSlot(i, stack.get())); } } return slots; @@ -279,11 +293,11 @@ public interface RegistryHasher extends MinecraftHasher { MinecraftHasher BLOCK_PREDICATE = MinecraftHasher.mapBuilder(builder -> builder .optionalNullable("blocks", BLOCK.holderSet(), AdventureModePredicate.BlockPredicate::getBlocks) - .optionalNullable("nbt", NBT_MAP, AdventureModePredicate.BlockPredicate::getNbt)); // Property and data component matchers are, unfortunately, too complicated to include here + .optionalNullable("nbt", NBT_STRING, AdventureModePredicate.BlockPredicate::getNbt)); // Property and data component matchers are, unfortunately, too complicated to include here // Encode as a single element if the list only has one element MinecraftHasher ADVENTURE_MODE_PREDICATE = MinecraftHasher.either(BLOCK_PREDICATE, - predicate -> predicate.getPredicates().size() == 1 ? predicate.getPredicates().get(0) : null, BLOCK_PREDICATE.list(), AdventureModePredicate::getPredicates); + predicate -> predicate.getPredicates().size() == 1 ? predicate.getPredicates().getFirst() : null, BLOCK_PREDICATE.list(), AdventureModePredicate::getPredicates); MinecraftHasher ATTRIBUTE_MODIFIER_DISPLAY_TYPE = MinecraftHasher.fromEnum(); @@ -304,16 +318,12 @@ public interface RegistryHasher extends MinecraftHasher { MinecraftHasher ITEM_USE_ANIMATION = MinecraftHasher.fromEnum(); - MinecraftHasher CONSUME_EFFECT_TYPE = enumRegistry(); - - MinecraftHasher CONSUME_EFFECT = CONSUME_EFFECT_TYPE.dispatch(ConsumeEffectType::fromEffect, ConsumeEffectType::mapBuilder); + MinecraftHasher CONSUME_EFFECT = ConsumeEffectType.CONSUME_EFFECT_HASHER; MinecraftHasher SUSPICIOUS_STEW_EFFECT = MinecraftHasher.mapBuilder(builder -> builder .accept("id", EFFECT_ID, SuspiciousStewEffect::getMobEffectId) .optional("duration", INT, SuspiciousStewEffect::getDuration, 160)); - MinecraftHasher INSTRUMENT_COMPONENT = MinecraftHasher.either(INSTRUMENT.holder(), InstrumentComponent::instrumentHolder, KEY, InstrumentComponent::instrumentLocation); - MinecraftHasher TOOL_RULE = MinecraftHasher.mapBuilder(builder -> builder .accept("blocks", RegistryHasher.BLOCK.holderSet(), ToolData.Rule::getBlocks) .optionalNullable("speed", MinecraftHasher.FLOAT, ToolData.Rule::getSpeed) @@ -337,14 +347,10 @@ public interface RegistryHasher extends MinecraftHasher { MinecraftHasher SWING_ANIMATION_TYPE = MinecraftHasher.fromEnum(); - MinecraftHasher PROVIDES_TRIM_MATERIAL = MinecraftHasher.either(TRIM_MATERIAL.holder(), ProvidesTrimMaterial::materialHolder, KEY, ProvidesTrimMaterial::materialLocation); - MinecraftHasher ARMOR_TRIM = MinecraftHasher.mapBuilder(builder -> builder .accept("material", TRIM_MATERIAL.holder(), ArmorTrim::material) .accept("pattern", TRIM_PATTERN.holder(), ArmorTrim::pattern)); - MinecraftHasher JUKEBOX_PLAYABLE = MinecraftHasher.either(JUKEBOX_SONG.holder(), JukeboxPlayable::songHolder, KEY, JukeboxPlayable::songLocation); - MinecraftHasher BANNER_PATTERN_LAYER = MinecraftHasher.mapBuilder(builder -> builder .accept("pattern", BANNER_PATTERN.holder(), BannerPatternLayer::getPattern) .accept("color", DYE_COLOR, BannerPatternLayer::getColorId)); @@ -359,8 +365,9 @@ public interface RegistryHasher extends MinecraftHasher { .optional("has_twinkle", BOOL, Fireworks.FireworkExplosion::isHasTwinkle, false)); MinecraftHasher BEEHIVE_OCCUPANT = MinecraftHasher.mapBuilder(builder -> builder - .accept("id", RegistryHasher.ENTITY_TYPE_KEY, beehiveOccupant -> beehiveOccupant.getEntityData().type()) - .accept(beehiveOccupant -> beehiveOccupant.getEntityData().tag(), MapBuilder.inlineNbtMap()) + .accept("entity_data", BeehiveOccupant::getEntityData, entityDataBuilder -> entityDataBuilder + .accept("id", ENTITY_TYPE_KEY, TypedEntityData::type) + .accept(TypedEntityData::tag, MapBuilder.inlineNbtMap())) .accept("ticks_in_hive", INT, BeehiveOccupant::getTicksInHive) .accept("min_ticks_in_hive", INT, BeehiveOccupant::getMinTicksInHive)); @@ -464,18 +471,6 @@ static > RegistryHasher enumIdRegistr return hasher::hash; } - /** - * Creates a hasher that hashes a {@code Holder}, also known as an {@code EitherHolder} in Mojmap. - * - *

Please note that a {@code Holder} is only a valid representation of an {@code EitherHolder} in MCPL if the stream codec of the {@code EitherHolder} does not support directly encoding unregistered values.

- * - * @param registry the registry the {@code Holder} is for. - * @return a hasher that hashes a {@code Holder} for the given registry. - */ - static MinecraftHasher> eitherHolderHasher(JavaRegistryKey registry) { - return MinecraftHasher.KEY.registryCast((registries, holder) -> holder.getOrCompute(id -> registry.key(registries, id))); - } - class RegistryHasherWithDirectHasher implements RegistryHasher { private final MinecraftHasher id; private final MinecraftHasher> holderHasher; diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/data/ComponentPosHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/data/ComponentPosHasher.java new file mode 100644 index 00000000000..89c4364e61b --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/data/ComponentPosHasher.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2026 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.item.hashing.data; + +import net.kyori.adventure.text.BlockNBTComponent; +import org.geysermc.geyser.item.hashing.MinecraftHasher; + +public interface ComponentPosHasher { + + MinecraftHasher LOCAL_POS_HASHER = MinecraftHasher.STRING.cast(BlockNBTComponent.LocalPos::asString); + + MinecraftHasher WORLD_POS_HASHER = MinecraftHasher.STRING.cast(BlockNBTComponent.WorldPos::asString); + + MinecraftHasher POS_HASHER = MinecraftHasher.dispatch(pos -> switch (pos) { + case BlockNBTComponent.LocalPos ignored -> LOCAL_POS_HASHER.cast(); + case BlockNBTComponent.WorldPos ignored -> WORLD_POS_HASHER.cast(); + default -> throw new IllegalArgumentException("Unimplemented hasher for pos " + pos); + }); +} diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/data/ConsumeEffectType.java b/core/src/main/java/org/geysermc/geyser/item/hashing/data/ConsumeEffectType.java index 6c03ff3e076..1691ded3eb1 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/data/ConsumeEffectType.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/data/ConsumeEffectType.java @@ -25,12 +25,13 @@ package org.geysermc.geyser.item.hashing.data; +import org.geysermc.geyser.item.hashing.EnumMapDispatchHasher; import org.geysermc.geyser.item.hashing.MapBuilder; import org.geysermc.geyser.item.hashing.MinecraftHasher; import org.geysermc.geyser.item.hashing.RegistryHasher; import org.geysermc.mcprotocollib.protocol.data.game.item.component.ConsumeEffect; -public enum ConsumeEffectType { +public enum ConsumeEffectType implements EnumMapDispatchHasher { APPLY_EFFECTS(ConsumeEffect.ApplyEffects.class, builder -> builder .acceptList("effects", RegistryHasher.MOB_EFFECT_INSTANCE, ConsumeEffect.ApplyEffects::effects) .optional("probability", MinecraftHasher.FLOAT, ConsumeEffect.ApplyEffects::probability, 1.0F)), @@ -42,6 +43,9 @@ public enum ConsumeEffectType { PLAY_SOUND(ConsumeEffect.PlaySound.class, builder -> builder .accept("sound", RegistryHasher.SOUND_EVENT, ConsumeEffect.PlaySound::sound)); + public static final MinecraftHasher CONSUME_EFFECT_TYPE_HASHER = RegistryHasher.enumRegistry(); + public static final MinecraftHasher CONSUME_EFFECT_HASHER = MinecraftHasher.mapBuilder(EnumMapDispatchHasher.dispatch(CONSUME_EFFECT_TYPE_HASHER, ConsumeEffectType::values)); + private final Class clazz; private final MapBuilder builder; @@ -55,17 +59,18 @@ ConsumeEffectType(Class clazz, MapBuilder builde this.builder = builder; } - public MapBuilder mapBuilder() { - return builder.cast(); + @Override + public ConsumeEffectType distinction() { + return this; + } + + @Override + public Class valueTypeClass() { + return clazz; } - public static ConsumeEffectType fromEffect(ConsumeEffect effect) { - Class clazz = effect.getClass(); - for (ConsumeEffectType type : values()) { - if (clazz == type.clazz) { - return type; - } - } - throw new IllegalStateException("Unimplemented consume effect type for hashing"); + @Override + public MapBuilder mapBuilder() { + return builder; } } diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/data/NbtComponentType.java b/core/src/main/java/org/geysermc/geyser/item/hashing/data/NbtComponentType.java new file mode 100644 index 00000000000..b36c45f9273 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/data/NbtComponentType.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026 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.item.hashing.data; + +import net.kyori.adventure.text.BlockNBTComponent; +import net.kyori.adventure.text.EntityNBTComponent; +import net.kyori.adventure.text.NBTComponent; +import net.kyori.adventure.text.StorageNBTComponent; +import org.geysermc.geyser.item.hashing.EnumMapDispatchHasher; +import org.geysermc.geyser.item.hashing.MapBuilder; +import org.geysermc.geyser.item.hashing.MinecraftHasher; + +public enum NbtComponentType implements EnumMapDispatchHasher> { + BLOCK(BlockNBTComponent.class, builder -> builder + .accept("block", ComponentPosHasher.POS_HASHER, BlockNBTComponent::pos)), + ENTITY(EntityNBTComponent.class, builder -> builder + .accept("entity", MinecraftHasher.STRING, component -> component.selector())), + STORAGE(StorageNBTComponent.class, builder -> builder + .accept("storage", MinecraftHasher.KEY, StorageNBTComponent::storage)); + + public static final MapBuilder> NBT_COMPONENT_SOURCE_MAP_BUILDER = EnumMapDispatchHasher.dispatchFuzzy(NbtComponentType::values); + + private final Class> clazz; + private final MapBuilder> mapBuilder; + + > NbtComponentType(Class clazz, MapBuilder mapBuilder) { + this.clazz = clazz; + this.mapBuilder = mapBuilder; + } + + @Override + public NbtComponentType distinction() { + return this; + } + + @Override + public Class> valueTypeClass() { + return clazz; + } + + @Override + public MapBuilder> mapBuilder() { + return mapBuilder; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/data/ObjectContentsType.java b/core/src/main/java/org/geysermc/geyser/item/hashing/data/ObjectContentsType.java index 63d769f5ea8..48365923a49 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/data/ObjectContentsType.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/data/ObjectContentsType.java @@ -25,46 +25,48 @@ package org.geysermc.geyser.item.hashing.data; -import lombok.Getter; import net.kyori.adventure.text.object.ObjectContents; import net.kyori.adventure.text.object.PlayerHeadObjectContents; import net.kyori.adventure.text.object.SpriteObjectContents; import org.geysermc.geyser.item.hashing.ComponentHasher; +import org.geysermc.geyser.item.hashing.EnumMapDispatchHasher; import org.geysermc.geyser.item.hashing.MapBuilder; import org.geysermc.geyser.item.hashing.MinecraftHasher; import java.util.function.Function; -public enum ObjectContentsType { - ATLAS("atlas", SpriteObjectContents.class, +public enum ObjectContentsType implements EnumMapDispatchHasher { + ATLAS(SpriteObjectContents.class, builder -> builder .optional("atlas", MinecraftHasher.KEY, SpriteObjectContents::atlas, SpriteObjectContents.DEFAULT_ATLAS) .accept("sprite", MinecraftHasher.KEY, SpriteObjectContents::sprite)), - PLAYER("player", PlayerHeadObjectContents.class, + PLAYER(PlayerHeadObjectContents.class, builder -> builder .accept("player", ComponentHasher.RESOLVABLE_PROFILE, Function.identity()) .optional("hat", MinecraftHasher.BOOL, PlayerHeadObjectContents::hat, true)); - @Getter - private final String name; + public static final MapBuilder OBJECT_CONTENTS_MAP_BUILDER = EnumMapDispatchHasher.dispatchFuzzy(ObjectContentsType::values); + + private final Class clazz; private final MapBuilder builder; - @SuppressWarnings("unused") // So Java knows what T we are talking about - ObjectContentsType(String name, Class clazz, MapBuilder builder) { - this.name = name; + ObjectContentsType(Class clazz, MapBuilder builder) { + this.clazz = clazz; this.builder = builder; } - public MapBuilder mapBuilder() { - return builder.cast(); + @Override + public ObjectContentsType distinction() { + return this; + } + + @Override + public Class valueTypeClass() { + return clazz; } - public static ObjectContentsType fromContents(ObjectContents contents) { - if (contents instanceof SpriteObjectContents) { - return ATLAS; - } else if (contents instanceof PlayerHeadObjectContents) { - return PLAYER; - } - throw new UnsupportedOperationException("Don't know how to hash object contents of type " + contents.getClass()); + @Override + public MapBuilder mapBuilder() { + return builder; } } diff --git a/core/src/main/java/org/geysermc/geyser/item/parser/ItemStackParser.java b/core/src/main/java/org/geysermc/geyser/item/parser/ItemStackParser.java index 2da04a329ed..808d1ceaaf8 100644 --- a/core/src/main/java/org/geysermc/geyser/item/parser/ItemStackParser.java +++ b/core/src/main/java/org/geysermc/geyser/item/parser/ItemStackParser.java @@ -151,6 +151,7 @@ private static ItemEnchantments parseEnchantments(GeyserSession session, NbtMap List colours = raw.getList("colors", NbtType.INT); return new CustomModelData(floats, flags, strings, colours); }); + registerSimple(DataComponentTypes.DYE, String.class, raw -> DyeColor.getByJavaIdentifier(raw).ordinal()); registerSimple(DataComponentTypes.DYED_COLOR, Integer.class); registerSimple(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, Boolean.class); register(DataComponentTypes.ENCHANTMENTS, NbtMap.class, ItemStackParser::parseEnchantments); diff --git a/core/src/main/java/org/geysermc/geyser/item/type/CrossbowItem.java b/core/src/main/java/org/geysermc/geyser/item/type/CrossbowItem.java index 27224f027e1..7d6d27327d9 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/CrossbowItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/CrossbowItem.java @@ -29,7 +29,6 @@ import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.item.TooltipOptions; -import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.geyser.translator.item.ItemTranslator; @@ -50,7 +49,7 @@ public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNul List chargedProjectiles = components.get(DataComponentTypes.CHARGED_PROJECTILES); if (chargedProjectiles != null && !chargedProjectiles.isEmpty()) { - ItemStack javaProjectile = chargedProjectiles.get(0); + ItemStack javaProjectile = chargedProjectiles.getFirst(); ItemData itemData = ItemTranslator.translateToBedrock(session, javaProjectile); NbtMapBuilder newProjectile = BedrockItemBuilder.createItemNbt(itemData); diff --git a/core/src/main/java/org/geysermc/geyser/item/type/GoatHornItem.java b/core/src/main/java/org/geysermc/geyser/item/type/GoatHornItem.java index 7d0cfa796f5..4dce3155d50 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/GoatHornItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/GoatHornItem.java @@ -37,7 +37,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.Holder; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.InstrumentComponent; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.Instrument; public class GoatHornItem extends Item { public GoatHornItem(String javaIdentifier, Builder builder) { @@ -51,7 +51,7 @@ public ItemData.Builder translateToBedrock(GeyserSession session, int count, Dat return builder; } - InstrumentComponent instrumentComponent = components.get(DataComponentTypes.INSTRUMENT); + Holder instrumentComponent = components.get(DataComponentTypes.INSTRUMENT); if (instrumentComponent != null) { GeyserInstrument instrument = GeyserInstrument.fromComponent(session, instrumentComponent); int bedrockId = instrument.bedrockId(); @@ -67,7 +67,7 @@ public ItemData.Builder translateToBedrock(GeyserSession session, int count, Dat public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { super.translateComponentsToBedrock(session, components, tooltip, builder); - InstrumentComponent component = components.get(DataComponentTypes.INSTRUMENT); + Holder component = components.get(DataComponentTypes.INSTRUMENT); if (component != null && tooltip.showInTooltip(DataComponentTypes.INSTRUMENT)) { GeyserInstrument instrument = GeyserInstrument.fromComponent(session, component); if (instrument.bedrockInstrument() == null) { @@ -82,7 +82,7 @@ public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNul int damage = itemData.getDamage(); // This could cause an issue since -1 is returned for non-vanilla goat horns - itemStack.getOrCreateComponents().put(DataComponentTypes.INSTRUMENT, new InstrumentComponent(Holder.ofId(GeyserInstrument.bedrockIdToJava(session, damage)), null)); + itemStack.getOrCreateComponents().put(DataComponentTypes.INSTRUMENT, Holder.ofId(GeyserInstrument.bedrockIdToJava(session, damage))); return itemStack; } diff --git a/core/src/main/java/org/geysermc/geyser/item/type/Item.java b/core/src/main/java/org/geysermc/geyser/item/type/Item.java index feae9aa1a98..08827955c7a 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/Item.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/Item.java @@ -284,7 +284,7 @@ public void translateNbtToJava(GeyserSession session, @NonNull NbtMap bedrockTag private void addJavaOnlyEnchantment(GeyserSession session, BedrockItemBuilder builder, String enchantmentName, int level) { String lvlTranslation = MinecraftLocale.getLocaleString("enchantment.level." + level, session.locale()); - builder.getOrCreateLore().add(0, ChatColor.RESET + ChatColor.GRAY + enchantmentName + " " + lvlTranslation); + builder.getOrCreateLore().addFirst(ChatColor.RESET + ChatColor.GRAY + enchantmentName + " " + lvlTranslation); } protected final void translateDyedColor(DataComponents components, BedrockItemBuilder builder) { diff --git a/core/src/main/java/org/geysermc/geyser/item/type/ShulkerBoxItem.java b/core/src/main/java/org/geysermc/geyser/item/type/ShulkerBoxItem.java index b898b9ad691..4d3420a16bd 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/ShulkerBoxItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/ShulkerBoxItem.java @@ -49,6 +49,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; public class ShulkerBoxItem extends BlockItem { public ShulkerBoxItem(Builder builder, Block block, Block... otherBlocks) { @@ -59,17 +60,18 @@ public ShulkerBoxItem(Builder builder, Block block, Block... otherBlocks) { public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { super.translateComponentsToBedrock(session, components, tooltip, builder); - List contents = components.get(DataComponentTypes.CONTAINER); + List> contents = components.get(DataComponentTypes.CONTAINER); if (contents == null || contents.isEmpty()) { // Empty shulker box return; } List itemsList = new ArrayList<>(); for (int slot = 0; slot < contents.size(); slot++) { - ItemStack item = contents.get(slot); - if (item == null || item.getId() == Items.AIR_ID) { + Optional optionalItem = contents.get(slot); + if (optionalItem.isEmpty() || optionalItem.get().getId() == Items.AIR_ID) { continue; } + ItemStack item = optionalItem.get(); ItemMapping boxMapping = session.getItemMappings().getMapping(item.getId()); int bedrockData = boxMapping.getBedrockData(); diff --git a/core/src/main/java/org/geysermc/geyser/item/type/TropicalFishBucketItem.java b/core/src/main/java/org/geysermc/geyser/item/type/TropicalFishBucketItem.java index 011d5bb2244..fbdc8f059c2 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/TropicalFishBucketItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/TropicalFishBucketItem.java @@ -70,7 +70,7 @@ public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNul int predefinedVariantId = TropicalFishEntity.getPredefinedId(packedVariant); if (predefinedVariantId != -1) { Component line = Component.translatable("entity.minecraft.tropical_fish.predefined." + predefinedVariantId, LORE_STYLE); - lore.add(0, MessageTranslator.convertMessage(line, session.locale())); + lore.addFirst(MessageTranslator.convertMessage(line, session.locale())); } else { Component typeTooltip = Component.translatable("entity.minecraft.tropical_fish.type." + TropicalFishEntity.getVariantName(packedVariant), LORE_STYLE); lore.add(0, MessageTranslator.convertMessage(typeTooltip, session.locale())); diff --git a/core/src/main/java/org/geysermc/geyser/level/FireworkColor.java b/core/src/main/java/org/geysermc/geyser/level/FireworkColor.java index dafe53707db..db436a8b5c9 100644 --- a/core/src/main/java/org/geysermc/geyser/level/FireworkColor.java +++ b/core/src/main/java/org/geysermc/geyser/level/FireworkColor.java @@ -29,6 +29,7 @@ import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.util.HSVLike; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.util.ColorUtils; import java.util.Locale; @@ -67,19 +68,12 @@ public enum FireworkColor { this.name = this.name().toLowerCase(Locale.ROOT); } - private static HSVLike toHSV(int rgbValue) { - int r = (rgbValue & (255 << 16)) >> 16; - int g = (rgbValue & (255 << 8)) >> 8; - int b = rgbValue & 255; - return HSVLike.fromRGB(r, g, b); - } - public static byte fromJavaRGB(int rgbValue) { - HSVLike hsv = toHSV(rgbValue); + HSVLike hsv = ColorUtils.toHSV(rgbValue); return (byte) nearestTo(hsv).ordinal(); } - // The following two methods were adapted from the Adventure project: + // Adapted from the Adventure project: // https://github.com/KyoriPowered/adventure/blob/09edf74409feb52d9147a5a811910de0721acf95/api/src/main/java/net/kyori/adventure/text/format/NamedTextColor.java#L193-L237 /** * Find the firework color nearest to the provided color. @@ -92,7 +86,7 @@ public static byte fromJavaRGB(int rgbValue) { float matchedDistance = Float.MAX_VALUE; FireworkColor match = VALUES[0]; for (final FireworkColor potential : VALUES) { - final float distance = distance(any, potential.color.asHSV()); + final float distance = ColorUtils.distance(any, potential.color.asHSV()); if (distance < matchedDistance) { match = potential; matchedDistance = distance; @@ -104,22 +98,6 @@ public static byte fromJavaRGB(int rgbValue) { return match; } - /** - * Returns a distance metric to the other color. - * - *

This value is unitless and should only be used to compare with other firework colors.

- * - * @param other color to compare to - * @return distance metric - */ - private static float distance(final HSVLike self, final HSVLike other) { - // weight hue more heavily than saturation and brightness. kind of magic numbers, but is fine for our use case of downsampling to a set of colors - final float hueDistance = 3 * Math.min(Math.abs(self.h() - other.h()), 1f - Math.abs(self.h() - other.h())); - final float saturationDiff = self.s() - other.s(); - final float valueDiff = self.v() - other.v(); - return hueDistance * hueDistance + saturationDiff * saturationDiff + valueDiff * valueDiff; - } - public static int fromBedrockId(int id) { for (FireworkColor fireworkColor : VALUES) { if (fireworkColor.ordinal() == id) { diff --git a/core/src/main/java/org/geysermc/geyser/level/GameRule.java b/core/src/main/java/org/geysermc/geyser/level/GameRule.java deleted file mode 100644 index 02fc7a9248d..00000000000 --- a/core/src/main/java/org/geysermc/geyser/level/GameRule.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2019-2022 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.level; - -import lombok.Getter; - -import java.util.Locale; - -/** - * This enum stores each gamerule along with the value type and the default. - * It is used to construct the list for the settings menu - */ -// TODO gamerules with feature flags (e.g. minecart speed with minecart experiment) -public enum GameRule { - ADVANCE_TIME("gamerule.doDaylightCycle", true), - ADVANCE_WEATHER("gamerule.doWeatherCycle", true), - ALLOW_ENTERING_NETHER_USING_PORTALS("gamerule.allowEnteringNetherUsingPortals", true), - BLOCK_DROPS("gamerule.doTileDrops", true), - BLOCK_EXPLOSION_DROP_DECAY("gamerule.blockExplosionDropDecay", true), - COMMAND_BLOCKS_WORK("gamerule.commandBlocksEnabled", true), - COMMAND_BLOCK_OUTPUT("gamerule.commandBlockOutput", true), - DROWNING_DAMAGE("gamerule.drowningDamage", true), - ELYTRA_MOVEMENT_CHECK("gamerule.minecraft.elytra_movement_check", true), - ENDER_PEARLS_VANISH_ON_DEATH("gamerule.enderPearlsVanishOnDeath", true), - ENTITY_DROPS("gamerule.doEntityDrops", true), - FALL_DAMAGE("gamerule.fallDamage", true), - FIRE_DAMAGE("gamerule.fireDamage", true), - FIRE_SPREAD_RADIUS_AROUND_PLAYER("gamerule.minecraft.fire_spread_radius_around_player", 128), - FORGIVE_DEAD_PLAYERS("gamerule.forgiveDeadPlayers", true), - FREEZE_DAMAGE("gamerule.freezeDamage", true), - GLOBAL_SOUND_EVENTS("gamerule.globalSoundEvents", true), - IMMEDIATE_RESPAWN("gamerule.doImmediateRespawn", false), - KEEP_INVENTORY("gamerule.keepInventory", false), - LAVA_SOURCE_CONVERSION("gamerule.lavaSourceConversion", false), - LIMITED_CRAFTING("gamerule.doLimitedCrafting", false), - LOCATOR_BAR("gamerule.locatorBar", true), - LOG_ADMIN_COMMANDS("gamerule.logAdminCommands", true), - MAX_BLOCK_MODIFICATIONS("gamerule.commandModificationBlockLimit", 32768), - MAX_COMMAND_FORKS("gamerule.maxCommandForkCount", 65536), - MAX_COMMAND_SEQUENCE_LENGTH("gamerule.maxCommandChainLength", 65536), - MAX_ENTITY_CRAMMING("gamerule.maxEntityCramming", 24), - MAX_SNOW_ACCUMULATION_HEIGHT("gamerule.snowAccumulationHeight", 1), - MOB_DROPS("gamerule.doMobLoot", true), - MOB_EXPLOSION_DROP_DECAY("gamerule.mobExplosionDropDecay", true), - MOB_GRIEFING("gamerule.mobGriefing", true), - NATURAL_HEALTH_REGENERATION("gamerule.naturalRegeneration", true), - PLAYER_MOVEMENT_CHECK("gamerule.minecraft.player_movement_check", true), - PLAYERS_NETHER_PORTAL_CREATIVE_DELAY("gamerule.playersNetherPortalCreativeDelay", 0), - PLAYERS_NETHER_PORTAL_DEFAULT_DELAY("gamerule.playersNetherPortalDefaultDelay", 80), - PLAYERS_SLEEPING_PERCENTAGE("gamerule.playersSleepingPercentage", 100), - PROJECTILES_CAN_BREAK_BLOCKS("gamerule.projectilesCanBreakBlocks", true), - PVP("gamerule.pvp", true), - RAIDS("gamerule.minecraft.raids", true), - RANDOM_TICK_SPEED("gamerule.randomTickSpeed", 3), - REDUCED_DEBUG_INFO("gamerule.reducedDebugInfo", false), - RESPAWN_RADIUS("gamerule.spawnRadius", 10), - SEND_COMMAND_FEEDBACK("gamerule.sendCommandFeedback", true), - SHOW_ADVANCEMENT_MESSAGES("gamerule.announceAdvancements", true), - SHOW_DEATH_MESSAGES("gamerule.showDeathMessages", true), - SPAWNER_BLOCKS_WORK("gamerule.spawnerBlocksEnabled", true), - SPAWN_MOBS("gamerule.doMobSpawning", true), - SPAWN_MONSTERS("gamerule.spawnMonsters", true), - SPAWN_PATROLS("gamerule.doPatrolSpawning", true), - SPAWN_PHANTOMS("gamerule.doInsomnia", true), - SPAWN_WANDERING_TRADERS("gamerule.doTraderSpawning", true), - SPAWN_WARDENS("gamerule.doWardenSpawning", true), - SPECTATORS_GENERATE_CHUNKS("gamerule.spectatorsGenerateChunks", true), - SPREAD_VINES("gamerule.doVinesSpread", true), - TNT_EXPLODES("gamerule.tntExplodes", true), - TNT_EXPLOSION_DROP_DECAY("gamerule.tntExplosionDropDecay", false), - UNIVERSAL_ANGER("gamerule.universalAnger", false), - WATER_SOURCE_CONVERSION("gamerule.waterSourceConversion", true); - - public static final GameRule[] VALUES = values(); - - @Getter - private final String translation; - - @Getter - private final Class type; - - private final int defaultValue; - - GameRule(String translation, boolean defaultValue) { - this.translation = translation; - this.type = Boolean.class; - this.defaultValue = defaultValue ? 1 : 0; - } - - GameRule(String translation, int defaultValue) { - this.translation = translation; - this.type = Integer.class; - this.defaultValue = defaultValue; - } - - public boolean getDefaultBooleanValue() { - return defaultValue != 0; - } - - public int getDefaultIntValue() { - return defaultValue; - } - - public String getJavaID() { - return name().toLowerCase(Locale.ROOT); - } -} diff --git a/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java b/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java index ca2ebcb085d..83cc04290d6 100644 --- a/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java @@ -25,8 +25,6 @@ package org.geysermc.geyser.level; -import it.unimi.dsi.fastutil.objects.Object2ObjectMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.cloudburstmc.math.vector.Vector3i; import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPacket; import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket; @@ -38,7 +36,6 @@ import java.util.concurrent.CompletableFuture; public class GeyserWorldManager extends WorldManager { - private final Object2ObjectMap gameruleCache = new Object2ObjectOpenHashMap<>(); @Override public int getBlockAt(GeyserSession session, int x, int y, int z) { @@ -89,32 +86,6 @@ public boolean hasOwnChunkCache() { return false; } - @Override - public void setGameRule(GeyserSession session, String name, Object value) { - super.setGameRule(session, name, value); - gameruleCache.put(name, String.valueOf(value)); - } - - @Override - public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) { - String value = gameruleCache.get(gameRule.getJavaID()); - if (value != null) { - return Boolean.parseBoolean(value); - } - - return gameRule.getDefaultBooleanValue(); - } - - @Override - public int getGameRuleInt(GeyserSession session, GameRule gameRule) { - String value = gameruleCache.get(gameRule.getJavaID()); - if (value != null) { - return Integer.parseInt(value); - } - - return gameRule.getDefaultIntValue(); - } - @Override public GameMode getDefaultGameMode(GeyserSession session) { return GameMode.SURVIVAL; diff --git a/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java b/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java index 63fee7094ea..78796140bf8 100644 --- a/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java +++ b/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java @@ -25,10 +25,13 @@ package org.geysermc.geyser.level; +import net.kyori.adventure.key.InvalidKeyException; import net.kyori.adventure.key.Key; +import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.nbt.NbtMap; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; import org.geysermc.geyser.util.DimensionUtils; +import org.geysermc.geyser.util.MinecraftKey; /** * Represents the information we store from the current Java dimension @@ -40,7 +43,7 @@ * As a Java dimension can be null in some login cases (e.g. GeyserConnect), make sure the player * is logged in before utilizing this field. */ -public record JavaDimension(int minY, int height, boolean piglinSafe, boolean ultrawarm, int bedrockId, boolean isNetherLike) { +public record JavaDimension(int minY, int height, boolean piglinSafe, boolean ultrawarm, int bedrockId, boolean isNetherLike, @Nullable Key defaultClock) { public static JavaDimension read(RegistryEntryContext entry) { NbtMap dimension = entry.data(); @@ -64,9 +67,21 @@ public static JavaDimension read(RegistryEntryContext entry) { isNetherLike = BedrockDimension.NETHER_IDENTIFIER.equals(identifier); } else { // Effects should give is a clue on how this (custom) dimension is supposed to look like - String effects = dimension.getString("effects"); - bedrockId = DimensionUtils.javaToBedrock(effects); - isNetherLike = BedrockDimension.NETHER_IDENTIFIER.equals(effects); + String skybox = dimension.getString("skybox"); + String skyboxId = switch (skybox) { + case "none" -> "minecraft:the_nether"; + case "end" -> "minecraft:the_end"; + default -> "minecraft:overworld"; + }; + bedrockId = DimensionUtils.javaToBedrock(skyboxId); + isNetherLike = BedrockDimension.NETHER_IDENTIFIER.equals(skyboxId); + } + + Key defaultClock; + try { + defaultClock = MinecraftKey.nullableKey(dimension.getString("default_clock", null)); + } catch (InvalidKeyException exception) { + defaultClock = null; } if (minY % 16 != 0) { @@ -76,6 +91,6 @@ public static JavaDimension read(RegistryEntryContext entry) { throw new RuntimeException("Height must be a multiple of 16!"); } - return new JavaDimension(minY, height, piglinSafe, ultrawarm, bedrockId, isNetherLike); + return new JavaDimension(minY, height, piglinSafe, ultrawarm, bedrockId, isNetherLike, defaultClock); } } diff --git a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java index 48f9734b274..5e845aa7236 100644 --- a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java @@ -25,10 +25,6 @@ package org.geysermc.geyser.level; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3i; @@ -36,19 +32,12 @@ import org.geysermc.geyser.level.block.type.BlockState; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty; -import java.util.HashMap; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import java.util.function.Function; /** * Class that manages or retrieves various information @@ -118,35 +107,6 @@ public int[] getBlocksAt(GeyserSession session, BlockPositionIterator iter) { */ public abstract boolean hasOwnChunkCache(); - /** - * Updates a gamerule value on the Java server - * - * @param session The session of the user that requested the change - * @param name The gamerule to change - * @param value The new value for the gamerule - */ - public void setGameRule(GeyserSession session, String name, Object value) { - session.sendCommandPacket("gamerule " + name + " " + value); - } - - /** - * Gets a gamerule value as a boolean - * - * @param session The session of the user that requested the value - * @param gameRule The gamerule to fetch the value of - * @return The boolean representation of the value - */ - public abstract boolean getGameRuleBool(GeyserSession session, GameRule gameRule); - - /** - * Get a gamerule value as an integer - * - * @param session The session of the user that requested the value - * @param gameRule The gamerule to fetch the value of - * @return The integer representation of the value - */ - public abstract int getGameRuleInt(GeyserSession session, GameRule gameRule); - /** * Get the default game mode of the server * @@ -188,20 +148,4 @@ public void setDifficulty(GeyserSession session, Difficulty difficulty) { */ public void getDecoratedPotData(GeyserSession session, Vector3i pos, Consumer> apply) { } - - protected static final Function, DataComponents> RAW_TRANSFORMER = map -> { - try { - Map, DataComponent> components = new HashMap<>(); - Int2ObjectMaps.fastForEach(map, entry -> { - DataComponentType type = DataComponentTypes.from(entry.getIntKey()); - ByteBuf buf = Unpooled.wrappedBuffer(entry.getValue()); - DataComponent value = type.readDataComponent(buf); - components.put(type, value); - }); - return new DataComponents(components); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - }; } diff --git a/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java b/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java index fa3e6b6170a..8a03f3ae261 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java @@ -109,11 +109,11 @@ public final class Blocks { .intState(LEVEL))); public static final Block SAND = register(new Block("sand", builder().destroyTime(0.5f))); public static final Block SUSPICIOUS_SAND = register(new Block("suspicious_sand", builder().setBlockEntity(BlockEntityType.BRUSHABLE_BLOCK).destroyTime(0.25f).pushReaction(PistonBehavior.DESTROY) - .intState(DUSTED))); + .intState(DUSTED))); public static final Block RED_SAND = register(new Block("red_sand", builder().destroyTime(0.5f))); public static final Block GRAVEL = register(new Block("gravel", builder().destroyTime(0.6f))); public static final Block SUSPICIOUS_GRAVEL = register(new Block("suspicious_gravel", builder().setBlockEntity(BlockEntityType.BRUSHABLE_BLOCK).destroyTime(0.25f).pushReaction(PistonBehavior.DESTROY) - .intState(DUSTED))); + .intState(DUSTED))); public static final Block GOLD_ORE = register(new Block("gold_ore", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block DEEPSLATE_GOLD_ORE = register(new Block("deepslate_gold_ore", builder().requiresCorrectToolForDrops().destroyTime(4.5f))); public static final Block IRON_ORE = register(new Block("iron_ore", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); @@ -250,8 +250,8 @@ public final class Blocks { public static final Block DEEPSLATE_LAPIS_ORE = register(new Block("deepslate_lapis_ore", builder().requiresCorrectToolForDrops().destroyTime(4.5f))); public static final Block LAPIS_BLOCK = register(new Block("lapis_block", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block DISPENSER = register(new Block("dispenser", builder().setBlockEntity(BlockEntityType.DISPENSER).requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(TRIGGERED))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(TRIGGERED))); public static final Block SANDSTONE = register(new Block("sandstone", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block CHISELED_SANDSTONE = register(new Block("chiseled_sandstone", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block CUT_SANDSTONE = register(new Block("cut_sandstone", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); @@ -260,69 +260,69 @@ public final class Blocks { .intState(NOTE) .booleanState(POWERED))); public static final Block WHITE_BED = register(new BedBlock("white_bed", 0, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block ORANGE_BED = register(new BedBlock("orange_bed", 1, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block MAGENTA_BED = register(new BedBlock("magenta_bed", 2, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block LIGHT_BLUE_BED = register(new BedBlock("light_blue_bed", 3, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block YELLOW_BED = register(new BedBlock("yellow_bed", 4, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block LIME_BED = register(new BedBlock("lime_bed", 5, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block PINK_BED = register(new BedBlock("pink_bed", 6, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block GRAY_BED = register(new BedBlock("gray_bed", 7, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block LIGHT_GRAY_BED = register(new BedBlock("light_gray_bed", 8, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block CYAN_BED = register(new BedBlock("cyan_bed", 9, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block PURPLE_BED = register(new BedBlock("purple_bed", 10, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block BLUE_BED = register(new BedBlock("blue_bed", 11, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block BROWN_BED = register(new BedBlock("brown_bed", 12, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block GREEN_BED = register(new BedBlock("green_bed", 13, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block RED_BED = register(new BedBlock("red_bed", 14, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block BLACK_BED = register(new BedBlock("black_bed", 15, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block POWERED_RAIL = register(new Block("powered_rail", builder().destroyTime(0.7f) .booleanState(POWERED) .enumState(RAIL_SHAPE_STRAIGHT) @@ -368,9 +368,10 @@ public final class Blocks { public static final Block RED_WOOL = register(new Block("red_wool", builder().destroyTime(0.8f))); public static final Block BLACK_WOOL = register(new Block("black_wool", builder().destroyTime(0.8f))); public static final Block MOVING_PISTON = register(new MovingPistonBlock("moving_piston", builder().setBlockEntity(BlockEntityType.PISTON).destroyTime(-1.0f).pushReaction(PistonBehavior.BLOCK) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .enumState(PISTON_TYPE))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .enumState(PISTON_TYPE))); public static final Block DANDELION = register(new Block("dandelion", builder().pushReaction(PistonBehavior.DESTROY))); + public static final Block GOLDEN_DANDELION = register(new Block("golden_dandelion", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block TORCHFLOWER = register(new Block("torchflower", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POPPY = register(new Block("poppy", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block BLUE_ORCHID = register(new Block("blue_orchid", builder().pushReaction(PistonBehavior.DESTROY))); @@ -393,73 +394,73 @@ public final class Blocks { .booleanState(UNSTABLE))); public static final Block BOOKSHELF = register(new Block("bookshelf", builder().destroyTime(1.5f))); public static final Block CHISELED_BOOKSHELF = register(new Block("chiseled_bookshelf", builder().setBlockEntity(BlockEntityType.CHISELED_BOOKSHELF).destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(SLOT_0_OCCUPIED) - .booleanState(SLOT_1_OCCUPIED) - .booleanState(SLOT_2_OCCUPIED) - .booleanState(SLOT_3_OCCUPIED) - .booleanState(SLOT_4_OCCUPIED) - .booleanState(SLOT_5_OCCUPIED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(SLOT_0_OCCUPIED) + .booleanState(SLOT_1_OCCUPIED) + .booleanState(SLOT_2_OCCUPIED) + .booleanState(SLOT_3_OCCUPIED) + .booleanState(SLOT_4_OCCUPIED) + .booleanState(SLOT_5_OCCUPIED))); public static final Block ACACIA_SHELF = register(new Block("acacia_shelf", builder().setBlockEntity(BlockEntityType.SHELF).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED) - .enumState(SIDE_CHAIN_PART) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED) + .enumState(SIDE_CHAIN_PART) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_SHELF = register(new Block("bamboo_shelf", builder().setBlockEntity(BlockEntityType.SHELF).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED) - .enumState(SIDE_CHAIN_PART) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED) + .enumState(SIDE_CHAIN_PART) + .booleanState(WATERLOGGED))); public static final Block BIRCH_SHELF = register(new Block("birch_shelf", builder().setBlockEntity(BlockEntityType.SHELF).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED) - .enumState(SIDE_CHAIN_PART) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED) + .enumState(SIDE_CHAIN_PART) + .booleanState(WATERLOGGED))); public static final Block CHERRY_SHELF = register(new Block("cherry_shelf", builder().setBlockEntity(BlockEntityType.SHELF).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED) - .enumState(SIDE_CHAIN_PART) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED) + .enumState(SIDE_CHAIN_PART) + .booleanState(WATERLOGGED))); public static final Block CRIMSON_SHELF = register(new Block("crimson_shelf", builder().setBlockEntity(BlockEntityType.SHELF).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED) - .enumState(SIDE_CHAIN_PART) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED) + .enumState(SIDE_CHAIN_PART) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_SHELF = register(new Block("dark_oak_shelf", builder().setBlockEntity(BlockEntityType.SHELF).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED) - .enumState(SIDE_CHAIN_PART) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED) + .enumState(SIDE_CHAIN_PART) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_SHELF = register(new Block("jungle_shelf", builder().setBlockEntity(BlockEntityType.SHELF).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED) - .enumState(SIDE_CHAIN_PART) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED) + .enumState(SIDE_CHAIN_PART) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_SHELF = register(new Block("mangrove_shelf", builder().setBlockEntity(BlockEntityType.SHELF).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED) - .enumState(SIDE_CHAIN_PART) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED) + .enumState(SIDE_CHAIN_PART) + .booleanState(WATERLOGGED))); public static final Block OAK_SHELF = register(new Block("oak_shelf", builder().setBlockEntity(BlockEntityType.SHELF).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED) - .enumState(SIDE_CHAIN_PART) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED) + .enumState(SIDE_CHAIN_PART) + .booleanState(WATERLOGGED))); public static final Block PALE_OAK_SHELF = register(new Block("pale_oak_shelf", builder().setBlockEntity(BlockEntityType.SHELF).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED) - .enumState(SIDE_CHAIN_PART) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED) + .enumState(SIDE_CHAIN_PART) + .booleanState(WATERLOGGED))); public static final Block SPRUCE_SHELF = register(new Block("spruce_shelf", builder().setBlockEntity(BlockEntityType.SHELF).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED) - .enumState(SIDE_CHAIN_PART) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED) + .enumState(SIDE_CHAIN_PART) + .booleanState(WATERLOGGED))); public static final Block WARPED_SHELF = register(new Block("warped_shelf", builder().setBlockEntity(BlockEntityType.SHELF).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED) - .enumState(SIDE_CHAIN_PART) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED) + .enumState(SIDE_CHAIN_PART) + .booleanState(WATERLOGGED))); public static final Block MOSSY_COBBLESTONE = register(new Block("mossy_cobblestone", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block OBSIDIAN = register(new Block("obsidian", builder().requiresCorrectToolForDrops().destroyTime(50.0f))); public static final Block TORCH = register(new Block("torch", builder().pushReaction(PistonBehavior.DESTROY))); @@ -475,18 +476,18 @@ public final class Blocks { public static final Block SOUL_FIRE = register(new Block("soul_fire", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block SPAWNER = register(new Block("spawner", builder().setBlockEntity(BlockEntityType.MOB_SPAWNER).requiresCorrectToolForDrops().destroyTime(5.0f))); public static final Block CREAKING_HEART = register(new Block("creaking_heart", builder().setBlockEntity(BlockEntityType.CREAKING_HEART).destroyTime(10.0f) - .enumState(AXIS, Axis.VALUES) - .enumState(CREAKING_HEART_STATE) - .booleanState(NATURAL))); + .enumState(AXIS, Axis.VALUES) + .enumState(CREAKING_HEART_STATE) + .booleanState(NATURAL))); public static final Block OAK_STAIRS = register(new Block("oak_stairs", builder().destroyTime(2.0f) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) .enumState(HALF) .enumState(STAIRS_SHAPE) .booleanState(WATERLOGGED))); public static final Block CHEST = register(new ChestBlock("chest", builder().setBlockEntity(BlockEntityType.CHEST).destroyTime(2.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(CHEST_TYPE, ChestType.VALUES) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(CHEST_TYPE, ChestType.VALUES) + .booleanState(WATERLOGGED))); public static final Block REDSTONE_WIRE = register(new Block("redstone_wire", builder().pushReaction(PistonBehavior.DESTROY) .enumState(EAST_REDSTONE) .enumState(NORTH_REDSTONE) @@ -502,38 +503,38 @@ public final class Blocks { public static final Block FARMLAND = register(new Block("farmland", builder().destroyTime(0.6f) .intState(MOISTURE))); public static final Block FURNACE = register(new FurnaceBlock("furnace", builder().setBlockEntity(BlockEntityType.FURNACE).requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(LIT))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(LIT))); public static final Block OAK_SIGN = register(new Block("oak_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block SPRUCE_SIGN = register(new Block("spruce_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block BIRCH_SIGN = register(new Block("birch_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block ACACIA_SIGN = register(new Block("acacia_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block CHERRY_SIGN = register(new Block("cherry_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_SIGN = register(new Block("jungle_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_SIGN = register(new Block("dark_oak_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block PALE_OAK_SIGN = register(new Block("pale_oak_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_SIGN = register(new Block("mangrove_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_SIGN = register(new Block("bamboo_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block OAK_DOOR = register(new DoorBlock("oak_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) .enumState(DOUBLE_BLOCK_HALF) @@ -552,119 +553,119 @@ public final class Blocks { .enumState(STAIRS_SHAPE) .booleanState(WATERLOGGED))); public static final Block OAK_WALL_SIGN = register(new Block("oak_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block SPRUCE_WALL_SIGN = register(new Block("spruce_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block BIRCH_WALL_SIGN = register(new Block("birch_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block ACACIA_WALL_SIGN = register(new Block("acacia_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block CHERRY_WALL_SIGN = register(new Block("cherry_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_WALL_SIGN = register(new Block("jungle_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_WALL_SIGN = register(new Block("dark_oak_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block PALE_OAK_WALL_SIGN = register(new Block("pale_oak_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_WALL_SIGN = register(new Block("mangrove_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_WALL_SIGN = register(new Block("bamboo_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block OAK_HANGING_SIGN = register(new Block("oak_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block SPRUCE_HANGING_SIGN = register(new Block("spruce_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block BIRCH_HANGING_SIGN = register(new Block("birch_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block ACACIA_HANGING_SIGN = register(new Block("acacia_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block CHERRY_HANGING_SIGN = register(new Block("cherry_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_HANGING_SIGN = register(new Block("jungle_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_HANGING_SIGN = register(new Block("dark_oak_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block PALE_OAK_HANGING_SIGN = register(new Block("pale_oak_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block CRIMSON_HANGING_SIGN = register(new Block("crimson_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block WARPED_HANGING_SIGN = register(new Block("warped_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_HANGING_SIGN = register(new Block("mangrove_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_HANGING_SIGN = register(new Block("bamboo_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block OAK_WALL_HANGING_SIGN = register(new Block("oak_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block SPRUCE_WALL_HANGING_SIGN = register(new Block("spruce_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block BIRCH_WALL_HANGING_SIGN = register(new Block("birch_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block ACACIA_WALL_HANGING_SIGN = register(new Block("acacia_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block CHERRY_WALL_HANGING_SIGN = register(new Block("cherry_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_WALL_HANGING_SIGN = register(new Block("jungle_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_WALL_HANGING_SIGN = register(new Block("dark_oak_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block PALE_OAK_WALL_HANGING_SIGN = register(new Block("pale_oak_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_WALL_HANGING_SIGN = register(new Block("mangrove_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block CRIMSON_WALL_HANGING_SIGN = register(new Block("crimson_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block WARPED_WALL_HANGING_SIGN = register(new Block("warped_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_WALL_HANGING_SIGN = register(new Block("bamboo_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block LEVER = register(new Block("lever", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) .enumState(ATTACH_FACE) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) @@ -721,7 +722,7 @@ public final class Blocks { public static final Block SUGAR_CANE = register(new Block("sugar_cane", builder().pushReaction(PistonBehavior.DESTROY) .intState(AGE_15))); public static final Block JUKEBOX = register(new Block("jukebox", builder().setBlockEntity(BlockEntityType.JUKEBOX).destroyTime(2.0f) - .booleanState(HAS_RECORD))); + .booleanState(HAS_RECORD))); public static final Block OAK_FENCE = register(new Block("oak_fence", builder().destroyTime(2.0f) .booleanState(EAST) .booleanState(NORTH) @@ -1040,9 +1041,9 @@ public final class Blocks { .intState(AGE_3))); public static final Block ENCHANTING_TABLE = register(new Block("enchanting_table", builder().setBlockEntity(BlockEntityType.ENCHANTING_TABLE).requiresCorrectToolForDrops().destroyTime(5.0f))); public static final Block BREWING_STAND = register(new Block("brewing_stand", builder().setBlockEntity(BlockEntityType.BREWING_STAND).destroyTime(0.5f) - .booleanState(HAS_BOTTLE_0) - .booleanState(HAS_BOTTLE_1) - .booleanState(HAS_BOTTLE_2))); + .booleanState(HAS_BOTTLE_0) + .booleanState(HAS_BOTTLE_1) + .booleanState(HAS_BOTTLE_2))); public static final Block CAULDRON = register(new CauldronBlock("cauldron", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block WATER_CAULDRON = register(new CauldronBlock("water_cauldron", builder().requiresCorrectToolForDrops().destroyTime(2.0f) .intState(LEVEL_CAULDRON))); @@ -1068,8 +1069,8 @@ public final class Blocks { public static final Block EMERALD_ORE = register(new Block("emerald_ore", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block DEEPSLATE_EMERALD_ORE = register(new Block("deepslate_emerald_ore", builder().requiresCorrectToolForDrops().destroyTime(4.5f))); public static final Block ENDER_CHEST = register(new Block("ender_chest", builder().setBlockEntity(BlockEntityType.ENDER_CHEST).destroyTime(22.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block TRIPWIRE_HOOK = register(new Block("tripwire_hook", builder().pushReaction(PistonBehavior.DESTROY) .booleanState(ATTACHED) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) @@ -1099,8 +1100,8 @@ public final class Blocks { .enumState(STAIRS_SHAPE) .booleanState(WATERLOGGED))); public static final Block COMMAND_BLOCK = register(new Block("command_block", builder().setBlockEntity(BlockEntityType.COMMAND_BLOCK).requiresCorrectToolForDrops().destroyTime(-1.0f) - .booleanState(CONDITIONAL) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .booleanState(CONDITIONAL) + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block BEACON = register(new Block("beacon", builder().setBlockEntity(BlockEntityType.BEACON).destroyTime(3.0f))); public static final Block COBBLESTONE_WALL = register(new Block("cobblestone_wall", builder().requiresCorrectToolForDrops().destroyTime(2.0f) .enumState(EAST_WALL) @@ -1129,6 +1130,7 @@ public final class Blocks { public static final Block POTTED_MANGROVE_PROPAGULE = register(new FlowerPotBlock("potted_mangrove_propagule", MANGROVE_PROPAGULE, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_FERN = register(new FlowerPotBlock("potted_fern", FERN, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_DANDELION = register(new FlowerPotBlock("potted_dandelion", DANDELION, builder().pushReaction(PistonBehavior.DESTROY))); + public static final Block POTTED_GOLDEN_DANDELION = register(new FlowerPotBlock("potted_golden_dandelion", GOLDEN_DANDELION, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_POPPY = register(new FlowerPotBlock("potted_poppy", POPPY, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_BLUE_ORCHID = register(new FlowerPotBlock("potted_blue_orchid", BLUE_ORCHID, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_ALLIUM = register(new FlowerPotBlock("potted_allium", ALLIUM, builder().pushReaction(PistonBehavior.DESTROY))); @@ -1190,47 +1192,47 @@ public final class Blocks { .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) .booleanState(POWERED))); public static final Block SKELETON_SKULL = register(new SkullBlock("skeleton_skull", SkullBlock.Type.SKELETON, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block SKELETON_WALL_SKULL = register(new WallSkullBlock("skeleton_wall_skull", SkullBlock.Type.SKELETON, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block WITHER_SKELETON_SKULL = register(new SkullBlock("wither_skeleton_skull", SkullBlock.Type.WITHER_SKELETON, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block WITHER_SKELETON_WALL_SKULL = register(new WallSkullBlock("wither_skeleton_wall_skull", SkullBlock.Type.WITHER_SKELETON, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block ZOMBIE_HEAD = register(new SkullBlock("zombie_head", SkullBlock.Type.ZOMBIE, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block ZOMBIE_WALL_HEAD = register(new WallSkullBlock("zombie_wall_head", SkullBlock.Type.ZOMBIE, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block PLAYER_HEAD = register(new SkullBlock("player_head", SkullBlock.Type.PLAYER, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block PLAYER_WALL_HEAD = register(new WallSkullBlock("player_wall_head", SkullBlock.Type.PLAYER, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block CREEPER_HEAD = register(new SkullBlock("creeper_head", SkullBlock.Type.CREEPER, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block CREEPER_WALL_HEAD = register(new WallSkullBlock("creeper_wall_head", SkullBlock.Type.CREEPER, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block DRAGON_HEAD = register(new SkullBlock("dragon_head", SkullBlock.Type.DRAGON, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block DRAGON_WALL_HEAD = register(new WallSkullBlock("dragon_wall_head", SkullBlock.Type.DRAGON, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block PIGLIN_HEAD = register(new SkullBlock("piglin_head", SkullBlock.Type.PIGLIN, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block PIGLIN_WALL_HEAD = register(new WallSkullBlock("piglin_wall_head", SkullBlock.Type.PIGLIN, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block ANVIL = register(new Block("anvil", builder().requiresCorrectToolForDrops().destroyTime(5.0f).pushReaction(PistonBehavior.BLOCK) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block CHIPPED_ANVIL = register(new Block("chipped_anvil", builder().requiresCorrectToolForDrops().destroyTime(5.0f).pushReaction(PistonBehavior.BLOCK) @@ -1238,25 +1240,25 @@ public final class Blocks { public static final Block DAMAGED_ANVIL = register(new Block("damaged_anvil", builder().requiresCorrectToolForDrops().destroyTime(5.0f).pushReaction(PistonBehavior.BLOCK) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block TRAPPED_CHEST = register(new ChestBlock("trapped_chest", builder().setBlockEntity(BlockEntityType.TRAPPED_CHEST).destroyTime(2.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(CHEST_TYPE, ChestType.VALUES) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(CHEST_TYPE, ChestType.VALUES) + .booleanState(WATERLOGGED))); public static final Block LIGHT_WEIGHTED_PRESSURE_PLATE = register(new Block("light_weighted_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) .intState(POWER))); public static final Block HEAVY_WEIGHTED_PRESSURE_PLATE = register(new Block("heavy_weighted_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) .intState(POWER))); public static final Block COMPARATOR = register(new Block("comparator", builder().setBlockEntity(BlockEntityType.COMPARATOR).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(MODE_COMPARATOR) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(MODE_COMPARATOR) + .booleanState(POWERED))); public static final Block DAYLIGHT_DETECTOR = register(new Block("daylight_detector", builder().setBlockEntity(BlockEntityType.DAYLIGHT_DETECTOR).destroyTime(0.2f) - .booleanState(INVERTED) - .intState(POWER))); + .booleanState(INVERTED) + .intState(POWER))); public static final Block REDSTONE_BLOCK = register(new Block("redstone_block", builder().requiresCorrectToolForDrops().destroyTime(5.0f))); public static final Block NETHER_QUARTZ_ORE = register(new Block("nether_quartz_ore", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block HOPPER = register(new Block("hopper", builder().setBlockEntity(BlockEntityType.HOPPER).requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(ENABLED) - .enumState(FACING_HOPPER, Direction.DOWN, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .booleanState(ENABLED) + .enumState(FACING_HOPPER, Direction.DOWN, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block QUARTZ_BLOCK = register(new Block("quartz_block", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block CHISELED_QUARTZ_BLOCK = register(new Block("chiseled_quartz_block", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block QUARTZ_PILLAR = register(new Block("quartz_pillar", builder().requiresCorrectToolForDrops().destroyTime(0.8f) @@ -1271,8 +1273,8 @@ public final class Blocks { .enumState(RAIL_SHAPE_STRAIGHT) .booleanState(WATERLOGGED))); public static final Block DROPPER = register(new Block("dropper", builder().setBlockEntity(BlockEntityType.DROPPER).requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(TRIGGERED))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(TRIGGERED))); public static final Block WHITE_TERRACOTTA = register(new Block("white_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.25f))); public static final Block ORANGE_TERRACOTTA = register(new Block("orange_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.25f))); public static final Block MAGENTA_TERRACOTTA = register(new Block("magenta_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.25f))); @@ -1494,69 +1496,69 @@ public final class Blocks { public static final Block LARGE_FERN = register(new Block("large_fern", builder().pushReaction(PistonBehavior.DESTROY) .enumState(DOUBLE_BLOCK_HALF))); public static final Block WHITE_BANNER = register(new BannerBlock("white_banner", 0, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block ORANGE_BANNER = register(new BannerBlock("orange_banner", 1, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block MAGENTA_BANNER = register(new BannerBlock("magenta_banner", 2, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block LIGHT_BLUE_BANNER = register(new BannerBlock("light_blue_banner", 3, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block YELLOW_BANNER = register(new BannerBlock("yellow_banner", 4, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block LIME_BANNER = register(new BannerBlock("lime_banner", 5, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block PINK_BANNER = register(new BannerBlock("pink_banner", 6, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block GRAY_BANNER = register(new BannerBlock("gray_banner", 7, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block LIGHT_GRAY_BANNER = register(new BannerBlock("light_gray_banner", 8, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block CYAN_BANNER = register(new BannerBlock("cyan_banner", 9, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block PURPLE_BANNER = register(new BannerBlock("purple_banner", 10, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block BLUE_BANNER = register(new BannerBlock("blue_banner", 11, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block BROWN_BANNER = register(new BannerBlock("brown_banner", 12, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block GREEN_BANNER = register(new BannerBlock("green_banner", 13, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block RED_BANNER = register(new BannerBlock("red_banner", 14, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block BLACK_BANNER = register(new BannerBlock("black_banner", 15, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block WHITE_WALL_BANNER = register(new BannerBlock("white_wall_banner", 0, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block ORANGE_WALL_BANNER = register(new BannerBlock("orange_wall_banner", 1, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block MAGENTA_WALL_BANNER = register(new BannerBlock("magenta_wall_banner", 2, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block LIGHT_BLUE_WALL_BANNER = register(new BannerBlock("light_blue_wall_banner", 3, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block YELLOW_WALL_BANNER = register(new BannerBlock("yellow_wall_banner", 4, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block LIME_WALL_BANNER = register(new BannerBlock("lime_wall_banner", 5, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block PINK_WALL_BANNER = register(new BannerBlock("pink_wall_banner", 6, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block GRAY_WALL_BANNER = register(new BannerBlock("gray_wall_banner", 7, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block LIGHT_GRAY_WALL_BANNER = register(new BannerBlock("light_gray_wall_banner", 8, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block CYAN_WALL_BANNER = register(new BannerBlock("cyan_wall_banner", 9, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block PURPLE_WALL_BANNER = register(new BannerBlock("purple_wall_banner", 10, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BLUE_WALL_BANNER = register(new BannerBlock("blue_wall_banner", 11, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BROWN_WALL_BANNER = register(new BannerBlock("brown_wall_banner", 12, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block GREEN_WALL_BANNER = register(new BannerBlock("green_wall_banner", 13, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block RED_WALL_BANNER = register(new BannerBlock("red_wall_banner", 14, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BLACK_WALL_BANNER = register(new BannerBlock("black_wall_banner", 15, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block RED_SANDSTONE = register(new Block("red_sandstone", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block CHISELED_RED_SANDSTONE = register(new Block("chiseled_red_sandstone", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block CUT_RED_SANDSTONE = register(new Block("cut_red_sandstone", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); @@ -1829,11 +1831,11 @@ public final class Blocks { public static final Block DIRT_PATH = register(new Block("dirt_path", builder().destroyTime(0.65f))); public static final Block END_GATEWAY = register(new Block("end_gateway", builder().setBlockEntity(BlockEntityType.END_GATEWAY).destroyTime(-1.0f).pushReaction(PistonBehavior.BLOCK))); public static final Block REPEATING_COMMAND_BLOCK = register(new Block("repeating_command_block", builder().setBlockEntity(BlockEntityType.COMMAND_BLOCK).requiresCorrectToolForDrops().destroyTime(-1.0f) - .booleanState(CONDITIONAL) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .booleanState(CONDITIONAL) + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block CHAIN_COMMAND_BLOCK = register(new Block("chain_command_block", builder().setBlockEntity(BlockEntityType.COMMAND_BLOCK).requiresCorrectToolForDrops().destroyTime(-1.0f) - .booleanState(CONDITIONAL) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .booleanState(CONDITIONAL) + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block FROSTED_ICE = register(new Block("frosted_ice", builder().destroyTime(0.5f) .intState(AGE_3))); public static final Block MAGMA_BLOCK = register(new Block("magma_block", builder().requiresCorrectToolForDrops().destroyTime(0.5f))); @@ -1846,39 +1848,39 @@ public final class Blocks { .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) .booleanState(POWERED))); public static final Block SHULKER_BOX = register(new Block("shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block WHITE_SHULKER_BOX = register(new Block("white_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block ORANGE_SHULKER_BOX = register(new Block("orange_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block MAGENTA_SHULKER_BOX = register(new Block("magenta_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block LIGHT_BLUE_SHULKER_BOX = register(new Block("light_blue_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block YELLOW_SHULKER_BOX = register(new Block("yellow_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block LIME_SHULKER_BOX = register(new Block("lime_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block PINK_SHULKER_BOX = register(new Block("pink_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block GRAY_SHULKER_BOX = register(new Block("gray_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block LIGHT_GRAY_SHULKER_BOX = register(new Block("light_gray_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block CYAN_SHULKER_BOX = register(new Block("cyan_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block PURPLE_SHULKER_BOX = register(new Block("purple_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block BLUE_SHULKER_BOX = register(new Block("blue_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block BROWN_SHULKER_BOX = register(new Block("brown_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block GREEN_SHULKER_BOX = register(new Block("green_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block RED_SHULKER_BOX = register(new Block("red_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block BLACK_SHULKER_BOX = register(new Block("black_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block WHITE_GLAZED_TERRACOTTA = register(new Block("white_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block ORANGE_GLAZED_TERRACOTTA = register(new Block("orange_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) @@ -2041,7 +2043,7 @@ public final class Blocks { .booleanState(WATERLOGGED))); public static final Block BLUE_ICE = register(new Block("blue_ice", builder().destroyTime(2.8f))); public static final Block CONDUIT = register(new Block("conduit", builder().setBlockEntity(BlockEntityType.CONDUIT).destroyTime(3.0f) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block BAMBOO_SAPLING = register(new Block("bamboo_sapling", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY))); public static final Block BAMBOO = register(new Block("bamboo", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .intState(AGE_1) @@ -2259,30 +2261,30 @@ public final class Blocks { public static final Block LOOM = register(new Block("loom", builder().destroyTime(2.5f) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BARREL = register(new Block("barrel", builder().setBlockEntity(BlockEntityType.BARREL).destroyTime(2.5f) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(OPEN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(OPEN))); public static final Block SMOKER = register(new Block("smoker", builder().setBlockEntity(BlockEntityType.SMOKER).requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(LIT))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(LIT))); public static final Block BLAST_FURNACE = register(new Block("blast_furnace", builder().setBlockEntity(BlockEntityType.BLAST_FURNACE).requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(LIT))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(LIT))); public static final Block CARTOGRAPHY_TABLE = register(new Block("cartography_table", builder().destroyTime(2.5f))); public static final Block FLETCHING_TABLE = register(new Block("fletching_table", builder().destroyTime(2.5f))); public static final Block GRINDSTONE = register(new Block("grindstone", builder().requiresCorrectToolForDrops().destroyTime(2.0f).pushReaction(PistonBehavior.BLOCK) .enumState(ATTACH_FACE) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block LECTERN = register(new LecternBlock("lectern", builder().setBlockEntity(BlockEntityType.LECTERN).destroyTime(2.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(HAS_BOOK) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(HAS_BOOK) + .booleanState(POWERED))); public static final Block SMITHING_TABLE = register(new Block("smithing_table", builder().destroyTime(2.5f))); public static final Block STONECUTTER = register(new Block("stonecutter", builder().requiresCorrectToolForDrops().destroyTime(3.5f) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BELL = register(new Block("bell", builder().setBlockEntity(BlockEntityType.BELL).destroyTime(5.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(BELL_ATTACHMENT) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(BELL_ATTACHMENT) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block LANTERN = register(new Block("lantern", builder().destroyTime(3.5f).pushReaction(PistonBehavior.DESTROY) .booleanState(HANGING) .booleanState(WATERLOGGED))); @@ -2314,15 +2316,15 @@ public final class Blocks { .booleanState(HANGING) .booleanState(WATERLOGGED))); public static final Block CAMPFIRE = register(new Block("campfire", builder().setBlockEntity(BlockEntityType.CAMPFIRE).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(LIT) - .booleanState(SIGNAL_FIRE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(LIT) + .booleanState(SIGNAL_FIRE) + .booleanState(WATERLOGGED))); public static final Block SOUL_CAMPFIRE = register(new Block("soul_campfire", builder().setBlockEntity(BlockEntityType.CAMPFIRE).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(LIT) - .booleanState(SIGNAL_FIRE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(LIT) + .booleanState(SIGNAL_FIRE) + .booleanState(WATERLOGGED))); public static final Block SWEET_BERRY_BUSH = register(new Block("sweet_berry_bush", builder().pushReaction(PistonBehavior.DESTROY) .intState(AGE_3))); public static final Block WARPED_STEM = register(new Block("warped_stem", builder().destroyTime(2.0f) @@ -2433,34 +2435,34 @@ public final class Blocks { .booleanState(OPEN) .booleanState(POWERED))); public static final Block CRIMSON_SIGN = register(new Block("crimson_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block WARPED_SIGN = register(new Block("warped_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block CRIMSON_WALL_SIGN = register(new Block("crimson_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block WARPED_WALL_SIGN = register(new Block("warped_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block STRUCTURE_BLOCK = register(new Block("structure_block", builder().setBlockEntity(BlockEntityType.STRUCTURE_BLOCK).requiresCorrectToolForDrops().destroyTime(-1.0f) - .enumState(STRUCTUREBLOCK_MODE))); + .enumState(STRUCTUREBLOCK_MODE))); public static final Block JIGSAW = register(new Block("jigsaw", builder().setBlockEntity(BlockEntityType.JIGSAW).requiresCorrectToolForDrops().destroyTime(-1.0f) - .enumState(ORIENTATION, FrontAndTop.VALUES))); + .enumState(ORIENTATION, FrontAndTop.VALUES))); public static final Block TEST_BLOCK = register(new Block("test_block", builder().setBlockEntity(BlockEntityType.TEST_BLOCK).destroyTime(-1.0f) - .enumState(TEST_BLOCK_MODE))); + .enumState(TEST_BLOCK_MODE))); public static final Block TEST_INSTANCE_BLOCK = register(new Block("test_instance_block", builder().setBlockEntity(BlockEntityType.TEST_INSTANCE_BLOCK).destroyTime(-1.0f))); public static final Block COMPOSTER = register(new Block("composter", builder().destroyTime(0.6f) .intState(LEVEL_COMPOSTER))); public static final Block TARGET = register(new Block("target", builder().destroyTime(0.5f) .intState(POWER))); public static final Block BEE_NEST = register(new Block("bee_nest", builder().setBlockEntity(BlockEntityType.BEEHIVE).destroyTime(0.3f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .intState(LEVEL_HONEY))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .intState(LEVEL_HONEY))); public static final Block BEEHIVE = register(new Block("beehive", builder().setBlockEntity(BlockEntityType.BEEHIVE).destroyTime(0.6f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .intState(LEVEL_HONEY))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .intState(LEVEL_HONEY))); public static final Block HONEY_BLOCK = register(new Block("honey_block", builder())); public static final Block HONEYCOMB_BLOCK = register(new Block("honeycomb_block", builder().destroyTime(0.6f))); public static final Block NETHERITE_BLOCK = register(new Block("netherite_block", builder().requiresCorrectToolForDrops().destroyTime(50.0f))); @@ -2703,14 +2705,14 @@ public final class Blocks { public static final Block TINTED_GLASS = register(new Block("tinted_glass", builder().destroyTime(0.3f))); public static final Block POWDER_SNOW = register(new Block("powder_snow", builder().destroyTime(0.25f))); public static final Block SCULK_SENSOR = register(new Block("sculk_sensor", builder().setBlockEntity(BlockEntityType.SCULK_SENSOR).destroyTime(1.5f) - .intState(POWER) - .enumState(SCULK_SENSOR_PHASE) - .booleanState(WATERLOGGED))); + .intState(POWER) + .enumState(SCULK_SENSOR_PHASE) + .booleanState(WATERLOGGED))); public static final Block CALIBRATED_SCULK_SENSOR = register(new Block("calibrated_sculk_sensor", builder().setBlockEntity(BlockEntityType.CALIBRATED_SCULK_SENSOR).destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .intState(POWER) - .enumState(SCULK_SENSOR_PHASE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .intState(POWER) + .enumState(SCULK_SENSOR_PHASE) + .booleanState(WATERLOGGED))); public static final Block SCULK = register(new Block("sculk", builder().destroyTime(0.2f))); public static final Block SCULK_VEIN = register(new Block("sculk_vein", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) .booleanState(DOWN) @@ -2721,11 +2723,11 @@ public final class Blocks { .booleanState(WATERLOGGED) .booleanState(WEST))); public static final Block SCULK_CATALYST = register(new Block("sculk_catalyst", builder().setBlockEntity(BlockEntityType.SCULK_CATALYST).destroyTime(3.0f) - .booleanState(BLOOM))); + .booleanState(BLOOM))); public static final Block SCULK_SHRIEKER = register(new Block("sculk_shrieker", builder().setBlockEntity(BlockEntityType.SCULK_SHRIEKER).destroyTime(3.0f) - .booleanState(CAN_SUMMON) - .booleanState(SHRIEKING) - .booleanState(WATERLOGGED))); + .booleanState(CAN_SUMMON) + .booleanState(SHRIEKING) + .booleanState(WATERLOGGED))); public static final Block COPPER_BLOCK = register(new Block("copper_block", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block EXPOSED_COPPER = register(new Block("exposed_copper", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block WEATHERED_COPPER = register(new Block("weathered_copper", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); @@ -2953,69 +2955,69 @@ public final class Blocks { .booleanState(LIT) .booleanState(POWERED))); public static final Block COPPER_CHEST = register(new ChestBlock("copper_chest", builder().setBlockEntity(BlockEntityType.CHEST).requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(CHEST_TYPE, ChestType.VALUES) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(CHEST_TYPE, ChestType.VALUES) + .booleanState(WATERLOGGED))); public static final Block EXPOSED_COPPER_CHEST = register(new ChestBlock("exposed_copper_chest", builder().setBlockEntity(BlockEntityType.CHEST).requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(CHEST_TYPE, ChestType.VALUES) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(CHEST_TYPE, ChestType.VALUES) + .booleanState(WATERLOGGED))); public static final Block WEATHERED_COPPER_CHEST = register(new ChestBlock("weathered_copper_chest", builder().setBlockEntity(BlockEntityType.CHEST).requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(CHEST_TYPE, ChestType.VALUES) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(CHEST_TYPE, ChestType.VALUES) + .booleanState(WATERLOGGED))); public static final Block OXIDIZED_COPPER_CHEST = register(new ChestBlock("oxidized_copper_chest", builder().setBlockEntity(BlockEntityType.CHEST).requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(CHEST_TYPE, ChestType.VALUES) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(CHEST_TYPE, ChestType.VALUES) + .booleanState(WATERLOGGED))); public static final Block WAXED_COPPER_CHEST = register(new ChestBlock("waxed_copper_chest", builder().setBlockEntity(BlockEntityType.CHEST).requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(CHEST_TYPE, ChestType.VALUES) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(CHEST_TYPE, ChestType.VALUES) + .booleanState(WATERLOGGED))); public static final Block WAXED_EXPOSED_COPPER_CHEST = register(new ChestBlock("waxed_exposed_copper_chest", builder().setBlockEntity(BlockEntityType.CHEST).requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(CHEST_TYPE, ChestType.VALUES) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(CHEST_TYPE, ChestType.VALUES) + .booleanState(WATERLOGGED))); public static final Block WAXED_WEATHERED_COPPER_CHEST = register(new ChestBlock("waxed_weathered_copper_chest", builder().setBlockEntity(BlockEntityType.CHEST).requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(CHEST_TYPE, ChestType.VALUES) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(CHEST_TYPE, ChestType.VALUES) + .booleanState(WATERLOGGED))); public static final Block WAXED_OXIDIZED_COPPER_CHEST = register(new ChestBlock("waxed_oxidized_copper_chest", builder().setBlockEntity(BlockEntityType.CHEST).requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(CHEST_TYPE, ChestType.VALUES) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(CHEST_TYPE, ChestType.VALUES) + .booleanState(WATERLOGGED))); public static final Block COPPER_GOLEM_STATUE = register(new Block("copper_golem_statue", builder().setBlockEntity(BlockEntityType.COPPER_GOLEM_STATUE).destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(COPPER_GOLEM_POSE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(COPPER_GOLEM_POSE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block EXPOSED_COPPER_GOLEM_STATUE = register(new Block("exposed_copper_golem_statue", builder().setBlockEntity(BlockEntityType.COPPER_GOLEM_STATUE).destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(COPPER_GOLEM_POSE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(COPPER_GOLEM_POSE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block WEATHERED_COPPER_GOLEM_STATUE = register(new Block("weathered_copper_golem_statue", builder().setBlockEntity(BlockEntityType.COPPER_GOLEM_STATUE).destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(COPPER_GOLEM_POSE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(COPPER_GOLEM_POSE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block OXIDIZED_COPPER_GOLEM_STATUE = register(new Block("oxidized_copper_golem_statue", builder().setBlockEntity(BlockEntityType.COPPER_GOLEM_STATUE).destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(COPPER_GOLEM_POSE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(COPPER_GOLEM_POSE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block WAXED_COPPER_GOLEM_STATUE = register(new Block("waxed_copper_golem_statue", builder().setBlockEntity(BlockEntityType.COPPER_GOLEM_STATUE).destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(COPPER_GOLEM_POSE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(COPPER_GOLEM_POSE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block WAXED_EXPOSED_COPPER_GOLEM_STATUE = register(new Block("waxed_exposed_copper_golem_statue", builder().setBlockEntity(BlockEntityType.COPPER_GOLEM_STATUE).destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(COPPER_GOLEM_POSE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(COPPER_GOLEM_POSE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block WAXED_WEATHERED_COPPER_GOLEM_STATUE = register(new Block("waxed_weathered_copper_golem_statue", builder().setBlockEntity(BlockEntityType.COPPER_GOLEM_STATUE).destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(COPPER_GOLEM_POSE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(COPPER_GOLEM_POSE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block WAXED_OXIDIZED_COPPER_GOLEM_STATUE = register(new Block("waxed_oxidized_copper_golem_statue", builder().setBlockEntity(BlockEntityType.COPPER_GOLEM_STATUE).destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(COPPER_GOLEM_POSE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(COPPER_GOLEM_POSE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block LIGHTNING_ROD = register(new Block("lightning_rod", builder().requiresCorrectToolForDrops().destroyTime(3.0f) .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) .booleanState(POWERED) @@ -3173,20 +3175,20 @@ public final class Blocks { public static final Block FROGSPAWN = register(new Block("frogspawn", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block REINFORCED_DEEPSLATE = register(new Block("reinforced_deepslate", builder().destroyTime(55.0f))); public static final Block DECORATED_POT = register(new Block("decorated_pot", builder().setBlockEntity(BlockEntityType.DECORATED_POT).pushReaction(PistonBehavior.DESTROY) - .booleanState(CRACKED) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .booleanState(CRACKED) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block CRAFTER = register(new Block("crafter", builder().setBlockEntity(BlockEntityType.CRAFTER).destroyTime(1.5f) - .booleanState(CRAFTING) - .enumState(ORIENTATION, FrontAndTop.VALUES) - .booleanState(TRIGGERED))); + .booleanState(CRAFTING) + .enumState(ORIENTATION, FrontAndTop.VALUES) + .booleanState(TRIGGERED))); public static final Block TRIAL_SPAWNER = register(new Block("trial_spawner", builder().setBlockEntity(BlockEntityType.TRIAL_SPAWNER).destroyTime(50.0f) - .booleanState(OMINOUS) - .enumState(TRIAL_SPAWNER_STATE))); + .booleanState(OMINOUS) + .enumState(TRIAL_SPAWNER_STATE))); public static final Block VAULT = register(new Block("vault", builder().setBlockEntity(BlockEntityType.VAULT).destroyTime(50.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OMINOUS) - .enumState(VAULT_STATE))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OMINOUS) + .enumState(VAULT_STATE))); public static final Block HEAVY_CORE = register(new Block("heavy_core", builder().destroyTime(10.0f) .booleanState(WATERLOGGED))); public static final Block PALE_MOSS_BLOCK = register(new Block("pale_moss_block", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY))); diff --git a/core/src/main/java/org/geysermc/geyser/level/block/GeyserCustomBlockData.java b/core/src/main/java/org/geysermc/geyser/level/block/GeyserCustomBlockData.java index d717e33c517..49b72cd69c8 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/GeyserCustomBlockData.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/GeyserCustomBlockData.java @@ -79,7 +79,7 @@ public class GeyserCustomBlockData implements CustomBlockData { if (property.values().isEmpty()) { throw new IllegalStateException(property.name() + " contains no values."); } - defaultProperties.put(property.name(), property.values().get(0)); + defaultProperties.put(property.name(), property.values().getFirst()); } this.defaultProperties = Object2ObjectMaps.unmodifiable(defaultProperties); } else { diff --git a/core/src/main/java/org/geysermc/geyser/level/block/property/Properties.java b/core/src/main/java/org/geysermc/geyser/level/block/property/Properties.java index df3a0c3cc06..0d94cb0b256 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/property/Properties.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/property/Properties.java @@ -126,7 +126,7 @@ public final class Properties { public static final EnumProperty CHEST_TYPE = EnumProperty.create("type", ChestType.VALUES); public static final BasicEnumProperty MODE_COMPARATOR = BasicEnumProperty.create("mode", "compare", "subtract"); public static final BasicEnumProperty DOOR_HINGE = BasicEnumProperty.create("hinge", "left", "right"); - public static final BasicEnumProperty NOTEBLOCK_INSTRUMENT = BasicEnumProperty.create("instrument", "harp", "basedrum", "snare", "hat", "bass", "flute", "bell", "guitar", "chime", "xylophone", "iron_xylophone", "cow_bell", "didgeridoo", "bit", "banjo", "pling", "zombie", "skeleton", "creeper", "dragon", "wither_skeleton", "piglin", "custom_head"); + public static final BasicEnumProperty NOTEBLOCK_INSTRUMENT = BasicEnumProperty.create("instrument", "harp", "basedrum", "snare", "hat", "bass", "flute", "bell", "guitar", "chime", "xylophone", "iron_xylophone", "cow_bell", "didgeridoo", "bit", "banjo", "pling", "trumpet", "trumpet_exposed", "trumpet_oxidized", "trumpet_weathered", "zombie", "skeleton", "creeper", "dragon", "wither_skeleton", "piglin", "custom_head"); public static final BasicEnumProperty PISTON_TYPE = BasicEnumProperty.create("type", "normal", "sticky"); public static final BasicEnumProperty SLAB_TYPE = BasicEnumProperty.create("type", "top", "bottom", "double"); public static final BasicEnumProperty STAIRS_SHAPE = BasicEnumProperty.create("shape", "straight", "inner_left", "inner_right", "outer_left", "outer_right"); diff --git a/core/src/main/java/org/geysermc/geyser/level/block/type/Block.java b/core/src/main/java/org/geysermc/geyser/level/block/type/Block.java index a619ec60e52..c3cf1998d2c 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/type/Block.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/type/Block.java @@ -81,7 +81,7 @@ public Block(@Subst("empty") String javaIdentifier, Builder builder) { this.destroyTime = builder.destroyTime; this.pushReaction = builder.pushReaction; - BlockState firstState = builder.build(this).get(0); + BlockState firstState = builder.build(this).getFirst(); this.propertyKeys = builder.propertyKeys; // Ensure this is not null before iterating over states this.defaultState = setDefaultState(firstState); } diff --git a/core/src/main/java/org/geysermc/geyser/level/gamerule/GameRule.java b/core/src/main/java/org/geysermc/geyser/level/gamerule/GameRule.java new file mode 100644 index 00000000000..5b7065917fc --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/level/gamerule/GameRule.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2019-2026 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.level.gamerule; + +import net.kyori.adventure.key.Key; +import org.geysermc.cumulus.component.Component; +import org.geysermc.cumulus.component.InputComponent; +import org.geysermc.cumulus.component.ToggleComponent; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.geyser.util.MinecraftKey; +import org.geysermc.geyser.util.TypeAdapter; + +/** + * GameRule types, used in the gamerule menu + */ +public interface GameRule { + + /** + * The id of the gamerule; without the minecraft namespace + */ + Key key(); + + /** + * The category of this gamerule + */ + GameRuleCategory category(); + + /** + * The {@link TypeAdapter} used for this gamerule + */ + TypeAdapter adapter(); + + /** + * default value for the gamerule + */ + T defaultValue(); + + /** + * ensures gamerule values are within range + */ + boolean validate(T value); + + Component toComponent(GeyserSession session, String currentValue); + + record Int(Key key, GameRuleCategory category, Integer defaultValue, int max, int min) implements GameRule { + public Int(String key, GameRuleCategory category, Integer defaultValue, int max, int min) { + this(MinecraftKey.key(key), category, defaultValue, max, min); + } + + @Override + public TypeAdapter adapter() { + return TypeAdapter.INTEGER; + } + + @Override + public boolean validate(Integer value) { + return value <= max && value >= min; + } + + @Override + public Component toComponent(GeyserSession session, String currentValue) { + return InputComponent.of(GameRule.translate(session, key), currentValue, currentValue); + } + } + + record Bool(Key key, GameRuleCategory category, Boolean defaultValue) implements GameRule { + public Bool(String key, GameRuleCategory category, Boolean defaultValue) { + this(MinecraftKey.key(key), category, defaultValue); + } + + @Override + public TypeAdapter adapter() { + return TypeAdapter.BOOLEAN; + } + + @Override + public boolean validate(Boolean value) { + return value != null; + } + + @Override + public Component toComponent(GeyserSession session, String currentValue) { + return ToggleComponent.of(GameRule.translate(session, key), adapter().parser().apply(currentValue)); + } + } + + private static String translate(GeyserSession session, Key key) { + String translatable = "gamerule." + key.namespace() + "." + key.value().replace('/', '.'); + return MessageTranslator.convertMessage(net.kyori.adventure.text.Component.translatable(translatable), session.locale()); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/level/gamerule/GameRuleCategory.java b/core/src/main/java/org/geysermc/geyser/level/gamerule/GameRuleCategory.java new file mode 100644 index 00000000000..db3695e0a56 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/level/gamerule/GameRuleCategory.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2026 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.level.gamerule; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextDecoration; +import org.geysermc.geyser.util.MinecraftKey; + +public enum GameRuleCategory { + PLAYER, + MOBS, + SPAWNING, + DROPS, + UPDATES, + CHAT, + MISC; + + public Key id() { + return MinecraftKey.key(name().toLowerCase()); + } + + public Component label() { + return Component.translatable("gamerule.category." + id().namespace() + "." + id().value()) + .color(NamedTextColor.YELLOW).style(Style.style(TextDecoration.BOLD)); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/level/gamerule/GameRuleHandler.java b/core/src/main/java/org/geysermc/geyser/level/gamerule/GameRuleHandler.java new file mode 100644 index 00000000000..02c016b74e5 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/level/gamerule/GameRuleHandler.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2026 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.level.gamerule; + +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; +import lombok.Getter; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import org.geysermc.cumulus.form.CustomForm; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.MinecraftLocale; +import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.ClientCommand; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundGameRuleValuesPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundSetGameRulePacket; + +import java.util.Comparator; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +public class GameRuleHandler { + + private static final Component EDIT_TITLE = Component.translatable("editGamerule.title"); + private final GeyserSession session; + @Getter + private State state = State.NOT_REQUESTED; + + public GameRuleHandler(GeyserSession session) { + this.session = session; + } + + public void requestGamerules() { + state = State.WAITING; + // Hacky, but otherwise, the waiting form shows up *after* the edit form if the server responds too quickly + session.scheduleInEventLoop(() -> { + if (state == State.WAITING) { + showWaitingForm(); + } + }, 500, TimeUnit.MILLISECONDS); + session.sendDownstreamGamePacket(new ServerboundClientCommandPacket(ClientCommand.REQUEST_GAMERULE_VALUES)); + } + + public void onGamerulesReceived(ClientboundGameRuleValuesPacket packet) { + if (state == State.WAITING) { + state = State.SHOWN; + + Map, String>> values = new Object2ObjectArrayMap<>(); + for (Map.Entry entry : packet.getValues().entrySet()) { + GameRule gameRule = Registries.GAME_RULES.get(entry.getKey()); + if (gameRule == null) { + GeyserImpl.getInstance().getLogger().debug("Unknown gamerule: " + entry.getKey()); + continue; + } + values.computeIfAbsent(gameRule.category(), (category) -> new Object2ObjectArrayMap<>()).put(gameRule, entry.getValue()); + } + + CustomForm.Builder builder = CustomForm.builder(); + builder.title(MessageTranslator.convertMessage(EDIT_TITLE, session.locale())); + + Map, String> ordered = new Object2ObjectArrayMap<>(packet.getValues().size()); + values.entrySet() + .stream() + .sorted(Map.Entry.comparingByKey(Comparator.comparing(GameRuleCategory::id))) + .forEach(entry -> { + builder.label(MessageTranslator.convertMessage(entry.getKey().label(), session.locale())); + + entry.getValue().entrySet() + .stream() + .sorted(Map.Entry.comparingByKey(Comparator.comparing(GameRule::key))) + .forEach(nested -> { + builder.component(nested.getKey().toComponent(session, nested.getValue())); + ordered.put(nested.getKey(), nested.getValue()); + }); + }); + + builder.validResultHandler((customForm, result) -> { + Map responses = new Object2ObjectArrayMap<>(); + for (var entry : ordered.entrySet()) { + handleUpdate(entry.getKey(), entry.getValue(), result.next(), responses); + } + if (session.getOpPermissionLevel() >= 2 && !responses.isEmpty()) { + session.sendDownstreamGamePacket(new ServerboundSetGameRulePacket(responses)); + } + state = State.NOT_REQUESTED; + }); + builder.closedOrInvalidResultHandler((customForm, result) -> state = State.NOT_REQUESTED); + + session.sendForm(builder); + } + } + + public void showWaitingForm() { + CustomForm waitingForm = CustomForm.builder() + .title("editGamerule.inGame.downloadingGamerules") + .translator(MinecraftLocale::getLocaleString, session.locale()) + .validResultHandler((customForm, result) -> { + // Resend waiting form only on submit; allow closing + showWaitingForm(); + }) + .closedOrInvalidResultHandler((customForm, result) -> { + if (state != State.SHOWN) { + this.state = State.NOT_REQUESTED; + } + }) + .build(); + session.sendForm(waitingForm); + } + + public void handleUpdate(GameRule gameRule, String previous, Object newValue, Map responses) { + T value; + try { + value = gameRule.adapter().parser().apply(newValue); + } catch (Throwable e) { + GeyserImpl.getInstance().getLogger().debug("Failed to parse value for gamerule %s (old value: %s, new value: %s)", gameRule.key(), previous, newValue); + return; + } + + if (!gameRule.validate(value)) { + GeyserImpl.getInstance().getLogger().debug("Got invalid value for gamerule %s (old value: %s, new value: %s)", gameRule.key(), previous, newValue); + return; + } + + T oldValue = gameRule.adapter().parser().apply(previous); + if (Objects.equals(oldValue, value)) { + return; + } + + responses.put(gameRule.key(), value.toString()); + } + + public enum State { + NOT_REQUESTED, + WAITING, + SHOWN + } +} diff --git a/core/src/main/java/org/geysermc/geyser/level/gamerule/GameRules.java b/core/src/main/java/org/geysermc/geyser/level/gamerule/GameRules.java new file mode 100644 index 00000000000..39adc8494b2 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/level/gamerule/GameRules.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2026 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.level.gamerule; + +import org.geysermc.geyser.registry.Registries; + +public class GameRules { + public static final GameRule ADVANCE_TIME = register(new GameRule.Bool("advance_time", GameRuleCategory.UPDATES, true)); + public static final GameRule ADVANCE_WEATHER = register(new GameRule.Bool("advance_weather", GameRuleCategory.UPDATES, true)); + public static final GameRule ALLOW_ENTERING_NETHER_USING_PORTALS = register(new GameRule.Bool("allow_entering_nether_using_portals", GameRuleCategory.MISC, true)); + public static final GameRule BLOCK_DROPS = register(new GameRule.Bool("block_drops", GameRuleCategory.DROPS, true)); + public static final GameRule BLOCK_EXPLOSION_DROP_DECAY = register(new GameRule.Bool("block_explosion_drop_decay", GameRuleCategory.DROPS, true)); + public static final GameRule COMMAND_BLOCKS_WORK = register(new GameRule.Bool("command_blocks_work", GameRuleCategory.MISC, true)); + public static final GameRule COMMAND_BLOCK_OUTPUT = register(new GameRule.Bool("command_block_output", GameRuleCategory.CHAT, true)); + public static final GameRule DROWNING_DAMAGE = register(new GameRule.Bool("drowning_damage", GameRuleCategory.PLAYER, true)); + public static final GameRule ELYTRA_MOVEMENT_CHECK = register(new GameRule.Bool("elytra_movement_check", GameRuleCategory.PLAYER, true)); + public static final GameRule ENDER_PEARLS_VANISH_ON_DEATH = register(new GameRule.Bool("ender_pearls_vanish_on_death", GameRuleCategory.PLAYER, true)); + public static final GameRule ENTITY_DROPS = register(new GameRule.Bool("entity_drops", GameRuleCategory.DROPS, true)); + public static final GameRule FALL_DAMAGE = register(new GameRule.Bool("fall_damage", GameRuleCategory.PLAYER, true)); + public static final GameRule FIRE_DAMAGE = register(new GameRule.Bool("fire_damage", GameRuleCategory.PLAYER, true)); + public static final GameRule FIRE_SPREAD_RADIUS_AROUND_PLAYER = register(new GameRule.Int("fire_spread_radius_around_player", GameRuleCategory.UPDATES, -1, Integer.MAX_VALUE, 128)); + public static final GameRule FORGIVE_DEAD_PLAYERS = register(new GameRule.Bool("forgive_dead_players", GameRuleCategory.MOBS, true)); + public static final GameRule FREEZE_DAMAGE = register(new GameRule.Bool("freeze_damage", GameRuleCategory.PLAYER, true)); + public static final GameRule GLOBAL_SOUND_EVENTS = register(new GameRule.Bool("global_sound_events", GameRuleCategory.MISC, true)); + public static final GameRule IMMEDIATE_RESPAWN = register(new GameRule.Bool("immediate_respawn", GameRuleCategory.PLAYER, false)); + public static final GameRule KEEP_INVENTORY = register(new GameRule.Bool("keep_inventory", GameRuleCategory.PLAYER, false)); + public static final GameRule LAVA_SOURCE_CONVERSION = register(new GameRule.Bool("lava_source_conversion", GameRuleCategory.UPDATES, false)); + public static final GameRule LIMITED_CRAFTING = register(new GameRule.Bool("limited_crafting", GameRuleCategory.PLAYER, false)); + public static final GameRule LOCATOR_BAR = register(new GameRule.Bool("locator_bar", GameRuleCategory.PLAYER, true)); + public static final GameRule LOG_ADMIN_COMMANDS = register(new GameRule.Bool("log_admin_commands", GameRuleCategory.CHAT, true)); + public static final GameRule MAX_BLOCK_MODIFICATIONS = register(new GameRule.Int("max_block_modifications", GameRuleCategory.MISC, 1, Integer.MAX_VALUE, 32768)); + public static final GameRule MAX_COMMAND_FORKS = register(new GameRule.Int("max_command_forks", GameRuleCategory.MISC, 0, Integer.MAX_VALUE, 65536)); + public static final GameRule MAX_COMMAND_SEQUENCE_LENGTH = register(new GameRule.Int("max_command_sequence_length", GameRuleCategory.MISC, 0, Integer.MAX_VALUE, 65536)); + public static final GameRule MAX_ENTITY_CRAMMING = register(new GameRule.Int("max_entity_cramming", GameRuleCategory.MOBS, 0, Integer.MAX_VALUE, 24)); + public static final GameRule MAX_MINECART_SPEED = register(new GameRule.Int("max_minecart_speed", GameRuleCategory.MISC, 1, 1000, 8)); + public static final GameRule MAX_SNOW_ACCUMULATION_HEIGHT = register(new GameRule.Int("max_snow_accumulation_height", GameRuleCategory.UPDATES, 0, 8, 1)); + public static final GameRule MOB_DROPS = register(new GameRule.Bool("mob_drops", GameRuleCategory.DROPS, true)); + public static final GameRule MOB_EXPLOSION_DROP_DECAY = register(new GameRule.Bool("mob_explosion_drop_decay", GameRuleCategory.DROPS, true)); + public static final GameRule MOB_GRIEFING = register(new GameRule.Bool("mob_griefing", GameRuleCategory.MOBS, true)); + public static final GameRule NATURAL_HEALTH_REGENERATION = register(new GameRule.Bool("natural_health_regeneration", GameRuleCategory.PLAYER, true)); + public static final GameRule PLAYER_MOVEMENT_CHECK = register(new GameRule.Bool("player_movement_check", GameRuleCategory.PLAYER, true)); + public static final GameRule PLAYERS_NETHER_PORTAL_CREATIVE_DELAY = register(new GameRule.Int("players_nether_portal_creative_delay", GameRuleCategory.PLAYER, 0, Integer.MAX_VALUE, 0)); + public static final GameRule PLAYERS_NETHER_PORTAL_DEFAULT_DELAY = register(new GameRule.Int("players_nether_portal_default_delay", GameRuleCategory.PLAYER, 0, Integer.MAX_VALUE, 80)); + public static final GameRule PLAYERS_SLEEPING_PERCENTAGE = register(new GameRule.Int("players_sleeping_percentage", GameRuleCategory.PLAYER, 0, Integer.MAX_VALUE, 100)); + public static final GameRule PROJECTILES_CAN_BREAK_BLOCKS = register(new GameRule.Bool("projectiles_can_break_blocks", GameRuleCategory.DROPS, true)); + public static final GameRule PVP = register(new GameRule.Bool("pvp", GameRuleCategory.PLAYER, true)); + public static final GameRule RAIDS = register(new GameRule.Bool("raids", GameRuleCategory.MOBS, true)); + public static final GameRule RANDOM_TICK_SPEED = register(new GameRule.Int("random_tick_speed", GameRuleCategory.UPDATES, 0, Integer.MAX_VALUE, 3)); + public static final GameRule REDUCED_DEBUG_INFO = register(new GameRule.Bool("reduced_debug_info", GameRuleCategory.MISC, false)); + public static final GameRule RESPAWN_RADIUS = register(new GameRule.Int("respawn_radius", GameRuleCategory.PLAYER, 0, Integer.MAX_VALUE, 10)); + public static final GameRule SEND_COMMAND_FEEDBACK = register(new GameRule.Bool("send_command_feedback", GameRuleCategory.CHAT, true)); + public static final GameRule SHOW_ADVANCEMENT_MESSAGES = register(new GameRule.Bool("show_advancement_messages", GameRuleCategory.CHAT, true)); + public static final GameRule SHOW_DEATH_MESSAGES = register(new GameRule.Bool("show_death_messages", GameRuleCategory.CHAT, true)); + public static final GameRule SPAWNER_BLOCKS_WORK = register(new GameRule.Bool("spawner_blocks_work", GameRuleCategory.MISC, true)); + public static final GameRule SPAWN_MOBS = register(new GameRule.Bool("spawn_mobs", GameRuleCategory.SPAWNING, true)); + public static final GameRule SPAWN_MONSTERS = register(new GameRule.Bool("spawn_monsters", GameRuleCategory.SPAWNING, true)); + public static final GameRule SPAWN_PATROLS = register(new GameRule.Bool("spawn_patrols", GameRuleCategory.SPAWNING, true)); + public static final GameRule SPAWN_PHANTOMS = register(new GameRule.Bool("spawn_phantoms", GameRuleCategory.SPAWNING, true)); + public static final GameRule SPAWN_WANDERING_TRADERS = register(new GameRule.Bool("spawn_wandering_traders", GameRuleCategory.SPAWNING, true)); + public static final GameRule SPAWN_WARDENS = register(new GameRule.Bool("spawn_wardens", GameRuleCategory.SPAWNING, true)); + public static final GameRule SPECTATORS_GENERATE_CHUNKS = register(new GameRule.Bool("spectators_generate_chunks", GameRuleCategory.PLAYER, true)); + public static final GameRule SPREAD_VINES = register(new GameRule.Bool("spread_vines", GameRuleCategory.UPDATES, true)); + public static final GameRule TNT_EXPLODES = register(new GameRule.Bool("tnt_explodes", GameRuleCategory.MISC, true)); + public static final GameRule TNT_EXPLOSION_DROP_DECAY = register(new GameRule.Bool("tnt_explosion_drop_decay", GameRuleCategory.DROPS, false)); + public static final GameRule UNIVERSAL_ANGER = register(new GameRule.Bool("universal_anger", GameRuleCategory.MOBS, false)); + public static final GameRule WATER_SOURCE_CONVERSION = register(new GameRule.Bool("water_source_conversion", GameRuleCategory.UPDATES, true)); + + public static GameRule register(GameRule gameRule) { + Registries.GAME_RULES.register(gameRule.key(), gameRule); + return gameRule; + } + + public static void init() { + // no-op + } +} diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index 5ec52015413..4778f66c02d 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -87,7 +87,7 @@ public final class GameProtocol { register(Bedrock_v944.CODEC, "26.10"); register(Bedrock_v975.CODEC, "26.20"); - MinecraftVersion latestBedrock = SUPPORTED_BEDROCK_VERSIONS.get(SUPPORTED_BEDROCK_VERSIONS.size() - 1); + MinecraftVersion latestBedrock = SUPPORTED_BEDROCK_VERSIONS.getLast(); DEFAULT_BEDROCK_VERSION = latestBedrock.versionString(); DEFAULT_BEDROCK_PROTOCOL = latestBedrock.protocolVersion(); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/Registries.java b/core/src/main/java/org/geysermc/geyser/registry/Registries.java index 5aed0d5a517..6f36251ba26 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -37,8 +37,9 @@ import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.item.type.Item; +import org.geysermc.geyser.level.gamerule.GameRule; +import org.geysermc.geyser.level.gamerule.GameRules; import org.geysermc.geyser.pack.ResourcePackHolder; import org.geysermc.geyser.registry.loader.BiomeIdentifierRegistryLoader; import org.geysermc.geyser.registry.loader.BlockEntityRegistryLoader; @@ -169,11 +170,6 @@ public final class Registries { */ public static final VersionedDeferredRegistry> POTION_MIXES = VersionedDeferredRegistry.create(VersionedRegistry::create, PotionMixRegistryLoader::new); - /** - * A versioned registry holding all the recipes, with the net ID being the key, and {@link GeyserRecipe} as the value. - */ - //public static final SimpleMappedDeferredRegistry> RECIPES = SimpleMappedDeferredRegistry.create("mappings/recipes.nbt", RecipeRegistryLoader::new); - /** * A mapped registry holding {@link ResourcePackHolder}'s with the pack uuid as keys. */ @@ -214,6 +210,12 @@ public final class Registries { */ public static final ListDeferredRegistry DANGEROUS_ENTITIES = ListDeferredRegistry.create(UtilMappings::dangerousEntities, RegistryLoaders.UTIL_MAPPINGS_KEYS); + /** + * A registry containing all the Java game rules. + * Loaded through {@link GameRules} + */ + public static final SimpleMappedRegistry> GAME_RULES = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new)); + public static void load() { if (loaded) return; loaded = true; @@ -229,7 +231,6 @@ public static void load() { BLOCK_ENTITIES.load(); PARTICLES.load(); // load potion mixes later - //RECIPES.load(); SOUNDS.load(); SOUND_LEVEL_EVENTS.load(); SOUND_TRANSLATORS.load(); @@ -237,6 +238,7 @@ public static void load() { GAME_MASTER_BLOCKS.load(); DANGEROUS_BLOCK_ENTITIES.load(); DANGEROUS_ENTITIES.load(); + GameRules.init(); } public static void populate() { diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java index 613df61aa51..911dadc1092 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java @@ -30,7 +30,6 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.PotionMixData; import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.item.Items; -import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; @@ -57,41 +56,41 @@ public Int2ObjectMap> load(Object input) { for (var entry : Registries.ITEMS.get().int2ObjectEntrySet()) { ItemMappings mappings = entry.getValue(); List ingredients = new ArrayList<>(); - ingredients.add(getNonNull(mappings, Items.NETHER_WART)); - ingredients.add(getNonNull(mappings, Items.REDSTONE)); - ingredients.add(getNonNull(mappings, Items.GLOWSTONE_DUST)); - ingredients.add(getNonNull(mappings, Items.FERMENTED_SPIDER_EYE)); - ingredients.add(getNonNull(mappings, Items.GUNPOWDER)); - ingredients.add(getNonNull(mappings, Items.DRAGON_BREATH)); - ingredients.add(getNonNull(mappings, Items.SUGAR)); - ingredients.add(getNonNull(mappings, Items.RABBIT_FOOT)); - ingredients.add(getNonNull(mappings, Items.GLISTERING_MELON_SLICE)); - ingredients.add(getNonNull(mappings, Items.SPIDER_EYE)); - ingredients.add(getNonNull(mappings, Items.PUFFERFISH)); - ingredients.add(getNonNull(mappings, Items.MAGMA_CREAM)); - ingredients.add(getNonNull(mappings, Items.GOLDEN_CARROT)); - ingredients.add(getNonNull(mappings, Items.BLAZE_POWDER)); - ingredients.add(getNonNull(mappings, Items.GHAST_TEAR)); - ingredients.add(getNonNull(mappings, Items.TURTLE_HELMET)); - ingredients.add(getNonNull(mappings, Items.PHANTOM_MEMBRANE)); + ingredients.add(mappings.getMapping(Items.NETHER_WART)); + ingredients.add(mappings.getMapping(Items.REDSTONE)); + ingredients.add(mappings.getMapping(Items.GLOWSTONE_DUST)); + ingredients.add(mappings.getMapping(Items.FERMENTED_SPIDER_EYE)); + ingredients.add(mappings.getMapping(Items.GUNPOWDER)); + ingredients.add(mappings.getMapping(Items.DRAGON_BREATH)); + ingredients.add(mappings.getMapping(Items.SUGAR)); + ingredients.add(mappings.getMapping(Items.RABBIT_FOOT)); + ingredients.add(mappings.getMapping(Items.GLISTERING_MELON_SLICE)); + ingredients.add(mappings.getMapping(Items.SPIDER_EYE)); + ingredients.add(mappings.getMapping(Items.PUFFERFISH)); + ingredients.add(mappings.getMapping(Items.MAGMA_CREAM)); + ingredients.add(mappings.getMapping(Items.GOLDEN_CARROT)); + ingredients.add(mappings.getMapping(Items.BLAZE_POWDER)); + ingredients.add(mappings.getMapping(Items.GHAST_TEAR)); + ingredients.add(mappings.getMapping(Items.TURTLE_HELMET)); + ingredients.add(mappings.getMapping(Items.PHANTOM_MEMBRANE)); // 1.21 - ingredients.add(getNonNull(mappings, Items.STONE)); - ingredients.add(getNonNull(mappings, Items.SLIME_BLOCK)); - ingredients.add(getNonNull(mappings, Items.COBWEB)); - ingredients.add(getNonNull(mappings, Items.BREEZE_ROD)); + ingredients.add(mappings.getMapping(Items.STONE)); + ingredients.add(mappings.getMapping(Items.SLIME_BLOCK)); + ingredients.add(mappings.getMapping(Items.COBWEB)); + ingredients.add(mappings.getMapping(Items.BREEZE_ROD)); List inputs = List.of( - getNonNull(mappings, Items.POTION), - getNonNull(mappings, Items.SPLASH_POTION), - getNonNull(mappings, Items.LINGERING_POTION) + mappings.getMapping(Items.POTION), + mappings.getMapping(Items.SPLASH_POTION), + mappings.getMapping(Items.LINGERING_POTION) ); - ItemMapping glassBottle = getNonNull(mappings, Items.GLASS_BOTTLE); + ItemMapping glassBottle = mappings.getMapping(Items.GLASS_BOTTLE); Set potionMixes = new HashSet<>(); // Add all types of potions as inputs - ItemMapping fillerIngredient = ingredients.get(0); + ItemMapping fillerIngredient = ingredients.getFirst(); for (ItemMapping entryInput : inputs) { for (Potion potion : Potion.VALUES) { potionMixes.add(new PotionMixData( @@ -117,12 +116,4 @@ public Int2ObjectMap> load(Object input) { allPotionMixes.trim(); return allPotionMixes; } - - private static ItemMapping getNonNull(ItemMappings mappings, Item javaItem) { - ItemMapping itemMapping = mappings.getMapping(javaItem); - if (itemMapping == null) - throw new NullPointerException("No item entry exists for java identifier: " + javaItem.javaIdentifier()); - - return itemMapping; - } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/RecipeRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/RecipeRegistryLoader.java deleted file mode 100644 index d0673a1c5f6..00000000000 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/RecipeRegistryLoader.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2019-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.registry.loader; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import org.cloudburstmc.nbt.NbtMap; -import org.cloudburstmc.nbt.NbtType; -import org.geysermc.geyser.inventory.recipe.GeyserRecipe; -import org.geysermc.mcprotocollib.protocol.codec.MinecraftTypes; -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.Ingredient; - -import java.util.ArrayList; -import java.util.Base64; -import java.util.List; -import java.util.Map; - -/** - * Populates the recipe registry with some recipes that Java does not send, to ensure they show up as intended - * in the recipe book. - */ -public abstract class RecipeRegistryLoader implements RegistryLoader>> { - -// @Override -// public Map> load(String input) { -// if (true) { -// return Collections.emptyMap(); -// } -// Map> deserializedRecipes = new Object2ObjectOpenHashMap<>(); -// -// List recipes; -// try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/recipes.nbt")) { -// try (NBTInputStream nbtStream = new NBTInputStream(new DataInputStream(stream))) { -// recipes = ((NbtMap) nbtStream.readTag()).getList("recipes", NbtType.COMPOUND); -// } -// } catch (Exception e) { -// throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e); -// } -// -// MinecraftCodecHelper helper = MinecraftCodec.CODEC.getHelperFactory().get(); -// for (NbtMap recipeCollection : recipes) { -// var pair = getRecipes(recipeCollection, helper); -// deserializedRecipes.put(pair.key(), pair.value()); -// } -// return deserializedRecipes; -// } - -// private static Pair> getRecipes(NbtMap recipes, MinecraftCodecHelper helper) { -// List typedRecipes = recipes.getList("recipes", NbtType.COMPOUND); -// RecipeType recipeType = RecipeType.from(recipes.getInt("recipe_type", -1)); -// if (recipeType == RecipeType.CRAFTING_SPECIAL_TIPPEDARROW) { -// return Pair.of(recipeType, getShapedRecipes(typedRecipes, helper)); -// } else { -// return Pair.of(recipeType, getShapelessRecipes(typedRecipes, helper)); -// } -// } - - private static List getShapelessRecipes(List recipes) { - List deserializedRecipes = new ObjectArrayList<>(recipes.size()); - for (NbtMap recipe : recipes) { - ItemStack output = toItemStack(recipe.getCompound("output")); - List rawInputs = recipe.getList("inputs", NbtType.COMPOUND); - Ingredient[] javaInputs = new Ingredient[rawInputs.size()]; - for (int i = 0; i < rawInputs.size(); i++) { - //javaInputs[i] = new Ingredient(new ItemStack[] {toItemStack(rawInputs.get(i), helper)}); - } - //deserializedRecipes.add(new GeyserShapelessRecipe(javaInputs, output)); - } - return deserializedRecipes; - } - - private static List getShapedRecipes(List recipes) { - List deserializedRecipes = new ObjectArrayList<>(recipes.size()); - for (NbtMap recipe : recipes) { - ItemStack output = toItemStack(recipe.getCompound("output")); - List shape = recipe.getList("shape", NbtType.INT_ARRAY); - - // In the recipes mapping, each recipe is mapped by a number - List letterToRecipe = new ArrayList<>(); - for (NbtMap rawInput : recipe.getList("inputs", NbtType.COMPOUND)) { - letterToRecipe.add(toItemStack(rawInput)); - } - - Ingredient[] inputs = new Ingredient[shape.size() * shape.get(0).length]; - int i = 0; - // Create a linear array of items from the "cube" of the shape - for (int j = 0; i < shape.size() * shape.get(0).length; j++) { - for (int index : shape.get(j)) { - ItemStack stack = letterToRecipe.get(index); - //inputs[i++] = new Ingredient(new ItemStack[] {stack}); - } - } - //deserializedRecipes.add(new GeyserShapedRecipe(shape.size(), shape.get(0).length, inputs, output)); - } - return deserializedRecipes; - } - - /** - * Converts our serialized NBT into an ItemStack. - * id is the Java item ID as an integer, components is an optional String of the data components serialized - * as bytes in Base64 (so MCProtocolLib can parse the data). - */ - private static ItemStack toItemStack(NbtMap nbt) { - int id = nbt.getInt("id"); - int count = nbt.getInt("count", 1); - String componentsRaw = nbt.getString("components", null); - if (componentsRaw != null) { - byte[] bytes = Base64.getDecoder().decode(componentsRaw); - ByteBuf buf = Unpooled.wrappedBuffer(bytes); - DataComponents components = MinecraftTypes.readDataComponentPatch(buf, false); - return new ItemStack(id, count, components); - } - return new ItemStack(id, count); - } -} diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java index 3c14ade1a04..913fa53a737 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java @@ -59,6 +59,7 @@ import org.geysermc.geyser.level.block.type.BlockState; import org.geysermc.geyser.level.block.type.FlowerPotBlock; import org.geysermc.geyser.registry.BlockRegistries; +import org.geysermc.geyser.registry.populator.conversion.GoldenDandelionConverter; import org.geysermc.geyser.registry.type.BlockMappings; import org.geysermc.geyser.registry.type.GeyserBedrockBlock; import org.geysermc.geyser.util.JsonUtils; @@ -120,9 +121,9 @@ private static void nullifyBlocksNbt() { private static void registerBedrockBlocks() { var blockMappers = ImmutableMap., Remapper>builder() // This is technically the same 1.21.111 palette; there have been no changes - .put(ObjectIntPair.of("1_21_130", Bedrock_v898.CODEC.getProtocolVersion()), tag -> tag) + .put(ObjectIntPair.of("1_21_130", Bedrock_v898.CODEC.getProtocolVersion()), GoldenDandelionConverter::convert) // 26.0 also doesn't have any changes, so we re-use the same file - .put(ObjectIntPair.of("1_21_130", Bedrock_v924.CODEC.getProtocolVersion()), tag -> tag) + .put(ObjectIntPair.of("1_21_130", Bedrock_v924.CODEC.getProtocolVersion()), GoldenDandelionConverter::convert) .put(ObjectIntPair.of("1_26_10", Bedrock_v944.CODEC.getProtocolVersion()), tag -> tag) .put(ObjectIntPair.of("1_26_20", Bedrock_v975.CODEC.getProtocolVersion()), tag -> tag) .build(); diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java index 572e99e630b..65771e0a727 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java @@ -711,8 +711,8 @@ private static void computeUseEffectsProperties(NbtMapBuilder itemProperties, Nb private static NbtMapBuilder addAttackRangeProperties(NbtMapBuilder component, AttackRange attackRange) { return component - .putCompound("reach", createReachMap(attackRange.minRange(), attackRange.maxRange())) - .putCompound("creative_reach", createReachMap(attackRange.minCreativeRange(), attackRange.maxCreativeRange())) + .putCompound("reach", createReachMap(attackRange.minReach(), attackRange.maxReach())) + .putCompound("creative_reach", createReachMap(attackRange.minCreativeReach(), attackRange.maxCreativeReach())) .putFloat("hitbox_margin", attackRange.hitboxMargin()); // TODO is this 1-to-1 with Java? } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index e902ab505aa..f61ad71ccbd 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -143,9 +143,11 @@ interface Remapper { } public static void populate() { + Map dandelion = Map.of(Items.GOLDEN_DANDELION, Items.DANDELION); + List paletteVersions = new ArrayList<>(3); - paletteVersions.add(new PaletteVersion("1_21_130", Bedrock_v898.CODEC.getProtocolVersion())); - paletteVersions.add(new PaletteVersion("1_26_0", Bedrock_v924.CODEC.getProtocolVersion())); + paletteVersions.add(new PaletteVersion("1_21_130", Bedrock_v898.CODEC.getProtocolVersion(), dandelion)); + paletteVersions.add(new PaletteVersion("1_26_0", Bedrock_v924.CODEC.getProtocolVersion(), dandelion)); paletteVersions.add(new PaletteVersion("1_26_10", Bedrock_v944.CODEC.getProtocolVersion())); paletteVersions.add(new PaletteVersion("1_26_20", Bedrock_v975.CODEC.getProtocolVersion(), "1_26_10")); @@ -930,23 +932,23 @@ public int compare(GeyserCustomMappingData firstData, GeyserCustomMappingData se // Loop through the predicates of the first definition and look for a range dispatch predicate for (MinecraftPredicate predicate : first.predicates()) { - if (predicate instanceof GeyserRangeDispatchPredicate rangeDispatch) { + if (predicate instanceof GeyserRangeDispatchPredicate(GeyserRangeDispatchPredicate.GeyserRangeDispatchProperty rangeProperty, double threshold, int index, boolean normalized, boolean negated)) { // Look for a similar predicate in the other definition's predicates Optional other = second.predicates().stream() .filter(GeyserRangeDispatchPredicate.class::isInstance) .map(GeyserRangeDispatchPredicate.class::cast) .filter(otherPredicate -> - otherPredicate.rangeProperty() == rangeDispatch.rangeProperty() - && otherPredicate.index() == rangeDispatch.index() - && otherPredicate.normalized() == rangeDispatch.normalized() - && otherPredicate.negated() == rangeDispatch.negated()) + otherPredicate.rangeProperty() == rangeProperty + && otherPredicate.index() == index + && otherPredicate.normalized() == normalized + && otherPredicate.negated() == negated) .findFirst(); if (other.isPresent()) { // If a similar predicate was found, check if they're negated // If they are, prefer the one with the lowest threshold // If they aren't, prefer the one with the highest threshold - double thresholdComparison = rangeDispatch.negated() ? rangeDispatch.threshold() - other.get().threshold() : other.get().threshold() - rangeDispatch.threshold(); + double thresholdComparison = negated ? threshold - other.get().threshold() : other.get().threshold() - threshold; if (thresholdComparison != 0.0) { return thresholdComparison < 0 ? -1 : 1; } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/conversion/GoldenDandelionConverter.java b/core/src/main/java/org/geysermc/geyser/registry/populator/conversion/GoldenDandelionConverter.java new file mode 100644 index 00000000000..2d0ec8bdd82 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/conversion/GoldenDandelionConverter.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026 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.conversion; + +import org.cloudburstmc.nbt.NbtMap; + +public class GoldenDandelionConverter extends ConversionHelper { + + public static NbtMap convert(NbtMap tag) { + if (tag.getString("name").equals("minecraft:golden_dandelion")) { + return withoutStates("dandelion"); + } + return tag; + } + +} diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 30b3c1e1881..b14415344a4 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -62,7 +62,6 @@ import org.cloudburstmc.protocol.bedrock.BedrockServerSession; import org.cloudburstmc.protocol.bedrock.data.Ability; import org.cloudburstmc.protocol.bedrock.data.AbilityLayer; -import org.cloudburstmc.protocol.bedrock.data.AuthoritativeMovementMode; import org.cloudburstmc.protocol.bedrock.data.ChatRestrictionLevel; import org.cloudburstmc.protocol.bedrock.data.ExperimentData; import org.cloudburstmc.protocol.bedrock.data.GamePublishSetting; @@ -155,6 +154,7 @@ import org.geysermc.geyser.item.type.BlockItem; import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.level.JavaDimension; +import org.geysermc.geyser.level.gamerule.GameRuleHandler; import org.geysermc.geyser.level.physics.CollisionManager; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.netty.LocalSession; @@ -296,6 +296,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private final EntityCache entityCache; private final EntityEffectCache effectCache; private final FormCache formCache; + private final GameRuleHandler gameRuleHandler; private final InputCache inputCache; private final LodestoneCache lodestoneCache; private final PistonCache pistonCache; @@ -646,14 +647,13 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { /** * Controls whether the daylight cycle gamerule has been sent to the client, so the sun/moon remain motionless. */ - private boolean daylightCycle = true; + private boolean shouldClientTickClock = true; private boolean reducedDebugInfo = false; /** * The op permission level set by the server */ - @Setter private int opPermissionLevel = 0; /** @@ -728,12 +728,35 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private long clientTicks; /** - * The world time in ticks according to the server - *

- * Note: The TickingStatePacket is currently ignored. + * The game ticks counter according to the server. + * + *

This is probably the counter you want to use when dealing with ticks on the server. The day time counters can + * change and can have a custom rate, however this counter never changes in a dimension, and always has the same rate (1 tick every 20th of a second), + * pending any server lag.

+ * + *

Note: The TickingStatePacket is currently ignored.

*/ @Setter - private long worldTicks; + private long gameTicks; + + /** + * The day time in ticks according to the server. + * + *

Note: The TickingStatePacket is currently ignored.

+ */ + private long dayTimeTicks; + + /** + * The partial day time tick according to the server. + * + *

Note: The TickingStatePacket is currently ignored.

+ */ + private double partialTimeTick; + + /** + * How fast the world time should pass according to the server. Starts at 0, server will tell us what it is when joining. + */ + private float clockRate = 0.0F; /** * Used to return players back to their vehicles if the server doesn't want them unmounting. @@ -768,7 +791,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private final GeyserEntityData entityData; - @Getter(AccessLevel.MODULE) + @Getter(AccessLevel.PACKAGE) private MinecraftProtocol protocol; private int nanosecondsPerTick = 50000000; @@ -822,9 +845,10 @@ public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSessio this.cameraData = new GeyserCameraData(this); this.entityData = new GeyserEntityData(this); - this.worldBorder = new WorldBorder(this); this.collisionManager = new CollisionManager(this); + this.gameRuleHandler = new GameRuleHandler(this); this.blockBreakHandler = new BlockBreakHandler(this); + this.worldBorder = new WorldBorder(this); this.playerEntity = new SessionPlayerEntity(this); collisionManager.updatePlayerBoundingBox(this.playerEntity.position()); @@ -883,6 +907,7 @@ public void connect() { sendRegistryDefinitions(); sendInitialPlayerState(); sendInitialGameRules(); + resetTimeParameters(); } /** @@ -1162,10 +1187,6 @@ private void connectDownstream() { downstream.setFlag(BuiltinFlags.CLIENT_TRANSFERRING, loginEvent.transferring()); downstream.connect(false); - - if (!daylightCycle) { - setDaylightCycle(true); - } } public void disconnect(String reason) { @@ -1365,7 +1386,26 @@ protected void tick() { } ticks++; - worldTicks++; + partialTimeTick += clockRate; + if (partialTimeTick >= 1.0F) { + long flooredPartialTick = (long) Math.floor(partialTimeTick); + dayTimeTicks += flooredPartialTick; + partialTimeTick -= flooredPartialTick; + + if (!isShouldClientTickClock()) { + synchronizeTime(); + } + } + } + + public void synchronizeTime() { + // https://minecraft.wiki/w/Day-night_cycle#24-hour_Minecraft_day + SetTimePacket setTimePacket = new SetTimePacket(); + // We use modulus to prevent an integer overflow + // 24000 is the range of ticks that a Minecraft day can be; we times by 8 so all moon phases are visible + // (Last verified behavior: Bedrock 1.18.12 / Java 1.18.2) + setTimePacket.setTime((int) (Math.abs(dayTimeTicks) % (24000 * 8))); + this.sendUpstreamPacket(setTimePacket); } public void sendJsonUIData(String key, String value) { @@ -1663,7 +1703,7 @@ public void openPauseScreenAdditions() { dialogManager.openDialog(BuiltInDialog.SERVER_LINKS); } } else if (additions.size() == 1) { - dialogManager.openDialog(additions.get(0)); + dialogManager.openDialog(additions.getFirst()); } else { dialogManager.openDialog(BuiltInDialog.CUSTOM_OPTIONS); } @@ -1672,12 +1712,12 @@ public void openPauseScreenAdditions() { @Override public void openQuickActions() { List quickActions = tagCache.get(DialogTag.QUICK_ACTIONS); - if (quickActions.isEmpty()) { - return; - } else if (quickActions.size() == 1) { - dialogManager.openDialog(quickActions.get(0)); - } else { - dialogManager.openDialog(BuiltInDialog.QUICK_ACTIONS); + if (!quickActions.isEmpty()) { + if (quickActions.size() == 1) { + dialogManager.openDialog(quickActions.getFirst()); + } else { + dialogManager.openDialog(BuiltInDialog.QUICK_ACTIONS); + } } } @@ -1715,6 +1755,13 @@ public InetSocketAddress getSocketAddress() { return this.upstream.getAddress(); } + public void setOpPermissionLevel(int opPermissionLevel) { + this.opPermissionLevel = opPermissionLevel; + if (gameRuleHandler.getState() == GameRuleHandler.State.SHOWN || gameRuleHandler.getState() == GameRuleHandler.State.WAITING) { + closeForm(); + } + } + @Override public boolean sendForm(@NonNull Form form) { // First close any dialogs that are open. This won't execute the dialog's closing action. @@ -1770,7 +1817,7 @@ public void prepareForConfigurationForm() { } // Disable time progression whilst the form is open // Once logged into the game this is set correctly when receiving a time packet from the server - setDaylightCycle(false); + resetTimeParameters(); } public @NonNull PlayerInventory getPlayerInventory() { @@ -1888,8 +1935,6 @@ private StartGamePacket buildStartGamePacket() { startGamePacket.setEnchantmentSeed(0); startGamePacket.setMultiplayerCorrelationId(""); - startGamePacket.getItemDefinitions().addAll(this.itemMappings.getItemDefinitions().values()); - // Needed for custom block mappings and custom skulls system startGamePacket.getBlockProperties().addAll(this.blockMappings.getBlockProperties()); @@ -1902,7 +1947,6 @@ private StartGamePacket buildStartGamePacket() { startGamePacket.setChatRestrictionLevel(ChatRestrictionLevel.NONE); - startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.SERVER); startGamePacket.setRewindHistorySize(0); // Server authorative block breaking results in the client always sending // positions for block breaking actions, which is easier to validate @@ -2082,15 +2126,46 @@ public void setReducedDebugInfo(boolean value) { } /** - * Changes the daylight cycle gamerule on the client - * This is used in login and configuration screens along-side normal usage + * Changes the daylight cycle gamerule on the client. + * The gamerule is true whenever the clock rate is normal (1.0), and false otherwise (because then we keep track of time). * - * @param doCycle If the cycle should continue + * @param shouldTick if the client should tick its daylight cycle clock */ - public void setDaylightCycle(boolean doCycle) { - sendGameRule("dodaylightcycle", doCycle); + public void setShouldClientTickClock(boolean shouldTick) { + if (this.shouldClientTickClock == shouldTick) { + return; + } + sendGameRule("dodaylightcycle", shouldTick); // Save the value so we don't have to constantly send a daylight cycle gamerule update - this.daylightCycle = doCycle; + this.shouldClientTickClock = shouldTick; + } + + /** + * Sets the current Java tick counters + * + * @param timeTicks the current day time ticks according to the server (minecraft:overworld clock) + * @param partialTick the current partial day time tick according to the server (minecraft:overworld clock) + */ + public void setTimeTicks(long timeTicks, float partialTick) { + this.dayTimeTicks = timeTicks; + this.partialTimeTick = partialTick; + synchronizeTime(); + } + + public void setClockRate(float rate) { + this.clockRate = rate; + setShouldClientTickClock(this.clockRate == 1.0f); + } + + /** + * Resets the Java game tick and time tick (time tick, partial tick, clock rate) counters to their initial values. + * + *

Please note that this also freezes time as the initial clock rate value is 0.

+ */ + public void resetTimeParameters() { + setGameTicks(0L); + setTimeTicks(0L, 0.0F); + setClockRate(0.0F); } /** diff --git a/core/src/main/java/org/geysermc/geyser/session/SessionDisconnectListener.java b/core/src/main/java/org/geysermc/geyser/session/SessionDisconnectListener.java index 071038e3026..816199d9282 100644 --- a/core/src/main/java/org/geysermc/geyser/session/SessionDisconnectListener.java +++ b/core/src/main/java/org/geysermc/geyser/session/SessionDisconnectListener.java @@ -58,7 +58,7 @@ public static void onSessionDisconnect(SessionDisconnectEventImpl event) { PlatformType platform = session.getGeyser().platformType(); String outdatedType = (platform == PlatformType.BUNGEECORD || platform == PlatformType.VELOCITY || platform == PlatformType.VIAPROXY) ? "geyser.network.remote.outdated.proxy" : "geyser.network.remote.outdated.server"; - event.disconnectReason(GeyserLocale.getPlayerLocaleString(outdatedType, locale, GameProtocol.getJavaVersions().get(0)) + '\n' + event.disconnectReason(GeyserLocale.getPlayerLocaleString(outdatedType, locale, GameProtocol.getJavaVersions().getFirst()) + '\n' + GeyserLocale.getPlayerLocaleString("geyser.network.remote.original_disconnect_message", locale, serverDisconnectMessage)); } else if (testForMissingProfilePublicKey(disconnectReason)) { event.disconnectReason("Please set `enforce-secure-profile` to `false` in server.properties for Bedrock players to be able to connect." + '\n' diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/BlockBreakHandler.java b/core/src/main/java/org/geysermc/geyser/session/cache/BlockBreakHandler.java index 51b482a2e5c..48f7abaa68b 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/BlockBreakHandler.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/BlockBreakHandler.java @@ -63,12 +63,11 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.BlockBreakStage; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction; import org.geysermc.mcprotocollib.protocol.data.game.item.component.AdventureModePredicate; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; import org.geysermc.mcprotocollib.protocol.data.game.item.component.ToolData; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundAttackPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket; @@ -449,8 +448,7 @@ protected boolean testForItemFrameEntity(Vector3i position) { Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, position); if (itemFrameEntity != null) { - ServerboundInteractPacket attackPacket = new ServerboundInteractPacket(itemFrameEntity.getEntityId(), - InteractAction.ATTACK, session.isSneaking()); + ServerboundAttackPacket attackPacket = new ServerboundAttackPacket(itemFrameEntity.getEntityId()); session.sendDownstreamGamePacket(attackPacket); interactPosition = position; return true; diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/BundleCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/BundleCache.java index 51e5c394f63..3f145d53071 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/BundleCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/BundleCache.java @@ -163,7 +163,7 @@ public void onItemAdded(GeyserItemStack bundle) { List contents = data.contents(); int bedrockSlot = platformConvertSlot(contents.size(), 0); - ItemData bedrockContent = contents.get(0).getItemData(session); + ItemData bedrockContent = contents.getFirst().getItemData(session); sendInventoryPacket(data.bundleId(), bedrockSlot, bedrockContent, bundle.getItemData(session)); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java index 8c60585f37e..31972355556 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java @@ -94,15 +94,20 @@ public final class RegistryCache implements JavaRegistryProvider { register(JavaRegistries.TRIM_PATTERN, TrimRecipe::readTrimPattern); register(JavaRegistries.DAMAGE_TYPE, RegistryReader.UNIT); register(JavaRegistries.DIALOG, Dialog::readDialog); + register(JavaRegistries.WORLD_CLOCK, RegistryReader.UNIT); register(JavaRegistries.CAT_VARIANT, VariantHolder.reader(CatEntity.BuiltInVariant.class, CatEntity.BuiltInVariant.BLACK)); + register(JavaRegistries.CAT_SOUND_VARIANT, RegistryReader.UNIT); register(JavaRegistries.FROG_VARIANT, VariantHolder.reader(FrogEntity.BuiltInVariant.class, FrogEntity.BuiltInVariant.TEMPERATE)); register(JavaRegistries.WOLF_VARIANT, VariantHolder.reader(WolfEntity.BuiltInVariant.class, WolfEntity.BuiltInVariant.PALE)); register(JavaRegistries.WOLF_SOUND_VARIANT, RegistryReader.UNIT); register(JavaRegistries.PIG_VARIANT, TemperatureVariantAnimal.VARIANT_READER); + register(JavaRegistries.PIG_SOUND_VARIANT, RegistryReader.UNIT); register(JavaRegistries.COW_VARIANT, TemperatureVariantAnimal.VARIANT_READER); + register(JavaRegistries.COW_SOUND_VARIANT, RegistryReader.UNIT); register(JavaRegistries.CHICKEN_VARIANT, TemperatureVariantAnimal.VARIANT_READER); + register(JavaRegistries.CHICKEN_SOUND_VARIANT, RegistryReader.UNIT); register(JavaRegistries.ZOMBIE_NAUTILUS_VARIANT, ZombieNautilusEntity.VARIANT_READER); // Load from MCProtocolLib's classloader @@ -188,7 +193,7 @@ private static void readRegistry(GeyserSession session, JavaRegistryKey r entry = new RegistryEntry(entry.getId(), localRegistry.get(entry.getId())); } - RegistryEntryContext context = new RegistryEntryContext(entry, entryIdMap, Optional.of(session)); + RegistryEntryContext context = new RegistryEntryContext(entry, key -> entryIdMap.getOrDefault(key, -1), Optional.of(session)); // This is what Geyser wants to keep as a value for this registry. T cacheEntry = reader.read(context); if (cacheEntry == null) { diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java index d8e9844bbfd..c8510253475 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java @@ -90,15 +90,20 @@ public class JavaRegistries { public static final JavaRegistryKey TRIM_PATTERN = create("trim_pattern"); public static final JavaRegistryKey DAMAGE_TYPE = create("damage_type"); public static final JavaRegistryKey DIALOG = create("dialog"); + public static final JavaRegistryKey WORLD_CLOCK = create("world_clock"); public static final JavaRegistryKey CAT_VARIANT = create("cat_variant"); + public static final JavaRegistryKey CAT_SOUND_VARIANT = create("cat_sound_variant"); public static final JavaRegistryKey FROG_VARIANT = create("frog_variant"); public static final JavaRegistryKey WOLF_VARIANT = create("wolf_variant"); public static final JavaRegistryKey WOLF_SOUND_VARIANT = create("wolf_sound_variant"); public static final JavaRegistryKey PIG_VARIANT = create("pig_variant"); + public static final JavaRegistryKey PIG_SOUND_VARIANT = create("pig_sound_variant"); public static final JavaRegistryKey COW_VARIANT = create("cow_variant"); + public static final JavaRegistryKey COW_SOUND_VARIANT = create("cow_sound_variant"); public static final JavaRegistryKey CHICKEN_VARIANT = create("chicken_variant"); + public static final JavaRegistryKey CHICKEN_SOUND_VARIANT = create("chicken_sound_variant"); public static final JavaRegistryKey ZOMBIE_NAUTILUS_VARIANT = create("zombie_nautilus_variant"); private static JavaRegistryKey create(String key, JavaRegistryKey.RegistryLookup registryLookup) { diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryContext.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryContext.java index ff684f8b8e6..9f6c0ddfc26 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryContext.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryContext.java @@ -25,8 +25,8 @@ package org.geysermc.geyser.session.cache.registry; -import it.unimi.dsi.fastutil.objects.Object2IntMap; import java.util.Optional; +import java.util.function.ToIntFunction; import net.kyori.adventure.key.Key; import org.cloudburstmc.nbt.NbtMap; @@ -38,16 +38,16 @@ * Used to store context around a single registry entry when reading said entry's NBT. * * @param entry the registry entry being read. - * @param keyIdMap a map for each of the resource location's in the registry and their respective network IDs. + * @param keyIdFunction a function that turns a resource location in the registry to its respective network ID. * @param session the Geyser session. Only empty during testing. */ -public record RegistryEntryContext(RegistryEntry entry, Object2IntMap keyIdMap, Optional session) { +public record RegistryEntryContext(RegistryEntry entry, ToIntFunction keyIdFunction, Optional session) { // TODO: not a fan of this. With JavaRegistryKey#key now being a thing, I'd rather have that always used, so that registry readers won't have to worry // about using the right method. This would require pre-populating all data-driven registries with default (probably null) values before actually decoding the data from the registy packet. // This could also be helpful in the feature when a data-driven registry reader needs to use an element from another data-driven registry public int getNetworkId(Key registryKey) { - return keyIdMap.getOrDefault(registryKey, -1); + return keyIdFunction.applyAsInt(registryKey); } public Key id() { diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/tags/BlockTag.java b/core/src/main/java/org/geysermc/geyser/session/cache/tags/BlockTag.java index 0267798adb4..3dc92701af2 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/tags/BlockTag.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/tags/BlockTag.java @@ -73,6 +73,7 @@ public final class BlockTag { public static final Tag ANVIL = create("anvil"); public static final Tag RAILS = create("rails"); public static final Tag LEAVES = create("leaves"); + public static final Tag PREVENTS_NEARBY_LEAF_DECAY = create("prevents_nearby_leaf_decay"); public static final Tag WOODEN_TRAPDOORS = create("wooden_trapdoors"); public static final Tag TRAPDOORS = create("trapdoors"); public static final Tag SMALL_FLOWERS = create("small_flowers"); @@ -91,6 +92,9 @@ public final class BlockTag { public static final Tag EMERALD_ORES = create("emerald_ores"); public static final Tag COPPER_ORES = create("copper_ores"); public static final Tag DIRT = create("dirt"); + public static final Tag MUD = create("mud"); + public static final Tag MOSS_BLOCKS = create("moss_blocks"); + public static final Tag GRASS_BLOCKS = create("grass_blocks"); public static final Tag TERRACOTTA = create("terracotta"); public static final Tag COMPLETES_FIND_TREE_TUTORIAL = create("completes_find_tree_tutorial"); public static final Tag SHULKER_BOXES = create("shulker_boxes"); @@ -122,7 +126,6 @@ public final class BlockTag { public static final Tag WALL_CORALS = create("wall_corals"); public static final Tag CORAL_PLANTS = create("coral_plants"); public static final Tag CORALS = create("corals"); - public static final Tag BAMBOO_PLANTABLE_ON = create("bamboo_plantable_on"); public static final Tag WALL_SIGNS = create("wall_signs"); public static final Tag SIGNS = create("signs"); public static final Tag WALL_HANGING_SIGNS = create("wall_hanging_signs"); @@ -149,18 +152,21 @@ public final class BlockTag { public static final Tag GUARDED_BY_PIGLINS = create("guarded_by_piglins"); public static final Tag PREVENT_MOB_SPAWNING_INSIDE = create("prevent_mob_spawning_inside"); public static final Tag UNSTABLE_BOTTOM_CENTER = create("unstable_bottom_center"); - public static final Tag MUSHROOM_GROW_BLOCK = create("mushroom_grow_block"); public static final Tag EDIBLE_FOR_SHEEP = create("edible_for_sheep"); public static final Tag CAN_GLIDE_THROUGH = create("can_glide_through"); public static final Tag INFINIBURN_OVERWORLD = create("infiniburn_overworld"); public static final Tag INFINIBURN_NETHER = create("infiniburn_nether"); public static final Tag INFINIBURN_END = create("infiniburn_end"); + public static final Tag SUBSTRATE_OVERWORLD = create("substrate_overworld"); public static final Tag BASE_STONE_OVERWORLD = create("base_stone_overworld"); public static final Tag STONE_ORE_REPLACEABLES = create("stone_ore_replaceables"); public static final Tag DEEPSLATE_ORE_REPLACEABLES = create("deepslate_ore_replaceables"); public static final Tag BASE_STONE_NETHER = create("base_stone_nether"); public static final Tag OVERWORLD_CARVER_REPLACEABLES = create("overworld_carver_replaceables"); public static final Tag NETHER_CARVER_REPLACEABLES = create("nether_carver_replaceables"); + public static final Tag BENEATH_TREE_PODZOL_REPLACEABLE = create("beneath_tree_podzol_replaceable"); + public static final Tag BENEATH_BAMBOO_PODZOL_REPLACEABLE = create("beneath_bamboo_podzol_replaceable"); + public static final Tag CANNOT_REPLACE_BELOW_TREE_TRUNK = create("cannot_replace_below_tree_trunk"); public static final Tag CANDLE_CAKES = create("candle_cakes"); public static final Tag CAULDRONS = create("cauldrons"); public static final Tag CRYSTAL_SOUND_BLOCKS = create("crystal_sound_blocks"); @@ -174,8 +180,10 @@ public final class BlockTag { public static final Tag MOSS_REPLACEABLE = create("moss_replaceable"); public static final Tag LUSH_GROUND_REPLACEABLE = create("lush_ground_replaceable"); public static final Tag AZALEA_ROOT_REPLACEABLE = create("azalea_root_replaceable"); - public static final Tag SMALL_DRIPLEAF_PLACEABLE = create("small_dripleaf_placeable"); - public static final Tag BIG_DRIPLEAF_PLACEABLE = create("big_dripleaf_placeable"); + public static final Tag ICE_SPIKE_REPLACEABLE = create("ice_spike_replaceable"); + public static final Tag FOREST_ROCK_CAN_PLACE_ON = create("forest_rock_can_place_on"); + public static final Tag HUGE_BROWN_MUSHROOM_CAN_PLACE_ON = create("huge_brown_mushroom_can_place_on"); + public static final Tag HUGE_RED_MUSHROOM_CAN_PLACE_ON = create("huge_red_mushroom_can_place_on"); public static final Tag SNOW = create("snow"); public static final Tag MINEABLE_AXE = create("mineable/axe"); public static final Tag MINEABLE_HOE = create("mineable/hoe"); @@ -218,12 +226,48 @@ public final class BlockTag { public static final Tag CONVERTABLE_TO_MUD = create("convertable_to_mud"); public static final Tag MANGROVE_LOGS_CAN_GROW_THROUGH = create("mangrove_logs_can_grow_through"); public static final Tag MANGROVE_ROOTS_CAN_GROW_THROUGH = create("mangrove_roots_can_grow_through"); - public static final Tag DRY_VEGETATION_MAY_PLACE_ON = create("dry_vegetation_may_place_on"); public static final Tag SNAPS_GOAT_HORN = create("snaps_goat_horn"); public static final Tag REPLACEABLE_BY_TREES = create("replaceable_by_trees"); public static final Tag REPLACEABLE_BY_MUSHROOMS = create("replaceable_by_mushrooms"); - public static final Tag SNOW_LAYER_CANNOT_SURVIVE_ON = create("snow_layer_cannot_survive_on"); - public static final Tag SNOW_LAYER_CAN_SURVIVE_ON = create("snow_layer_can_survive_on"); + public static final Tag ENABLES_BUBBLE_COLUMN_DRAG_DOWN = create("enables_bubble_column_drag_down"); + public static final Tag ENABLES_BUBBLE_COLUMN_PUSH_UP = create("enables_bubble_column_push_up"); + public static final Tag SUPPORTS_VEGETATION = create("supports_vegetation"); + public static final Tag SUPPORTS_DRY_VEGETATION = create("supports_dry_vegetation"); + public static final Tag SUPPORTS_CROPS = create("supports_crops"); + public static final Tag SUPPORTS_STEM_CROPS = create("supports_stem_crops"); + public static final Tag SUPPORTS_STEM_FRUIT = create("supports_stem_fruit"); + public static final Tag SUPPORTS_PUMPKIN_STEM = create("supports_pumpkin_stem"); + public static final Tag SUPPORTS_MELON_STEM = create("supports_melon_stem"); + public static final Tag SUPPORTS_PUMPKIN_STEM_FRUIT = create("supports_pumpkin_stem_fruit"); + public static final Tag SUPPORTS_MELON_STEM_FRUIT = create("supports_melon_stem_fruit"); + public static final Tag SUPPORTS_SUGAR_CANE = create("supports_sugar_cane"); + public static final Tag SUPPORTS_SUGAR_CANE_ADJACENTLY = create("supports_sugar_cane_adjacently"); + public static final Tag SUPPORTS_BAMBOO = create("supports_bamboo"); + public static final Tag SUPPORTS_SMALL_DRIPLEAF = create("supports_small_dripleaf"); + public static final Tag SUPPORTS_BIG_DRIPLEAF = create("supports_big_dripleaf"); + public static final Tag SUPPORTS_CACTUS = create("supports_cactus"); + public static final Tag SUPPORTS_CHORUS_PLANT = create("supports_chorus_plant"); + public static final Tag SUPPORTS_CHORUS_FLOWER = create("supports_chorus_flower"); + public static final Tag SUPPORTS_NETHER_SPROUTS = create("supports_nether_sprouts"); + public static final Tag SUPPORTS_AZALEA = create("supports_azalea"); + public static final Tag SUPPORTS_WARPED_FUNGUS = create("supports_warped_fungus"); + public static final Tag SUPPORTS_CRIMSON_FUNGUS = create("supports_crimson_fungus"); + public static final Tag SUPPORTS_MANGROVE_PROPAGULE = create("supports_mangrove_propagule"); + public static final Tag SUPPORTS_HANGING_MANGROVE_PROPAGULE = create("supports_hanging_mangrove_propagule"); + public static final Tag SUPPORTS_NETHER_WART = create("supports_nether_wart"); + public static final Tag SUPPORTS_CRIMSON_ROOTS = create("supports_crimson_roots"); + public static final Tag SUPPORTS_WARPED_ROOTS = create("supports_warped_roots"); + public static final Tag SUPPORTS_WITHER_ROSE = create("supports_wither_rose"); + public static final Tag SUPPORTS_COCOA = create("supports_cocoa"); + public static final Tag SUPPORTS_LILY_PAD = create("supports_lily_pad"); + public static final Tag SUPPORTS_FROGSPAWN = create("supports_frogspawn"); + public static final Tag SUPPORT_OVERRIDE_CACTUS_FLOWER = create("support_override_cactus_flower"); + public static final Tag SUPPORT_OVERRIDE_SNOW_LAYER = create("support_override_snow_layer"); + public static final Tag CANNOT_SUPPORT_SNOW_LAYER = create("cannot_support_snow_layer"); + public static final Tag CANNOT_SUPPORT_SEAGRASS = create("cannot_support_seagrass"); + public static final Tag CANNOT_SUPPORT_KELP = create("cannot_support_kelp"); + public static final Tag OVERRIDES_MUSHROOM_LIGHT_REQUIREMENT = create("overrides_mushroom_light_requirement"); + public static final Tag GROWS_CROPS = create("grows_crops"); public static final Tag INVALID_SPAWN_INSIDE = create("invalid_spawn_inside"); public static final Tag SNIFFER_DIGGABLE_BLOCK = create("sniffer_diggable_block"); public static final Tag SNIFFER_EGG_HATCH_BOOST = create("sniffer_egg_hatch_boost"); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/tags/EnchantmentTag.java b/core/src/main/java/org/geysermc/geyser/session/cache/tags/EnchantmentTag.java index 1ef87564265..44514b339bf 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/tags/EnchantmentTag.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/tags/EnchantmentTag.java @@ -63,13 +63,6 @@ public final class EnchantmentTag { public static final Tag TRADES_SNOW_COMMON = create("trades/snow_common"); public static final Tag TRADES_SWAMP_COMMON = create("trades/swamp_common"); public static final Tag TRADES_TAIGA_COMMON = create("trades/taiga_common"); - public static final Tag TRADES_DESERT_SPECIAL = create("trades/desert_special"); - public static final Tag TRADES_JUNGLE_SPECIAL = create("trades/jungle_special"); - public static final Tag TRADES_PLAINS_SPECIAL = create("trades/plains_special"); - public static final Tag TRADES_SAVANNA_SPECIAL = create("trades/savanna_special"); - public static final Tag TRADES_SNOW_SPECIAL = create("trades/snow_special"); - public static final Tag TRADES_SWAMP_SPECIAL = create("trades/swamp_special"); - public static final Tag TRADES_TAIGA_SPECIAL = create("trades/taiga_special"); private EnchantmentTag() {} diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/tags/GeyserHolderSet.java b/core/src/main/java/org/geysermc/geyser/session/cache/tags/GeyserHolderSet.java index 42f8e10a5d5..2b9b421c98d 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/tags/GeyserHolderSet.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/tags/GeyserHolderSet.java @@ -195,7 +195,7 @@ public static GeyserHolderSet readHolderSet(JavaRegistryKey registry, } else if (holderSet instanceof List list) { if (list.isEmpty()) { return new GeyserHolderSet<>(registry); - } else if (list.get(0) instanceof NbtMap) { + } else if (list.getFirst() instanceof NbtMap) { if (reader != null) { return new GeyserHolderSet<>(registry, list.stream().map(o -> (NbtMap) o).map(reader).toList()); } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/tags/ItemTag.java b/core/src/main/java/org/geysermc/geyser/session/cache/tags/ItemTag.java index 1202a387d4b..6abf3bc736c 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/tags/ItemTag.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/tags/ItemTag.java @@ -91,6 +91,9 @@ public final class ItemTag { public static final Tag EMERALD_ORES = create("emerald_ores"); public static final Tag COPPER_ORES = create("copper_ores"); public static final Tag DIRT = create("dirt"); + public static final Tag MUD = create("mud"); + public static final Tag MOSS_BLOCKS = create("moss_blocks"); + public static final Tag GRASS_BLOCKS = create("grass_blocks"); public static final Tag TERRACOTTA = create("terracotta"); public static final Tag COMPLETES_FIND_TREE_TUTORIAL = create("completes_find_tree_tutorial"); public static final Tag SHULKER_BOXES = create("shulker_boxes"); @@ -199,16 +202,22 @@ public final class ItemTag { public static final Tag BREAKS_DECORATED_POTS = create("breaks_decorated_pots"); public static final Tag VILLAGER_PLANTABLE_SEEDS = create("villager_plantable_seeds"); public static final Tag VILLAGER_PICKS_UP = create("villager_picks_up"); - public static final Tag DYEABLE = create("dyeable"); public static final Tag FURNACE_MINECART_FUEL = create("furnace_minecart_fuel"); public static final Tag BUNDLES = create("bundles"); public static final Tag BOOK_CLONING_TARGET = create("book_cloning_target"); + public static final Tag DYES = create("dyes"); + public static final Tag LOOM_DYES = create("loom_dyes"); + public static final Tag LOOM_PATTERNS = create("loom_patterns"); + public static final Tag CAULDRON_CAN_REMOVE_DYE = create("cauldron_can_remove_dye"); + public static final Tag CAT_COLLAR_DYES = create("cat_collar_dyes"); + public static final Tag WOLF_COLLAR_DYES = create("wolf_collar_dyes"); public static final Tag SKELETON_PREFERRED_WEAPONS = create("skeleton_preferred_weapons"); public static final Tag DROWNED_PREFERRED_WEAPONS = create("drowned_preferred_weapons"); public static final Tag PIGLIN_PREFERRED_WEAPONS = create("piglin_preferred_weapons"); public static final Tag PILLAGER_PREFERRED_WEAPONS = create("pillager_preferred_weapons"); public static final Tag WITHER_SKELETON_DISLIKED_WEAPONS = create("wither_skeleton_disliked_weapons"); public static final Tag SHEARABLE_FROM_COPPER_GOLEM = create("shearable_from_copper_golem"); + public static final Tag METAL_NUGGETS = create("metal_nuggets"); public static final Tag ENCHANTABLE_FOOT_ARMOR = create("enchantable/foot_armor"); public static final Tag ENCHANTABLE_LEG_ARMOR = create("enchantable/leg_armor"); public static final Tag ENCHANTABLE_CHEST_ARMOR = create("enchantable/chest_armor"); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/waypoint/AzimuthWaypoint.java b/core/src/main/java/org/geysermc/geyser/session/cache/waypoint/AzimuthWaypoint.java index 2900ebe772c..539042bbab9 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/waypoint/AzimuthWaypoint.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/waypoint/AzimuthWaypoint.java @@ -49,8 +49,8 @@ public AzimuthWaypoint(GeyserSession session, Optional player, Col @Override public void setData(WaypointData data) { - if (data instanceof AzimuthWaypointData azimuthData) { - angle = azimuthData.angle(); + if (data instanceof AzimuthWaypointData(float azimuthAngle)) { + angle = azimuthAngle; updatePosition(); } else { session.getGeyser().getLogger().warning("Received incorrect waypoint data " + data.getClass() + " for azimuth waypoint"); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/waypoint/ChunkWaypoint.java b/core/src/main/java/org/geysermc/geyser/session/cache/waypoint/ChunkWaypoint.java index 0a5a9698c19..6ab0c1c6abc 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/waypoint/ChunkWaypoint.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/waypoint/ChunkWaypoint.java @@ -42,9 +42,9 @@ public ChunkWaypoint(GeyserSession session, Optional player, Color @Override public void setData(WaypointData data) { - if (data instanceof ChunkWaypointData chunkData) { + if (data instanceof ChunkWaypointData(int chunkX, int chunkZ)) { // Set position in centre of chunk - position = Vector3f.from(chunkData.chunkX() * 16.0F + 8.0F, session.getPlayerEntity().position().getY(), chunkData.chunkZ() * 16.0F + 8.0F); + position = Vector3f.from(chunkX * 16.0F + 8.0F, session.getPlayerEntity().position().getY(), chunkZ * 16.0F + 8.0F); } else { session.getGeyser().getLogger().warning("Received incorrect waypoint data " + data.getClass() + " for chunk waypoint"); } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/waypoint/CoordinatesWaypoint.java b/core/src/main/java/org/geysermc/geyser/session/cache/waypoint/CoordinatesWaypoint.java index 63485b8a6a2..5092f9539e8 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/waypoint/CoordinatesWaypoint.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/waypoint/CoordinatesWaypoint.java @@ -25,12 +25,13 @@ package org.geysermc.geyser.session.cache.waypoint; +import org.cloudburstmc.math.vector.Vector3i; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.level.waypoint.Vec3iWaypointData; import org.geysermc.mcprotocollib.protocol.data.game.level.waypoint.WaypointData; -import java.awt.Color; +import java.awt.*; import java.util.Optional; public class CoordinatesWaypoint extends GeyserWaypoint { @@ -41,8 +42,8 @@ public CoordinatesWaypoint(GeyserSession session, Optional player, @Override public void setData(WaypointData data) { - if (data instanceof Vec3iWaypointData vec3iData) { - position = vec3iData.vector().toFloat(); + if (data instanceof Vec3iWaypointData(Vector3i vector)) { + position = vector.toFloat(); } else { session.getGeyser().getLogger().warning("Received incorrect waypoint data " + data.getClass() + " for coordinates waypoint"); } diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/Dialog.java b/core/src/main/java/org/geysermc/geyser/session/dialog/Dialog.java index 7bd420b43d1..f0f4e1c93e8 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/Dialog.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/Dialog.java @@ -79,21 +79,20 @@ protected Dialog(Optional session, NbtMap map) { afterAction = AfterAction.fromString(map.getString("after_action")); Object bodyTag = map.get("body"); - if (bodyTag == null) { - labels = List.of(); - } else if (bodyTag instanceof NbtMap bodyMap) { - labels = readBody(session, bodyMap).map(List::of).orElse(List.of()); - } else if (bodyTag instanceof List bodyList) { - labels = new ArrayList<>(); - for (Object tag : bodyList) { - if (tag instanceof NbtMap bodyMap) { - readBody(session, bodyMap).ifPresent(labels::add); - } else { - throw new IllegalStateException("Found non-NBT map in list of bodies, was: " + tag); + switch (bodyTag) { + case null -> labels = List.of(); + case NbtMap bodyMap -> labels = readBody(session, bodyMap).map(List::of).orElse(List.of()); + case List bodyList -> { + labels = new ArrayList<>(); + for (Object tag : bodyList) { + if (tag instanceof NbtMap bodyMap) { + readBody(session, bodyMap).ifPresent(labels::add); + } else { + throw new IllegalStateException("Found non-NBT map in list of bodies, was: " + tag); + } } } - } else { - throw new IllegalStateException("Expected body tag to either be a NBT map or list thereof, was: " + bodyTag); + default -> throw new IllegalStateException("Expected body tag to either be a NBT map or list thereof, was: " + bodyTag); } List inputTag = map.getList("inputs", NbtType.COMPOUND); diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/DialogHolder.java b/core/src/main/java/org/geysermc/geyser/session/dialog/DialogHolder.java index a279eda9d15..0c5a9a15feb 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/DialogHolder.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/DialogHolder.java @@ -262,8 +262,8 @@ private boolean runAction(Optional button, @NonNull ParsedInputs i session.sendCommand(command); return true; - } else if (action instanceof DialogAction.OpenUrl openUrl) { - showUrl(openUrl.url()); + } else if (action instanceof DialogAction.OpenUrl(String url)) { + showUrl(url); return false; } else { action.run(session, inputs); diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/action/DialogAction.java b/core/src/main/java/org/geysermc/geyser/session/dialog/action/DialogAction.java index bc2d97bf22c..8aeff82ff74 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/action/DialogAction.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/action/DialogAction.java @@ -198,7 +198,7 @@ public String command(GeyserSession session, ParsedInputs inputs) { // Append the remaining segment if there is one if (segments.size() > variables.size()) { - command.append(segments.get(segments.size() - 1)); + command.append(segments.getLast()); } return command.toString(); diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java index e8479e50166..23aa70afeb4 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java @@ -248,7 +248,7 @@ public static void handleBedrockSkin(AvatarEntity playerEntity, BedrockClientDat geyser.getLogger().debug("The size of '" + playerEntity.getUsername() + "' skin is: " + clientData.getSkinImageWidth() + "x" + clientData.getSkinImageHeight()); } - if (!clientData.getCapeId().equals("")) { + if (!clientData.getCapeId().isEmpty()) { SkinProvider.storeBedrockCape(clientData.getCapeId(), capeBytes); } } catch (Exception e) { diff --git a/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java b/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java index 65a62665d55..4654c1eff02 100644 --- a/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java +++ b/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java @@ -40,7 +40,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; @@ -48,10 +50,14 @@ public class MinecraftLocale { public static final Map> LOCALE_MAPPINGS = new HashMap<>(); + private static final List REMOVED_KEYS = new ArrayList<>(); + private static final Map REPLACED_KEYS = new HashMap<>(); + // Check instance availability to avoid exception during testing private static final boolean IN_INSTANCE = GeyserImpl.getInstance() != null; private static final Path LOCALE_FOLDER = (IN_INSTANCE) ? GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales") : null; + private static final Path DEPRECATED = LOCALE_FOLDER == null ? null : getPath("deprecated"); static { if (IN_INSTANCE) { @@ -76,6 +82,18 @@ public static void ensureEN_US() { })); } + public static void downloadDeprecations() { + if (!loadDeprecations()) { + AssetUtils.addTask(!Files.exists(DEPRECATED), new AssetUtils.ClientJarTask("assets/minecraft/lang/deprecated.json", + stream -> AssetUtils.saveFile(DEPRECATED, stream), + () -> { + if (!loadDeprecations()) { + GeyserImpl.getInstance().getLogger().warning("Failed to load deprecated locale file: it doesn't exist?"); + } + })); + } + } + /** * Downloads a locale from Mojang if it's not already loaded * @@ -184,6 +202,35 @@ private static boolean loadLocale(String locale) { } } + private static boolean loadDeprecations() { + if (Files.exists(DEPRECATED) && Files.isReadable(DEPRECATED)) { + try (InputStream localeStream = Files.newInputStream(DEPRECATED, StandardOpenOption.READ)) { + // Parse the file as json + JsonObject localeObj = JsonUtils.fromJson(localeStream); + JsonElement removed = localeObj.get("removed"); + if (removed.isJsonArray()) { + for (JsonElement removedElement : removed.getAsJsonArray()) { + REMOVED_KEYS.add(removedElement.getAsString()); + } + } + + JsonElement renamed = localeObj.get("renamed"); + if (renamed.isJsonObject()) { + for (Map.Entry renamedElement : renamed.getAsJsonObject().entrySet()) { + REPLACED_KEYS.put(renamedElement.getKey(), renamedElement.getValue().getAsString()); + } + } + + return true; + } catch (FileNotFoundException e){ + throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.locale.fail.file", DEPRECATED, e.getMessage())); + } catch (Exception e) { + throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.locale.fail.json", DEPRECATED), e); + } + } + return false; + } + /** * Load and parse a json lang file. * @@ -200,7 +247,10 @@ public static Map parseLangFile(Path localeFile, String locale) // Parse all the locale fields Map langMap = new HashMap<>(); for (Map.Entry entry : localeObj.entrySet()) { - langMap.put(entry.getKey(), entry.getValue().getAsString()); + if (REMOVED_KEYS.contains(entry.getKey())) { + continue; + } + langMap.put(REPLACED_KEYS.getOrDefault(entry.getKey(), entry.getKey()), entry.getValue().getAsString()); } return langMap; } catch (FileNotFoundException e){ diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/BundleInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/BundleInventoryTranslator.java index 6abd7025d6c..e578d29b5ab 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/BundleInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/BundleInventoryTranslator.java @@ -201,7 +201,7 @@ static ItemStackResponse handleBundle(GeyserSession sessio // Can't select bundle slots while holding bundle in any version; don't set desired bundle slot - if (bundleSlotData.getStackNetworkId() != contents.get(0).getNetId()) { + if (bundleSlotData.getStackNetworkId() != contents.getFirst().getNetId()) { // We're pulling out the first item; if something mismatches, wuh oh. return rejectRequest(request); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java index 96ee267684d..c645c0b5663 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java @@ -114,7 +114,7 @@ public ItemStackResponse translateSpecialRequest(GeyserSession session, Containe // Get the patterns compound tag List newBlockEntityTag = craftData.getResultItems()[0].getTag().getList("Patterns", NbtType.COMPOUND); // Get the pattern that the Bedrock client requests - the last pattern in the Patterns list - NbtMap pattern = newBlockEntityTag.get(newBlockEntityTag.size() - 1); + NbtMap pattern = newBlockEntityTag.getLast(); // Java's formula: 4 * row + col // And the Java loom window has a fixed row/width of four diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java index 9dfc410c902..314a3387e6d 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java @@ -696,17 +696,16 @@ private void flattenPositions() { * @return 0 - Fully retracted, 1 - Extending, 2 - Fully extended, 3 - Retracting */ private byte getState() { - switch (action) { - case PUSHING: - return (byte) (isDone() ? 2 : 1); - case PULLING: - return (byte) (isDone() ? 0 : 3); - default: + return switch (action) { + case PUSHING -> (byte) (isDone() ? 2 : 1); + case PULLING -> (byte) (isDone() ? 0 : 3); + default -> { if (progress == 1.0f) { - return 2; + yield 2; } - return (byte) (isDone() ? 0 : 2); - } + yield (byte) (isDone() ? 0 : 2); + } + }; } /** diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBookEditTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBookEditTranslator.java index b2d6fe7d7e7..252905660e8 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBookEditTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBookEditTranslator.java @@ -116,9 +116,9 @@ public void translate(GeyserSession session, BookEditPacket packet) { } // Remove empty pages at the end while (!pages.isEmpty()) { - String currentPage = pages.get(pages.size() - 1); + String currentPage = pages.getLast(); if (currentPage.isEmpty()) { - pages.remove(pages.size() - 1); + pages.removeLast(); } else { break; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index bfa3586cb5f..28e8c03c953 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -27,6 +27,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; +import org.cloudburstmc.math.vector.Vector3d; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.protocol.bedrock.data.SoundEvent; @@ -77,14 +78,15 @@ import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InventoryUtils; import org.geysermc.geyser.util.SoundUtils; +import org.geysermc.mcprotocollib.protocol.data.game.Holder; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction; import org.geysermc.mcprotocollib.protocol.data.game.item.HashedStack; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.InstrumentComponent; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.Instrument; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClickPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundAttackPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket; @@ -102,7 +104,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator component = session.getPlayerInventory() .getItemInHand() .getComponent(DataComponentTypes.INSTRUMENT); if (component != null) { @@ -416,8 +418,8 @@ public void translate(GeyserSession session, InventoryTransactionPacket packet) List legacySlots = packet.getLegacySlots(); if (packet.getActions().size() == 1 && !legacySlots.isEmpty()) { - InventoryActionData actionData = packet.getActions().get(0); - LegacySetItemSlotData slotData = legacySlots.get(0); + InventoryActionData actionData = packet.getActions().getFirst(); + LegacySetItemSlotData slotData = legacySlots.getFirst(); if (slotData.getContainerId() == 6 && !actionData.getFromItem().isNull()) { // The player is trying to swap out an armor piece that already has an item in it // 1.19.4 brings this natively, but we need this specific case for custom head rendering to work @@ -482,8 +484,7 @@ public void translate(GeyserSession session, InventoryTransactionPacket packet) } else { entityId = entity.getEntityId(); } - ServerboundInteractPacket attackPacket = new ServerboundInteractPacket(entityId, - InteractAction.ATTACK, session.isSneaking()); + ServerboundAttackPacket attackPacket = new ServerboundAttackPacket(entityId); session.sendDownstreamGamePacket(attackPacket); // Even though it is true that we already send this in BedrockAnimateTranslator, the behaviour is a bit inconsistent and @@ -510,31 +511,23 @@ private void processEntityInteraction(GeyserSession session, InventoryTransactio boolean isSpectator = session.getGameMode() == GameMode.SPECTATOR; for (Hand hand : EntityUtils.HANDS) { session.sendDownstreamGamePacket(new ServerboundInteractPacket(entity.getEntityId(), - InteractAction.INTERACT_AT, clickPosition.getX(), clickPosition.getY(), clickPosition.getZ(), - hand, session.isSneaking())); - - InteractionResult result; - if (isSpectator) { - result = InteractionResult.PASS; - } else { - result = entity.interactAt(hand); - } + hand, Vector3d.from(clickPosition.getX(), clickPosition.getY(), clickPosition.getZ()), + session.isSneaking())); - if (!result.consumesAction()) { - session.sendDownstreamGamePacket(new ServerboundInteractPacket(entity.getEntityId(), - InteractAction.INTERACT, hand, session.isSneaking())); - if (!isSpectator) { + if (!isSpectator) { + InteractionResult result = entity.interactAt(hand); + if (!result.consumesAction()) { result = entity.interact(hand); } - } - if (result.consumesAction()) { - if (result.shouldSwing() && hand == Hand.OFF_HAND) { - // Currently, Bedrock will send us the arm swing packet in most cases. But it won't for offhand. - session.sendDownstreamGamePacket(new ServerboundSwingPacket(hand)); - // Note here to look into sending the animation packet back to Bedrock + if (result.consumesAction()) { + if (result.shouldSwing() && hand == Hand.OFF_HAND) { + // Currently, Bedrock will send us the arm swing packet in most cases. But it won't for offhand. + session.sendDownstreamGamePacket(new ServerboundSwingPacket(hand)); + // Note here to look into sending the animation packet back to Bedrock + } + return; } - return; } } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRespawnTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRespawnTranslator.java index 878f0044368..8450dc2f9e5 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRespawnTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRespawnTranslator.java @@ -38,7 +38,7 @@ public class BedrockRespawnTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, RespawnPacket packet) { if (packet.getState() == RespawnPacket.State.CLIENT_READY) { - ServerboundClientCommandPacket javaRespawnPacket = new ServerboundClientCommandPacket(ClientCommand.RESPAWN); + ServerboundClientCommandPacket javaRespawnPacket = new ServerboundClientCommandPacket(ClientCommand.PERFORM_RESPAWN); session.sendDownstreamGamePacket(javaRespawnPacket); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockShowCreditsTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockShowCreditsTranslator.java index 06f680ca6c0..d2d8e2a975e 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockShowCreditsTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockShowCreditsTranslator.java @@ -38,7 +38,7 @@ public class BedrockShowCreditsTranslator extends PacketTranslator { + @Override + public void translate(GeyserSession session, ClientboundGameRuleValuesPacket packet) { + session.getGameRuleHandler().onGamerulesReceived(packet); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLowDiskSpaceWarningTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLowDiskSpaceWarningTranslator.java new file mode 100644 index 00000000000..8f65b0fbd6c --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLowDiskSpaceWarningTranslator.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026 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.translator.protocol.java; + +import net.kyori.adventure.text.Component; +import org.cloudburstmc.protocol.bedrock.packet.ToastRequestPacket; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundLowDiskSpaceWarningPacket; + +@Translator(packet = ClientboundLowDiskSpaceWarningPacket.class) +public class JavaLowDiskSpaceWarningTranslator extends PacketTranslator { + + private static final Component LOW_DISK_SPACE = Component.translatable("chunk.toast.lowDiskSpace"); + private static final Component LOW_DISK_SPACE_DESCRIPTION = Component.translatable("chunk.toast.lowDiskSpace.description"); + + @Override + public void translate(GeyserSession session, ClientboundLowDiskSpaceWarningPacket packet) { + ToastRequestPacket toastRequestPacket = new ToastRequestPacket(); + toastRequestPacket.setTitle(MessageTranslator.convertMessage(LOW_DISK_SPACE, session.locale())); + toastRequestPacket.setContent(MessageTranslator.convertMessage(LOW_DISK_SPACE_DESCRIPTION, session.locale())); + session.sendUpstreamPacket(toastRequestPacket); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeBookAddTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeBookAddTranslator.java index 33e9d1b21b9..ed597e99504 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeBookAddTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeBookAddTranslator.java @@ -29,6 +29,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.RecipeData; import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket; import org.cloudburstmc.protocol.bedrock.packet.UnlockedRecipesPacket; +import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe; import org.geysermc.geyser.inventory.recipe.GeyserShapelessRecipe; @@ -67,47 +68,54 @@ public void translate(GeyserSession session, ClientboundRecipeBookAddPacket pack } RecipeDisplay display = contents.display(); - if (display instanceof ShapedCraftingRecipeDisplay shapedRecipe) { - GeyserRecipe geyserRecipe = new GeyserShapedRecipe(contents.id(), netId, shapedRecipe); - - List recipeData = geyserRecipe.asRecipeData(session); - craftingDataPacket.getCraftingData().addAll(recipeData); - - List bedrockRecipeIds = new ArrayList<>(); - for (int i = 0; i < recipeData.size(); i++) { - String recipeId = contents.id() + "_" + i; - recipesPacket.getUnlockedRecipes().add(recipeId); - bedrockRecipeIds.add(recipeId); - geyserRecipes.put(netId++, geyserRecipe); + switch (display) { + case ShapedCraftingRecipeDisplay shapedRecipe -> { + GeyserRecipe geyserRecipe = new GeyserShapedRecipe(contents.id(), netId, shapedRecipe); + + List recipeData = geyserRecipe.asRecipeData(session); + craftingDataPacket.getCraftingData().addAll(recipeData); + + List bedrockRecipeIds = new ArrayList<>(); + for (int i = 0; i < recipeData.size(); i++) { + String recipeId = contents.id() + "_" + i; + recipesPacket.getUnlockedRecipes().add(recipeId); + bedrockRecipeIds.add(recipeId); + geyserRecipes.put(netId++, geyserRecipe); + } + javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds)); } - javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds)); - } else if (display instanceof ShapelessCraftingRecipeDisplay shapelessRecipe) { - GeyserRecipe geyserRecipe = new GeyserShapelessRecipe(contents.id(), netId, shapelessRecipe); - - List recipeData = geyserRecipe.asRecipeData(session); - craftingDataPacket.getCraftingData().addAll(recipeData); - - List bedrockRecipeIds = new ArrayList<>(); - for (int i = 0; i < recipeData.size(); i++) { - String recipeId = contents.id() + "_" + i; - recipesPacket.getUnlockedRecipes().add(recipeId); - bedrockRecipeIds.add(recipeId); - geyserRecipes.put(netId++, geyserRecipe); - } - javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds)); - } else if (display instanceof SmithingRecipeDisplay smithingRecipe) { - if (display.result() instanceof SmithingTrimDemoSlotDisplay) { - // Skip these - Bedrock already knows about them from the TrimDataPacket - continue; + case ShapelessCraftingRecipeDisplay shapelessRecipe -> { + GeyserRecipe geyserRecipe = new GeyserShapelessRecipe(contents.id(), netId, shapelessRecipe); + + List recipeData = geyserRecipe.asRecipeData(session); + craftingDataPacket.getCraftingData().addAll(recipeData); + + List bedrockRecipeIds = new ArrayList<>(); + for (int i = 0; i < recipeData.size(); i++) { + String recipeId = contents.id() + "_" + i; + recipesPacket.getUnlockedRecipes().add(recipeId); + bedrockRecipeIds.add(recipeId); + geyserRecipes.put(netId++, geyserRecipe); + } + javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds)); } + case SmithingRecipeDisplay smithingRecipe -> { + if (contents.display().result() instanceof SmithingTrimDemoSlotDisplay) { + // Skip these - Bedrock already knows about them from the TrimDataPacket + continue; + } - GeyserSmithingRecipe geyserRecipe = new GeyserSmithingRecipe(contents.id(), netId, smithingRecipe); - session.getSmithingRecipes().add(geyserRecipe); + GeyserSmithingRecipe geyserRecipe = new GeyserSmithingRecipe(contents.id(), netId, smithingRecipe); + session.getSmithingRecipes().add(geyserRecipe); - List recipeData = geyserRecipe.asRecipeData(session); - craftingDataPacket.getCraftingData().addAll(recipeData); + List recipeData = geyserRecipe.asRecipeData(session); + craftingDataPacket.getCraftingData().addAll(recipeData); - netId += recipeData.size(); + netId += recipeData.size(); + } + default -> { + GeyserImpl.getInstance().getLogger().debug("Ignoring unknown recipe display type! " + entry); + } } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java index 67eeea7cc73..015b112b7e1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java @@ -91,7 +91,7 @@ public void translate(GeyserSession session, ClientboundRespawnPacket packet) { DimensionUtils.fastSwitchDimension(session, fakeDim); } session.setWorldName(spawnInfo.getWorldName()); - session.setWorldTicks(0); + session.resetTimeParameters(); DimensionUtils.switchDimension(session, newDimension); ChunkUtils.loadDimension(session); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java index 3e776e5f43c..6d14e174514 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java @@ -180,7 +180,7 @@ private static void updateCraftingGrid(int slot, ItemStack item, InventoryHolder session.getCraftingRecipes().put(newRecipeId, geyserRecipe); CraftingDataPacket craftPacket = new CraftingDataPacket(); - craftPacket.getCraftingData().add(geyserRecipe.asRecipeData(session).get(0)); + craftPacket.getCraftingData().add(geyserRecipe.asRecipeData(session).getFirst()); session.sendUpstreamPacket(craftPacket); index = 0; @@ -245,7 +245,7 @@ static void updateSmithingTableOutput(int slot, ItemStack output, InventoryHolde session.getSmithingRecipes().add(geyserRecipe); CraftingDataPacket craftPacket = new CraftingDataPacket(); - craftPacket.getCraftingData().add(geyserRecipe.asRecipeData(session).get(0)); + craftPacket.getCraftingData().add(geyserRecipe.asRecipeData(session).getFirst()); session.sendUpstreamPacket(craftPacket); // Just set one of the slots to air, then right back to its proper item. diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaMountScreenOpenTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaMountScreenOpenTranslator.java index fa2ff4f5d7c..95a9e5fca59 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaMountScreenOpenTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaMountScreenOpenTranslator.java @@ -130,32 +130,37 @@ public void translate(GeyserSession session, ClientboundMountScreenOpenPacket pa int slotCount = 2; // Don't depend on slot count sent from server InventoryTranslator inventoryTranslator; - if (entity instanceof LlamaEntity llamaEntity) { - if (entity.getFlag(EntityFlag.CHESTED)) { - slotCount += llamaEntity.getStrength() * 3; + switch (entity) { + case LlamaEntity llamaEntity -> { + if (entity.getFlag(EntityFlag.CHESTED)) { + slotCount += llamaEntity.getStrength() * 3; + } + inventoryTranslator = new LlamaInventoryTranslator(slotCount); + slots.add(CARPET_SLOT); } - inventoryTranslator = new LlamaInventoryTranslator(slotCount); - slots.add(CARPET_SLOT); - } else if (entity instanceof ChestedHorseEntity) { - if (entity.getFlag(EntityFlag.CHESTED)) { - slotCount += 15; + case ChestedHorseEntity ignored -> { + if (entity.getFlag(EntityFlag.CHESTED)) { + slotCount += 15; + } + inventoryTranslator = new DonkeyInventoryTranslator(slotCount); + slots.add(SADDLE_SLOT); } - inventoryTranslator = new DonkeyInventoryTranslator(slotCount); - slots.add(SADDLE_SLOT); - } else if (entity instanceof CamelEntity) { - if (entity.getFlag(EntityFlag.CHESTED)) { - slotCount += 15; + case CamelEntity ignored -> { + if (entity.getFlag(EntityFlag.CHESTED)) { + slotCount += 15; + } + // The camel has an invisible armor slot and needs special handling, same as the donkey + inventoryTranslator = new DonkeyInventoryTranslator(slotCount); + slots.add(SADDLE_SLOT); } - // The camel has an invisible armor slot and needs special handling, same as the donkey - inventoryTranslator = new DonkeyInventoryTranslator(slotCount); - slots.add(SADDLE_SLOT); - } else { - inventoryTranslator = new MountInventoryTranslator(slotCount); - slots.add(SADDLE_SLOT); - if (entity instanceof NautilusEntity) { - slots.add(NAUTILUS_ARMOR_SLOT); - } else if (!(entity instanceof SkeletonHorseEntity || entity instanceof ZombieHorseEntity)) { - slots.add(HORSE_ARMOR_SLOT); + default -> { + inventoryTranslator = new MountInventoryTranslator(slotCount); + slots.add(SADDLE_SLOT); + if (entity instanceof NautilusEntity) { + slots.add(NAUTILUS_ARMOR_SLOT); + } else if (!(entity instanceof SkeletonHorseEntity || entity instanceof ZombieHorseEntity)) { + slots.add(HORSE_ARMOR_SLOT); + } } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java index 22afcf15160..199e5d59654 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java @@ -103,7 +103,7 @@ public void translate(GeyserSession session, ClientboundGameEventPacket packet) case WIN_GAME: switch ((EnterCreditsValue) packet.getValue()) { case SEEN_BEFORE -> { - ServerboundClientCommandPacket javaRespawnPacket = new ServerboundClientCommandPacket(ClientCommand.RESPAWN); + ServerboundClientCommandPacket javaRespawnPacket = new ServerboundClientCommandPacket(ClientCommand.PERFORM_RESPAWN); session.sendDownstreamGamePacket(javaRespawnPacket); } case FIRST_TIME -> { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java index b7a92dbd426..c86ef6d460e 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java @@ -25,8 +25,10 @@ package org.geysermc.geyser.translator.protocol.java.level; +import net.kyori.adventure.key.Key; +import org.geysermc.geyser.session.cache.registry.JavaRegistries; +import org.geysermc.mcprotocollib.protocol.data.game.level.ClockNetworkState; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundSetTimePacket; -import org.cloudburstmc.protocol.bedrock.packet.SetTimePacket; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -36,21 +38,18 @@ public class JavaSetTimeTranslator extends PacketTranslator> 16; + int g = (rgbValue & (255 << 8)) >> 8; + int b = rgbValue & 255; + return HSVLike.fromRGB(r, g, b); + } + + // Adapted from the Adventure project: + // https://github.com/KyoriPowered/adventure/blob/09edf74409feb52d9147a5a811910de0721acf95/api/src/main/java/net/kyori/adventure/text/format/NamedTextColor.java#L193-L237 + /** + * Returns a distance metric to the other color. + * + *

This value is unitless and should only be used to compare with other firework colors.

+ * + * @param other color to compare to + * @return distance metric + */ + public static float distance(final HSVLike self, final HSVLike other) { + // weight hue more heavily than saturation and brightness. kind of magic numbers, but is fine for our use case of downsampling to a set of colors + final float hueDistance = 3 * Math.min(Math.abs(self.h() - other.h()), 1f - Math.abs(self.h() - other.h())); + final float saturationDiff = self.s() - other.s(); + final float valueDiff = self.v() - other.v(); + return hueDistance * hueDistance + saturationDiff * saturationDiff + valueDiff * valueDiff; + } + + // The following six methods are "inspired by" Mojang's ARGB class: + // https://mcsrc.dev/1/26.1.2/net/minecraft/util/ARGB + public static int argbAlpha(int color) { + return color >>> 24; + } + + public static int argbRed(int color) { + return color >> 16 & 0xFF; + } + + public static int argbGreen(int color) { + return color >> 8 & 0xFF; + } + + public static int argbBlue(int color) { + return color & 0xFF; + } + + public static int argbColor(final int alpha, final int red, final int green, final int blue) { + return (alpha & 0xFF) << 24 | (red & 0xFF) << 16 | (green & 0xFF) << 8 | blue & 0xFF; + } + + public static int argbOpaque(final int color) { + return color | 0xFF000000; + } + + // "Inspired by" Mojang's DyedItemColor class: + // https://mcsrc.dev/1/26.1.2/net/minecraft/world/item/component/DyedItemColor + public static int mixDyes(@Nullable Integer current, List dyes) { + int totalRed = 0; + int totalGreen = 0; + int totalBlue = 0; + int totalIntensity = 0; + int colorCount = 0; + if (current != null) { + int red = argbRed(current); + int green = argbGreen(current); + int blue = argbBlue(current); + totalIntensity += Math.max(red, Math.max(green, blue)); + totalRed += red; + totalGreen += green; + totalBlue += blue; + colorCount++; + } + + for (DyeColor dye : dyes) { + int color = dye.getTextureDiffuseColor(); + int red = argbRed(color); + int green = argbGreen(color); + int blue = argbBlue(color); + totalIntensity += Math.max(red, Math.max(green, blue)); + totalRed += red; + totalGreen += green; + totalBlue += blue; + colorCount++; + } + + int red = totalRed / colorCount; + int green = totalGreen / colorCount; + int blue = totalBlue / colorCount; + float averageIntensity = (float) totalIntensity / colorCount; + float resultIntensity = Math.max(red, Math.max(green, blue)); + red = (int) (red * averageIntensity / resultIntensity); + green = (int) (green * averageIntensity / resultIntensity); + blue = (int) (blue * averageIntensity / resultIntensity); + return argbColor(0, red, green, blue); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/util/InternalPlatformType.java b/core/src/main/java/org/geysermc/geyser/util/InternalPlatformType.java new file mode 100644 index 00000000000..55b2b42e78c --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/util/InternalPlatformType.java @@ -0,0 +1,32 @@ +/* + * 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.util; + +import org.geysermc.geyser.api.util.PlatformType; + +public final class InternalPlatformType { + public static final PlatformType GAMETEST = new PlatformType("gametest"); +} diff --git a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java index b80d60ebf7d..638f0838010 100644 --- a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.util; +import net.kyori.adventure.key.Key; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.nbt.NbtMap; @@ -55,11 +56,13 @@ import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.translator.protocol.java.inventory.JavaMerchantOffersTranslator; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.CompositeSlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemSlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemStackSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.OnlyWithComponentSlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.TagSlotDisplay; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.WithRemainderSlotDisplay; @@ -360,27 +363,30 @@ public static boolean acceptsAsInput(GeyserSession session, SlotDisplay slotDisp if (slotDisplay instanceof EmptySlotDisplay) { return itemStack.isEmpty(); } - if (slotDisplay instanceof CompositeSlotDisplay compositeSlotDisplay) { - if (compositeSlotDisplay.contents().size() == 1) { - return acceptsAsInput(session, compositeSlotDisplay.contents().get(0), itemStack); + if (slotDisplay instanceof CompositeSlotDisplay(List contents)) { + if (contents.size() == 1) { + return acceptsAsInput(session, contents.getFirst(), itemStack); } - return compositeSlotDisplay.contents().stream().anyMatch(aSlotDisplay -> acceptsAsInput(session, aSlotDisplay, itemStack)); + return contents.stream().anyMatch(aSlotDisplay -> acceptsAsInput(session, aSlotDisplay, itemStack)); } if (slotDisplay instanceof WithRemainderSlotDisplay remainderSlotDisplay) { return acceptsAsInput(session, remainderSlotDisplay.input(), itemStack); } - if (slotDisplay instanceof ItemSlotDisplay itemSlotDisplay) { - return itemStack.getJavaId() == itemSlotDisplay.item(); + if (slotDisplay instanceof ItemSlotDisplay(int item)) { + return itemStack.getJavaId() == item; } - if (slotDisplay instanceof ItemStackSlotDisplay itemStackSlotDisplay) { - ItemStack other = itemStackSlotDisplay.itemStack(); + if (slotDisplay instanceof ItemStackSlotDisplay(ItemStack other)) { // Amount check might be flimsy? return itemStack.getJavaId() == other.getId() && itemStack.getAmount() >= other.getAmount() && Objects.equals(itemStack.getComponents(), other.getDataComponentsPatch()); } - if (slotDisplay instanceof TagSlotDisplay tagSlotDisplay) { - return itemStack.is(session, new Tag<>(JavaRegistries.ITEM, tagSlotDisplay.tag())); + if (slotDisplay instanceof TagSlotDisplay(Key tag)) { + return itemStack.is(session, new Tag<>(JavaRegistries.ITEM, tag)); } + if (slotDisplay instanceof OnlyWithComponentSlotDisplay(SlotDisplay source, DataComponentType component)) { + return itemStack.has(component) && acceptsAsInput(session, source, itemStack); + } + // TODO WithAnyPotion? session.getGeyser().getLogger().warning("Unknown slot display type: " + slotDisplay); return false; } diff --git a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java index 4e3dffe9b30..f6a0128b2b9 100644 --- a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java @@ -161,8 +161,8 @@ public static void buildAndShowLoginWindow(GeyserSession session) { return; } - // Set DoDaylightCycle to false so the time doesn't accelerate while we're here - session.setDaylightCycle(false); + // So the time doesn't accelerate while we're here + session.resetTimeParameters(); session.sendForm( SimpleForm.builder() diff --git a/core/src/main/java/org/geysermc/geyser/util/MathUtils.java b/core/src/main/java/org/geysermc/geyser/util/MathUtils.java index 29a53f9ada0..6dc735e9728 100644 --- a/core/src/main/java/org/geysermc/geyser/util/MathUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/MathUtils.java @@ -202,10 +202,7 @@ public static float clamp(float value, float low, float high) { if (value < low) { return low; } - if (value > high) { - return high; - } - return value; + return Math.min(value, high); } /** diff --git a/core/src/main/java/org/geysermc/geyser/util/MinecraftKey.java b/core/src/main/java/org/geysermc/geyser/util/MinecraftKey.java index 8c0a5630042..e36e55bcc3d 100644 --- a/core/src/main/java/org/geysermc/geyser/util/MinecraftKey.java +++ b/core/src/main/java/org/geysermc/geyser/util/MinecraftKey.java @@ -35,11 +35,24 @@ public final class MinecraftKey { /** * To prevent constant warnings from invalid regex. + * + * @throws net.kyori.adventure.key.InvalidKeyException for invalid keys */ public static Key key(@Subst("empty") String s) { return Key.key(s); } + /** + * @return null when the input is null + * @throws net.kyori.adventure.key.InvalidKeyException for invalid keys + */ + public static @Nullable Key nullableKey(@Nullable @Subst("empty") String s) { + if (s == null) { + return null; + } + return Key.key(s); + } + /** * To prevent constant warnings from invalid regex. */ @@ -51,7 +64,7 @@ public static Key key(@Subst("empty") String namespace, @Subst("empty") String v if (identifier == null) { return null; } - return identifier instanceof IdentifierImpl impl ? impl.identifier() : key(identifier.namespace(), identifier.path()); + return identifier instanceof IdentifierImpl(Key key) ? key : key(identifier.namespace(), identifier.path()); } public static @Nullable Identifier keyToIdentifier(@Nullable Key key) { diff --git a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java index 1ca90c8787e..f7c16d362cc 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java @@ -28,10 +28,6 @@ import org.cloudburstmc.protocol.bedrock.packet.SetDifficultyPacket; import org.geysermc.cumulus.component.DropdownComponent; import org.geysermc.cumulus.form.CustomForm; -import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.Permissions; -import org.geysermc.geyser.level.GameRule; -import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.MinecraftLocale; @@ -82,22 +78,6 @@ public static CustomForm buildForm(GeyserSession session) { } } - boolean showGamerules = session.getOpPermissionLevel() >= 2 || session.hasPermission(Permissions.SETTINGS_GAMERULES); - if (showGamerules) { - builder.label("geyser.settings.title.game_rules") - .translator(MinecraftLocale::getLocaleString); // we need translate gamerules next - - WorldManager worldManager = GeyserImpl.getInstance().getWorldManager(); - for (GameRule gamerule : GameRule.VALUES) { - // Add the relevant form item based on the gamerule type - if (Boolean.class.equals(gamerule.getType())) { - builder.toggle(gamerule.getTranslation(), worldManager.getGameRuleBool(session, gamerule)); - } else if (Integer.class.equals(gamerule.getType())) { - builder.input(gamerule.getTranslation(), "", String.valueOf(worldManager.getGameRuleInt(session, gamerule))); - } - } - } - builder.validResultHandler(response -> { applyDifficultyFix(session); if (showClientSettings) { @@ -120,22 +100,6 @@ public static CustomForm buildForm(GeyserSession session) { session.getPreferencesCache().setPrefersCustomSkulls(response.next()); } } - - if (showGamerules) { - for (GameRule gamerule : GameRule.VALUES) { - if (Boolean.class.equals(gamerule.getType())) { - boolean value = response.next(); - if (value != session.getGeyser().getWorldManager().getGameRuleBool(session, gamerule)) { - session.getGeyser().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); - } - } else if (Integer.class.equals(gamerule.getType())) { - int value = Integer.parseInt(response.next()); - if (value != session.getGeyser().getWorldManager().getGameRuleInt(session, gamerule)) { - session.getGeyser().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); - } - } - } - } }); builder.closedOrInvalidResultHandler($ -> applyDifficultyFix(session)); diff --git a/core/src/main/java/org/geysermc/geyser/util/TypeAdapter.java b/core/src/main/java/org/geysermc/geyser/util/TypeAdapter.java new file mode 100644 index 00000000000..abb8e47af17 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/util/TypeAdapter.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026 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.util; + +import java.util.function.Function; + +public record TypeAdapter(Class type, Function parser) { + public static final TypeAdapter BOOLEAN = new TypeAdapter<>(Boolean.class, (object) -> { + if (object instanceof Boolean bool) { + return bool; + } + return Boolean.parseBoolean(object.toString()); + }); + public static final TypeAdapter INTEGER = new TypeAdapter<>(Integer.class, (object) -> { + if (object instanceof Integer intValue) { + return intValue; + } + return Integer.parseInt(object.toString()); + }); +} + diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index 4ce8ad58ea7..a4ba5aed259 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 4ce8ad58ea7ab779d613a64862956d6d0563a8e3 +Subproject commit a4ba5aed2596a1b06d5eccf8fe4507eb9a1fddc0 diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 2f0a8da2000..021e45045e0 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 2f0a8da200084688ec0919b59abf062977f50aef +Subproject commit 021e45045e00bb65d73477c01052276caf9bf92a diff --git a/core/src/test/java/org/geysermc/geyser/configuration/CustomSkullsLoaderTest.java b/core/src/test/java/org/geysermc/geyser/configuration/CustomSkullsLoaderTest.java index 654e548003f..ddbd4a18562 100644 --- a/core/src/test/java/org/geysermc/geyser/configuration/CustomSkullsLoaderTest.java +++ b/core/src/test/java/org/geysermc/geyser/configuration/CustomSkullsLoaderTest.java @@ -50,10 +50,10 @@ void readConfig() throws IOException { null); GeyserCustomSkullConfiguration skullConfig = FileUtils.loadConfig(skullConfigFile, GeyserCustomSkullConfiguration.class); - assertEquals("GeyserMC", skullConfig.getPlayerUsernames().get(0)); - assertEquals("8b8d8e8f-2759-47c6-acb5-5827de8a72b8", skullConfig.getPlayerUUIDs().get(0)); - assertEquals("ewogICJ0aW1lc3RhbXAiIDogMTY1NzMyMjIzOTgzMywKICAicHJvZmlsZUlkIiA6ICJjZGRiZTUyMGQwNDM0YThiYTFjYzlmYzkyZmRlMmJjZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbWJlcmljaHUiLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTkwNzkwYzU3ZTE4MWVkMTNhZGVkMTRjNDdlZTJmN2M4ZGUzNTMzZTAxN2JhOTU3YWY3YmRmOWRmMWJkZTk0ZiIsCiAgICAgICJtZXRhZGF0YSIgOiB7CiAgICAgICAgIm1vZGVsIiA6ICJzbGltIgogICAgICB9CiAgICB9CiAgfQp9", skullConfig.getPlayerProfiles().get(0)); - assertEquals("a90790c57e181ed13aded14c47ee2f7c8de3533e017ba957af7bdf9df1bde94f", skullConfig.getPlayerSkinHashes().get(0)); + assertEquals("GeyserMC", skullConfig.getPlayerUsernames().getFirst()); + assertEquals("8b8d8e8f-2759-47c6-acb5-5827de8a72b8", skullConfig.getPlayerUUIDs().getFirst()); + assertEquals("ewogICJ0aW1lc3RhbXAiIDogMTY1NzMyMjIzOTgzMywKICAicHJvZmlsZUlkIiA6ICJjZGRiZTUyMGQwNDM0YThiYTFjYzlmYzkyZmRlMmJjZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbWJlcmljaHUiLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTkwNzkwYzU3ZTE4MWVkMTNhZGVkMTRjNDdlZTJmN2M4ZGUzNTMzZTAxN2JhOTU3YWY3YmRmOWRmMWJkZTk0ZiIsCiAgICAgICJtZXRhZGF0YSIgOiB7CiAgICAgICAgIm1vZGVsIiA6ICJzbGltIgogICAgICB9CiAgICB9CiAgfQp9", skullConfig.getPlayerProfiles().getFirst()); + assertEquals("a90790c57e181ed13aded14c47ee2f7c8de3533e017ba957af7bdf9df1bde94f", skullConfig.getPlayerSkinHashes().getFirst()); } @SneakyThrows diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContext.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContext.java index 4ddb0df2f79..4c646f0a7ad 100644 --- a/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContext.java +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContext.java @@ -125,7 +125,7 @@ public BedrockPacket nextPacket() { if (packets.isEmpty()) { return null; } - return packets.remove(0); + return packets.removeFirst(); } public List packets() { diff --git a/gradle.properties b/gradle.properties index 3709cf907c0..7f2296f7ddc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,5 +8,5 @@ org.gradle.vfs.watch=false group=org.geysermc id=geyser -version=2.9.6-SNAPSHOT +version=2.10.0-SNAPSHOT description=Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b60d5bf74b3..91aa9ee23ec 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -base-api = "1.0.2" +base-api = "1.0.3" bstats = "3.1.0" cumulus = "1.1.2" configurate = "4.2.0-GeyserMC-20251111.004649-11" @@ -18,7 +18,7 @@ protocol-common = "3.0.0.Beta12-20260428.175455-16" protocol-codec = "3.0.0.Beta12-20260428.175455-16" raknet = "1.0.0.CR3-20260421.190401-34" minecraftauth = "5.0.0" -mcprotocollib = "1.21.11-20260217.182735-13" +mcprotocollib = "26.1-20260404.063343-16" adventure = "4.25.0" adventure-platform = "4.4.1" junit = "6.0.0" @@ -27,40 +27,40 @@ jetbrains-annotations = "24.0.0" log4j = "2.20.0" jline = "3.21.0" terminalconsoleappender = "1.2.0" -folia = "1.19.4-R0.1-SNAPSHOT" -viaversion = "4.9.2" +folia = "1.21.11-R0.1-SNAPSHOT" +viaversion = "5.9.0-SNAPSHOT" adapters = "1.17-SNAPSHOT" -cloud = "2.0.0-rc.2" -cloud-minecraft = "2.0.0-beta.14" -cloud-minecraft-modded = "2.0.0-beta.15" +cloud = "2.0.0" +cloud-minecraft = "2.0.0-SNAPSHOT" +cloud-minecraft-modded = "2.0.0-SNAPSHOT" commodore = "2.2" -bungeecord = "1.21-R0.1-20250215.224541-54" -bungeecord-api = "1.21-R0.1" -velocity = "3.4.0-SNAPSHOT" -viaproxy = "3.3.2-SNAPSHOT" -fabric-loader = "0.18.2" -fabric-api = "0.139.4+1.21.11" -fabric-permissions-api = "0.6.1" -neoforge-minecraft = "21.11.0-beta" +bungeecord = "1.21-R0.4-20250913.044521-9" +bungeecord-api = "26.1-R0.1-SNAPSHOT" +velocity = "3.5.0-SNAPSHOT" +viaproxy = "3.4.10" +fabric-loader = "0.18.6" +fabric-api = "0.145.3+26.1.1" +fabric-permissions-api = "0.7.0" +neoforge-minecraft = "26.1.1.4-beta" mixin = "0.8.5" -mixinextras = "0.3.5" -minecraft = "1.21.11" +mixinextras = "0.5.3" +minecraft = "26.1.1" mockito = "5.+" # plugin versions indra = "4.0.0" -shadow = "9.2.2" -architectury-plugin = "3.4.162" -architectury-loom = "1.13.457" -loom-companion = "1.13.467" -minotaur = "2.8.10" -lombok = "9.1.0" +shadow = "9.4.1" +architectury-plugin = "3.5-SNAPSHOT" +architectury-loom = "1.14-SNAPSHOT" +loom-companion = "1.14-SNAPSHOT" +minotaur = "2.9.0" +lombok = "9.2.0" blossom = "2.2.0" runtask = "3.0.2" # run tasks versions -runpaperversion = "1.21.11" -runvelocityversion = "3.4.0-SNAPSHOT" +runpaperversion = "26.1.1" +runvelocityversion = "3.5.0-SNAPSHOT" [libraries] base-api = { group = "org.geysermc.api", name = "base-api", version.ref = "base-api" } @@ -166,9 +166,8 @@ mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" } lombok = { group = "io.freefair.gradle", name = "lombok-plugin", version.ref = "lombok" } indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" } shadow = { group = "com.gradleup.shadow", name = "com.gradleup.shadow.gradle.plugin", version.ref = "shadow" } -# architectury-plugin = { group = "architectury-plugin", name = "architectury-plugin.gradle.plugin", version.ref = "architectury-plugin" } -architectury-plugin = { group = "dev.kastle.architectury-plugin", name = "architectury-plugin.gradle.plugin", version = "5ffb79eb20" } -architectury-loom = { group = "dev.architectury.loom", name = "dev.architectury.loom.gradle.plugin", version.ref = "architectury-loom" } +architectury-plugin = { group = "architectury-plugin", name = "architectury-plugin.gradle.plugin", version.ref = "architectury-plugin" } +architectury-loom = { group = "dev.architectury.loom-no-remap", name = "dev.architectury.loom-no-remap.gradle.plugin", version.ref = "architectury-loom" } minotaur = { group = "com.modrinth.minotaur", name = "Minotaur", version.ref = "minotaur" } loom-companion = { group = "dev.architectury.loom-companion", name = "dev.architectury.loom-companion.gradle.plugin", version.ref = "loom-companion" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bad7c2462f5..c61a118f7dd 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle.kts b/settings.gradle.kts index aeef7f8c863..84b208842c9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,7 +10,6 @@ pluginManagement { maven("https://maven.fabricmc.net/") maven("https://maven.architectury.dev/") maven("https://maven.neoforged.net/releases") - maven("https://jitpack.io/") } includeBuild("build-logic") } @@ -43,4 +42,4 @@ project(":viaproxy").projectDir = file("bootstrap/viaproxy") // Allow to download JVMs for toolchains plugins { id("org.gradle.toolchains.foojay-resolver-convention") version ("1.0.0") -} \ No newline at end of file +}