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 5d7a9859d78..2c953598304 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 | -| 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/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/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..17596b3c427 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; @@ -335,6 +336,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 +459,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 +604,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/command/CommandRegistry.java b/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java index 70bd21bc538..df97c64627d 100644 --- a/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java @@ -415,7 +415,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/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 1618929631b..3382d5cb77b 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..635e5022088 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java @@ -101,17 +101,15 @@ 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); + default -> { + GeyserImpl.getInstance().getLogger().warning("Unsure how to convert to ItemStack: " + slotDisplay); + yield GeyserItemStack.EMPTY; + } + }; } public int getJavaId() { 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/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..918ab923e24 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 @@ -92,7 +92,7 @@ public static List translateToInput(GeyserSession sessi } 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 +102,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 +131,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); @@ -178,12 +176,10 @@ public static Pair translateToOutput(GeyserSession session, Slot 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)); } session.getGeyser().getLogger().warning("Unimplemented slot display type for output: " + slotDisplay); @@ -248,7 +244,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 +287,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 3519e8f9de9..96808c901c7 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)) { trimItem = context.session().get().getItemMappings().getMapping(materialProviders().get(provider)); break; } @@ -110,10 +108,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..0a372585bf7 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) @@ -86,7 +91,7 @@ public MinecraftHasher get() { case TRUE -> true; }); - MinecraftHasher CLICK_EVENT_ACTION = MinecraftHasher.STRING.cast(ClickEvent.Action::toString); + MinecraftHasher> CLICK_EVENT_ACTION = MinecraftHasher.STRING.cast(ClickEvent.Action::name); MinecraftHasher CLICK_EVENT_TEXT_PAYLOAD = MinecraftHasher.STRING.cast(ClickEvent.Payload.Text::value); @@ -96,13 +101,15 @@ public MinecraftHasher get() { // - Dialog has no proper implementation within Adventure yet. Once it does, we'd probably only hash dialog holders with a resource location, because setting up // hashers for the full dialog structure can be a lot of work. // - Custom uses BinaryTagHolder to store NBT data, which essentially only stores a string representation. This won't work with hashing, we need a NBT tag to hash. - MinecraftHasher CLICK_EVENT = CLICK_EVENT_ACTION.dispatch("action", ClickEvent::action, action -> switch (action) { - case OPEN_URL -> builder -> builder.accept("url", CLICK_EVENT_TEXT_PAYLOAD, event -> (ClickEvent.Payload.Text) event.payload()); - case OPEN_FILE -> builder -> builder.accept("path", CLICK_EVENT_TEXT_PAYLOAD, event -> (ClickEvent.Payload.Text) event.payload()); - case RUN_COMMAND, SUGGEST_COMMAND -> builder -> builder.accept("command", CLICK_EVENT_TEXT_PAYLOAD, event -> (ClickEvent.Payload.Text) event.payload()); - case CHANGE_PAGE -> builder -> builder.accept("page", CLICK_EVENT_INT_PAYLOAD, event -> (ClickEvent.Payload.Int) event.payload()); - case COPY_TO_CLIPBOARD -> builder -> builder.accept("value", CLICK_EVENT_TEXT_PAYLOAD, event -> (ClickEvent.Payload.Text) event.payload()); - case SHOW_DIALOG, CUSTOM -> MapBuilder.unit(); + MinecraftHasher> CLICK_EVENT = CLICK_EVENT_ACTION.dispatch("action", ClickEvent::action, action -> switch (action) { + case ClickEvent.Action.OpenUrl ignored -> builder -> builder.accept("url", CLICK_EVENT_TEXT_PAYLOAD, event -> (ClickEvent.Payload.Text) event.payload()); + case ClickEvent.Action.OpenFile ignored -> builder -> builder.accept("path", CLICK_EVENT_TEXT_PAYLOAD, event -> (ClickEvent.Payload.Text) event.payload()); + case ClickEvent.Action.RunCommand ignored -> builder -> builder.accept("command", CLICK_EVENT_TEXT_PAYLOAD, event -> (ClickEvent.Payload.Text) event.payload()); + case ClickEvent.Action.SuggestCommand ignored -> builder -> builder.accept("command", CLICK_EVENT_TEXT_PAYLOAD, event -> (ClickEvent.Payload.Text) event.payload()); + case ClickEvent.Action.ChangePage ignored -> builder -> builder.accept("page", CLICK_EVENT_INT_PAYLOAD, event -> (ClickEvent.Payload.Int) event.payload()); + case ClickEvent.Action.CopyToClipboard ignored -> builder -> builder.accept("value", CLICK_EVENT_TEXT_PAYLOAD, event -> (ClickEvent.Payload.Text) event.payload()); + case ClickEvent.Action.ShowDialog ignored -> MapBuilder.unit(); + case ClickEvent.Action.Custom ignored -> MapBuilder.unit(); }); MinecraftHasher> HOVER_EVENT_ACTION = MinecraftHasher.STRING.cast(HoverEvent.Action::toString); @@ -148,30 +155,30 @@ 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) .optionalNullable("separator", COMPONENT, SelectorComponent::separator)); - MinecraftHasher> NBT_COMPONENT = component(builder -> builder + 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 +191,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..cb1afe2bd05 --- /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 1504fe911c8..ad4c76e983d 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/GameRule.java b/core/src/main/java/org/geysermc/geyser/level/GameRule.java index 02fc7a9248d..fee10cb4d62 100644 --- a/core/src/main/java/org/geysermc/geyser/level/GameRule.java +++ b/core/src/main/java/org/geysermc/geyser/level/GameRule.java @@ -33,7 +33,7 @@ * 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) +// TODO gamerules from ClientboundGameRuleValuesPacket public enum GameRule { ADVANCE_TIME("gamerule.doDaylightCycle", true), ADVANCE_WEATHER("gamerule.doWeatherCycle", true), 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/block/Blocks.java b/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java index 8753287387e..8d2f368dd1a 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 @@ -110,11 +110,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))); @@ -251,8 +251,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))); @@ -261,69 +261,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) @@ -369,9 +369,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))); @@ -394,73 +395,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))); @@ -476,18 +477,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) @@ -503,38 +504,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) @@ -553,119 +554,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) @@ -722,7 +723,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) @@ -1041,9 +1042,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))); @@ -1069,8 +1070,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) @@ -1100,8 +1101,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) @@ -1130,6 +1131,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))); @@ -1191,47 +1193,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) @@ -1239,25 +1241,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) @@ -1272,8 +1274,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))); @@ -1495,69 +1497,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))); @@ -1830,11 +1832,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))); @@ -1847,39 +1849,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) @@ -2042,7 +2044,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) @@ -2260,30 +2262,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))); @@ -2315,15 +2317,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) @@ -2434,34 +2436,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))); @@ -2704,14 +2706,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) @@ -2722,11 +2724,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))); @@ -2954,69 +2956,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) @@ -3174,20 +3176,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 0863aa8138e..0f53d472d0d 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/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index 1b688f4d772..cabacbc7772 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -85,7 +85,7 @@ public final class GameProtocol { register(Bedrock_v924.CODEC, "26.0", "26.1", "26.2", "26.3"); register(Bedrock_v944.CODEC, "26.10"); - 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..5594f3ccd7a 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -37,7 +37,6 @@ 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.pack.ResourcePackHolder; import org.geysermc.geyser.registry.loader.BiomeIdentifierRegistryLoader; @@ -169,11 +168,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. */ @@ -229,7 +223,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(); 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 91ac7ae1d19..6bebd768889 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 @@ -58,6 +58,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; @@ -119,9 +120,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) .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 e0557ba016f..290d45bd126 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 @@ -134,9 +134,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())); GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); @@ -920,23 +922,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 855cd13cb7a..c4f426d2d7a 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; @@ -640,7 +639,7 @@ 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; @@ -724,12 +723,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 + */ + private float clockRate = 1.0f; /** * Used to return players back to their vehicles if the server doesn't want them unmounting. @@ -764,7 +786,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private final GeyserEntityData entityData; - @Getter(AccessLevel.MODULE) + @Getter(AccessLevel.PACKAGE) private MinecraftProtocol protocol; private int nanosecondsPerTick = 50000000; @@ -1162,8 +1184,9 @@ private void connectDownstream() { downstream.setFlag(BuiltinFlags.CLIENT_TRANSFERRING, loginEvent.transferring()); downstream.connect(false); - if (!daylightCycle) { - setDaylightCycle(true); + if (shouldClientTickClock) { + // Java server will tell us to tick time if they want us to + setShouldClientTickClock(false); } } @@ -1364,7 +1387,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) { @@ -1662,7 +1704,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); } @@ -1671,12 +1713,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); + } } } @@ -1769,7 +1811,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); + setShouldClientTickClock(false); } public @NonNull PlayerInventory getPlayerInventory() { @@ -1879,8 +1921,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()); @@ -1893,7 +1933,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 @@ -2076,10 +2115,30 @@ public void setReducedDebugInfo(boolean value) { * * @param doCycle If the cycle should continue */ - public void setDaylightCycle(boolean doCycle) { + public void setShouldClientTickClock(boolean doCycle) { + if (this.shouldClientTickClock == doCycle) { + return; + } sendGameRule("dodaylightcycle", doCycle); // Save the value so we don't have to constantly send a daylight cycle gamerule update - this.daylightCycle = doCycle; + this.shouldClientTickClock = doCycle; + } + + /** + * 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); } /** 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..eca37a599ec 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 @@ -29,6 +29,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.data.TrimMaterial; import org.cloudburstmc.protocol.bedrock.data.TrimPattern; +import org.geysermc.geyser.api.util.Unit; import org.geysermc.geyser.entity.type.living.animal.FrogEntity; import org.geysermc.geyser.entity.type.living.animal.TemperatureVariantAnimal; import org.geysermc.geyser.entity.type.living.animal.nautilus.ZombieNautilusEntity; @@ -90,15 +91,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/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 3e5554436f3..59dd249f9d8 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 @@ -695,17 +695,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..170374cc514 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,8 +511,8 @@ 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())); + hand, Vector3d.from(clickPosition.getX(), clickPosition.getY(), clickPosition.getZ()), + session.isSneaking())); InteractionResult result; if (isSpectator) { @@ -522,7 +523,7 @@ private void processEntityInteraction(GeyserSession session, InventoryTransactio if (!result.consumesAction()) { session.sendDownstreamGamePacket(new ServerboundInteractPacket(entity.getEntityId(), - InteractAction.INTERACT, hand, session.isSneaking())); + hand, Vector3d.ZERO, session.isSneaking())); if (!isSpectator) { result = entity.interact(hand); } 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 { + + 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..89e29a071d8 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,9 @@ public void translate(GeyserSession session, ClientboundRespawnPacket packet) { DimensionUtils.fastSwitchDimension(session, fakeDim); } session.setWorldName(spawnInfo.getWorldName()); - session.setWorldTicks(0); + session.setGameTicks(0L); + session.setTimeTicks(0L, 0.0F); + session.setShouldClientTickClock(false); 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 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)); } 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 403a3d1616e..7799fd30ac4 100644 --- a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java @@ -156,7 +156,7 @@ public static void buildAndShowLoginWindow(GeyserSession session) { } // Set DoDaylightCycle to false so the time doesn't accelerate while we're here - session.setDaylightCycle(false); + session.setShouldClientTickClock(false); 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..6043efdda21 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.util; +import net.kyori.adventure.key.Key; import org.cloudburstmc.protocol.bedrock.packet.SetDifficultyPacket; import org.geysermc.cumulus.component.DropdownComponent; import org.geysermc.cumulus.form.CustomForm; @@ -36,6 +37,10 @@ import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundSetGameRulePacket; + +import java.util.HashMap; +import java.util.Map; public class SettingsUtils { /** @@ -122,19 +127,26 @@ public static CustomForm buildForm(GeyserSession session) { } if (showGamerules) { + // TODO GameRule fetching via ClientboundGameRuleValuesPacket + //Map changedGamerules = new HashMap<>(); for (GameRule gamerule : GameRule.VALUES) { if (Boolean.class.equals(gamerule.getType())) { boolean value = response.next(); if (value != session.getGeyser().getWorldManager().getGameRuleBool(session, gamerule)) { + //changedGamerules.put(null, String.valueOf(value)); 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)) { + //changedGamerules.put(null, String.valueOf(value)); session.getGeyser().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); } } } + //if (!changedGamerules.isEmpty()) { + // session.sendDownstreamGamePacket(new ServerboundSetGameRulePacket(changedGamerules)); + //} } }); diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index 4ce8ad58ea7..daa21574283 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 4ce8ad58ea7ab779d613a64862956d6d0563a8e3 +Subproject commit daa215742837ca1c16ef34e724bba98b6bc047a9 diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 2f0a8da2000..d594fe8b60e 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 2f0a8da200084688ec0919b59abf062977f50aef +Subproject commit d594fe8b60ecf514f00cde07353ba8f2c911e288 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 824c5f4f10c..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.5-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 cf8bdc11216..6d136c53c7f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,8 +18,8 @@ protocol-common = "3.0.0.Beta12-20260316.225858-10" protocol-codec = "3.0.0.Beta12-20260316.225858-10" raknet = "1.0.0.CR3-20260417.085727-30" minecraftauth = "5.0.0" -mcprotocollib = "1.21.11-20260217.182735-13" -adventure = "4.25.0" +mcprotocollib = "26.1-20260404.063343-16" +adventure = "5.0.0" adventure-platform = "4.4.1" junit = "6.0.0" checkerframework = "3.19.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.11-SNAPSHOT" +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 +}