diff --git a/README.md b/README.md index 50ef6c7ab..3da04c09d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here! ## Supported Versions -Geyser is currently supporting Minecraft Bedrock 1.21.50 - 1.21.72 and Minecraft Java 1.21.5. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/). +Geyser is currently supporting Minecraft Bedrock 1.21.50 - 1.21.80 and Minecraft Java 1.21.5. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/). ## Setting Up Take a look [here](https://geysermc.org/wiki/geyser/setup/) for how to set up Geyser. 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 667b4190b..a5c583b96 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 @@ -210,11 +210,19 @@ public class BoatEntity extends Entity implements Leashable, Tickable { if (isPaddlingLeft) { paddleTimeLeft += ROWING_SPEED; - sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_LEFT, paddleTimeLeft); + if (GameProtocol.is1_21_80orHigher(session)) { + dirtyMetadata.put(EntityDataTypes.ROW_TIME_LEFT, paddleTimeLeft); + } else { + sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_LEFT, paddleTimeLeft); + } } if (isPaddlingRight) { paddleTimeRight += ROWING_SPEED; - sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_RIGHT, paddleTimeRight); + if (GameProtocol.is1_21_80orHigher(session)) { + dirtyMetadata.put(EntityDataTypes.ROW_TIME_RIGHT, paddleTimeRight); + } else { + sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_RIGHT, paddleTimeRight); + } } } diff --git a/core/src/main/java/org/geysermc/geyser/network/CodecProcessor.java b/core/src/main/java/org/geysermc/geyser/network/CodecProcessor.java index f2ed792c7..b91b3c407 100644 --- a/core/src/main/java/org/geysermc/geyser/network/CodecProcessor.java +++ b/core/src/main/java/org/geysermc/geyser/network/CodecProcessor.java @@ -282,8 +282,6 @@ class CodecProcessor { .updateSerializer(InventorySlotPacket.class, INVENTORY_SLOT_SERIALIZER_V748) .updateSerializer(MovePlayerPacket.class, MOVE_PLAYER_SERIALIZER) .updateSerializer(MoveEntityAbsolutePacket.class, MOVE_ENTITY_SERIALIZER) - .updateSerializer(RiderJumpPacket.class, ILLEGAL_SERIALIZER) - .updateSerializer(PlayerInputPacket.class, ILLEGAL_SERIALIZER) // Ignored only when serverbound .updateSerializer(BossEventPacket.class, bossEventSerializer) .updateSerializer(MobArmorEquipmentPacket.class, MOB_ARMOR_EQUIPMENT_SERIALIZER) @@ -303,6 +301,13 @@ class CodecProcessor { .updateSerializer(SimpleEventPacket.class, IGNORED_SERIALIZER) .updateSerializer(MultiplayerSettingsPacket.class, IGNORED_SERIALIZER); + // These packets have been removed post 1.21.80. + if (codec.getProtocolVersion() < 800) { + codecBuilder + .updateSerializer(RiderJumpPacket.class, ILLEGAL_SERIALIZER) + .updateSerializer(PlayerInputPacket.class, ILLEGAL_SERIALIZER); + } + return codecBuilder.build(); } 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 a3e2ac6ac..04f7a9153 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -27,10 +27,10 @@ package org.geysermc.geyser.network; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; -import org.cloudburstmc.protocol.bedrock.codec.v748.Bedrock_v748; import org.cloudburstmc.protocol.bedrock.codec.v766.Bedrock_v766; import org.cloudburstmc.protocol.bedrock.codec.v776.Bedrock_v776; import org.cloudburstmc.protocol.bedrock.codec.v786.Bedrock_v786; +import org.cloudburstmc.protocol.bedrock.codec.v800.Bedrock_v800; import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec; @@ -49,8 +49,8 @@ public final class GameProtocol { * Default Bedrock codec that should act as a fallback. Should represent the latest available * release of the game that Geyser supports. */ - public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v786.CODEC.toBuilder() - .minecraftVersion("1.21.70") + public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v800.CODEC.toBuilder() + .minecraftVersion("1.21.80") .build()); /** @@ -71,6 +71,9 @@ public final class GameProtocol { SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v776.CODEC.toBuilder() .minecraftVersion("1.21.60 - 1.21.62") .build())); + SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v786.CODEC.toBuilder() + .minecraftVersion("1.21.70 - 1.21.73") + .build())); SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); } @@ -90,10 +93,6 @@ public final class GameProtocol { /* Bedrock convenience methods to gatekeep features and easily remove the check on version removal */ - public static boolean isPreWinterDrop(GeyserSession session) { - return session.getUpstream().getProtocolVersion() == Bedrock_v748.CODEC.getProtocolVersion(); - } - public static boolean isPreCreativeInventoryRewrite(int protocolVersion) { return protocolVersion < 776; } @@ -102,6 +101,14 @@ public final class GameProtocol { return session.protocolVersion() >= Bedrock_v786.CODEC.getProtocolVersion(); } + public static boolean isTheOneVersionWithBrokenForms(GeyserSession session) { + return session.protocolVersion() == Bedrock_v786.CODEC.getProtocolVersion(); + } + + public static boolean is1_21_80orHigher(GeyserSession session) { + return session.protocolVersion() >= Bedrock_v800.CODEC.getProtocolVersion(); + } + /** * Gets the {@link PacketCodec} for Minecraft: Java Edition. * 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 af0c9dbc0..26e7bc9e8 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -31,6 +31,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; +import org.cloudburstmc.protocol.bedrock.data.biome.BiomeDefinitions; import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.PotionMixData; import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; import org.geysermc.geyser.GeyserImpl; @@ -98,9 +99,15 @@ public final class Registries { /** * A registry holding a NbtMap of all the known biomes. + * Remove once 1.21.80 is lowest supported version - replaced by {@link Registries#BIOMES} */ public static final SimpleDeferredRegistry BIOMES_NBT = SimpleDeferredRegistry.create("bedrock/biome_definitions.dat", RegistryLoaders.NBT); + /** + * A registry holding biome data for all known biomes. + */ + public static final SimpleDeferredRegistry BIOMES = SimpleDeferredRegistry.create("bedrock/stripped_biome_definitions.json", RegistryLoaders.BIOME_LOADER); + /** * A mapped registry which stores Java biome identifiers and their Bedrock biome identifier. */ @@ -200,6 +207,7 @@ public final class Registries { BEDROCK_ENTITY_IDENTIFIERS.load(); BIOMES_NBT.load(); + BIOMES.load(); BIOME_IDENTIFIERS.load(); BLOCK_ENTITIES.load(); PARTICLES.load(); diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeLoader.java new file mode 100644 index 000000000..3e6541926 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeLoader.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.registry.loader; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import org.cloudburstmc.protocol.bedrock.data.biome.BiomeDefinitionData; +import org.cloudburstmc.protocol.bedrock.data.biome.BiomeDefinitions; +import org.geysermc.geyser.GeyserImpl; + +import java.awt.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.util.Map; + +public class BiomeLoader implements RegistryLoader { + private final Gson GSON = new GsonBuilder() + .registerTypeAdapter(Color.class, new ColorTypeAdapter()) + .create(); // temporary + + @Override + public BiomeDefinitions load(String input) { + Type type = new TypeToken>() {}.getType(); + Map biomes; + try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input)) { + biomes = GSON.fromJson(new InputStreamReader(stream), type); + } catch (Exception e) { + throw new AssertionError("Unable to load Bedrock biomes!", e); + } + + return new BiomeDefinitions(biomes); + } + + public static class ColorTypeAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, Color color) throws IOException { + if (color == null) { + out.nullValue(); + return; + } + + out.beginObject(); + out.name("r").value(color.getRed()); + out.name("g").value(color.getGreen()); + out.name("b").value(color.getBlue()); + out.name("a").value(color.getAlpha()); + out.endObject(); + } + + @Override + public Color read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + + int r = 0, g = 0, b = 0, a = 255; + in.beginObject(); + while (in.hasNext()) { + switch (in.nextName()) { + case "r": r = in.nextInt(); break; + case "g": g = in.nextInt(); break; + case "b": b = in.nextInt(); break; + case "a": a = in.nextInt(); break; + default: in.skipValue(); break; + } + } + in.endObject(); + return new Color(r, g, b, a); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/RegistryLoaders.java b/core/src/main/java/org/geysermc/geyser/registry/loader/RegistryLoaders.java index eaede3b15..0b26e0178 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/RegistryLoaders.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/RegistryLoaders.java @@ -38,6 +38,11 @@ public final class RegistryLoaders { */ public static final NbtRegistryLoader NBT = new NbtRegistryLoader(); + /** + * The {@link RegistryLoader} responsible for loading biome data. + */ + public static final BiomeLoader BIOME_LOADER = new BiomeLoader(); + /** * The {@link RegistryLoader} responsible for loading resource packs. */ @@ -69,4 +74,4 @@ public final class RegistryLoaders { private RegistryLoaders() { } -} \ No newline at end of file +} 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 bb7a633d2..10d9c8fe2 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 @@ -45,6 +45,7 @@ import org.cloudburstmc.nbt.NbtUtils; import org.cloudburstmc.protocol.bedrock.codec.v766.Bedrock_v766; import org.cloudburstmc.protocol.bedrock.codec.v776.Bedrock_v776; import org.cloudburstmc.protocol.bedrock.codec.v786.Bedrock_v786; +import org.cloudburstmc.protocol.bedrock.codec.v800.Bedrock_v800; import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.geysermc.geyser.GeyserImpl; @@ -121,6 +122,7 @@ public final class BlockRegistryPopulator { .put(ObjectIntPair.of("1_21_50", Bedrock_v766.CODEC.getProtocolVersion()), Conversion776_766::remapBlock) .put(ObjectIntPair.of("1_21_60", Bedrock_v776.CODEC.getProtocolVersion()), Conversion786_776::remapBlock) .put(ObjectIntPair.of("1_21_70", Bedrock_v786.CODEC.getProtocolVersion()), tag -> tag) + .put(ObjectIntPair.of("1_21_80", Bedrock_v800.CODEC.getProtocolVersion()), tag -> tag) .build(); // We can keep this strong as nothing should be garbage collected 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 09a65bf8b..bc3c3a9d1 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 @@ -48,6 +48,7 @@ import org.cloudburstmc.nbt.NbtUtils; import org.cloudburstmc.protocol.bedrock.codec.v766.Bedrock_v766; import org.cloudburstmc.protocol.bedrock.codec.v776.Bedrock_v776; import org.cloudburstmc.protocol.bedrock.codec.v786.Bedrock_v786; +import org.cloudburstmc.protocol.bedrock.codec.v800.Bedrock_v800; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.SimpleItemDefinition; @@ -129,6 +130,8 @@ public class ItemRegistryPopulator { paletteVersions.add(new PaletteVersion("1_21_50", Bedrock_v766.CODEC.getProtocolVersion(), itemFallbacks, (item, mapping) -> mapping)); paletteVersions.add(new PaletteVersion("1_21_60", Bedrock_v776.CODEC.getProtocolVersion(), itemFallbacks, (item, mapping) -> mapping)); paletteVersions.add(new PaletteVersion("1_21_70", Bedrock_v786.CODEC.getProtocolVersion())); + // Not a typo; they're the same + paletteVersions.add(new PaletteVersion("1_21_70", Bedrock_v800.CODEC.getProtocolVersion())); GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/TagRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/TagRegistryPopulator.java index 740198168..c9f74eef4 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/TagRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/TagRegistryPopulator.java @@ -36,6 +36,7 @@ import it.unimi.dsi.fastutil.objects.ObjectIntPair; import org.cloudburstmc.protocol.bedrock.codec.v766.Bedrock_v766; import org.cloudburstmc.protocol.bedrock.codec.v776.Bedrock_v776; import org.cloudburstmc.protocol.bedrock.codec.v786.Bedrock_v786; +import org.cloudburstmc.protocol.bedrock.codec.v800.Bedrock_v800; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.item.type.Item; @@ -70,7 +71,9 @@ public final class TagRegistryPopulator { List> paletteVersions = List.of( ObjectIntPair.of("1_21_50", Bedrock_v766.CODEC.getProtocolVersion()), ObjectIntPair.of("1_21_60", Bedrock_v776.CODEC.getProtocolVersion()), - ObjectIntPair.of("1_21_70", Bedrock_v786.CODEC.getProtocolVersion()) + ObjectIntPair.of("1_21_70", Bedrock_v786.CODEC.getProtocolVersion()), + // Not a typo, they're the same file + ObjectIntPair.of("1_21_70", Bedrock_v800.CODEC.getProtocolVersion()) ); Type type = new TypeToken>>() {}.getType(); 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 354e640b2..059442962 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -786,9 +786,16 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false); - BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket(); - biomeDefinitionListPacket.setDefinitions(Registries.BIOMES_NBT.get()); - upstream.sendPacket(biomeDefinitionListPacket); + if (GameProtocol.is1_21_80orHigher(this)) { + BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket(); + biomeDefinitionListPacket.setBiomes(Registries.BIOMES.get()); + upstream.sendPacket(biomeDefinitionListPacket); + GeyserImpl.getInstance().getLogger().info(biomeDefinitionListPacket.toString()); + } else { + BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket(); + biomeDefinitionListPacket.setDefinitions(Registries.BIOMES_NBT.get()); + upstream.sendPacket(biomeDefinitionListPacket); + } AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket(); entityPacket.setIdentifiers(Registries.BEDROCK_ENTITY_IDENTIFIERS.get()); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/FormCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/FormCache.java index 2f5d5e517..be5deece3 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/FormCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/FormCache.java @@ -31,10 +31,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import lombok.RequiredArgsConstructor; import org.cloudburstmc.protocol.bedrock.packet.ModalFormRequestPacket; import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket; @@ -48,6 +44,11 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + @RequiredArgsConstructor public class FormCache { private static final Gson GSON_TEMP = new Gson(); @@ -113,7 +114,7 @@ public class FormCache { //todo work on a proper solution in Cumulus, but that'd require all Floodgate instances to update as well and // drops support for older Bedrock versions (because Cumulus isn't made to support multiple versions). That's // why this hotfix exists. - if (form instanceof CustomForm customForm && GameProtocol.is1_21_70orHigher(session)) { + if (form instanceof CustomForm customForm && GameProtocol.isTheOneVersionWithBrokenForms(session) && response.getCancelReason().isEmpty()) { // Labels are no longer included as a json null, so we have to manually add them for now. IntList labelIndexes = new IntArrayList(); for (int i = 0; i < customForm.content().size(); i++) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java index 8e09c6c98..7c374c73f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java @@ -171,7 +171,7 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator