From 03b067e23e0d2690fd71fa30f1e8ae616836997e Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Wed, 20 Apr 2022 21:37:50 -0400 Subject: [PATCH] Merge master into extensions (#2941) * Don't always store cert/client data used for skin uploaded This takes up a decent 30K of memory that we don't use after the skin is uploaded. The GameProfileTranslator cannot be run more than once per session. * Make all moon phases visible The fix to prevent integer overflows also prevented moon phases from being visible until now. Fixes #2927 * SetTimeTranslator: cast from long on the entire modulus This should fix some inaccuracies with time on older worlds. * Bump version; drop 1.17.40; support 1.18.30 * Actually bump to 2.0.3-SNAPSHOT * Fix message being sent still if a single escape character is sent * Replace instances of configs using `generateduuid` for Metrics * Fix some merge mistakes Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com> --- README.md | 2 +- build-logic/src/main/kotlin/Versions.kt | 2 +- core/build.gradle.kts | 2 +- .../GeyserJacksonConfiguration.java | 2 +- .../entity/type/player/PlayerEntity.java | 2 + .../entity/type/player/SkullPlayerEntity.java | 2 + .../geyser/level/BedrockDimension.java | 41 + .../level/block/BlockPositionIterator.java | 2 - .../geysermc/geyser/network/GameProtocol.java | 13 +- .../geyser/network/UpstreamPacketHandler.java | 7 +- .../populator/BlockRegistryPopulator.java | 72 +- .../populator/ItemRegistryPopulator.java | 30 +- .../geyser/session/GeyserSession.java | 13 +- .../geyser/session/auth/AuthData.java | 13 +- .../session/auth/BedrockClientData.java | 6 + .../geyser/session/cache/ChunkCache.java | 5 +- .../bedrock/BedrockTextTranslator.java | 12 +- .../player/BedrockMovePlayerTranslator.java | 13 +- .../java/JavaGameProfileTranslator.java | 8 +- .../JavaLevelChunkWithLightTranslator.java | 39 +- .../java/level/JavaSetTimeTranslator.java | 5 +- .../org/geysermc/geyser/util/ChunkUtils.java | 29 +- .../geysermc/geyser/util/DimensionUtils.java | 2 +- .../geyser/util/LoginEncryptionUtils.java | 6 +- ...e.1_17_40.nbt => block_palette.1_18_0.nbt} | Bin .../bedrock/block_palette.1_18_30.nbt | Bin 0 -> 45536 bytes ...17_40.json => creative_items.1_18_30.json} | 2529 +++++++++-------- ....json => runtime_item_states.1_18_30.json} | 264 +- 28 files changed, 1648 insertions(+), 1473 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java rename core/src/main/resources/bedrock/{block_palette.1_17_40.nbt => block_palette.1_18_0.nbt} (100%) create mode 100644 core/src/main/resources/bedrock/block_palette.1_18_30.nbt rename core/src/main/resources/bedrock/{creative_items.1_17_40.json => creative_items.1_18_30.json} (84%) rename core/src/main/resources/bedrock/{runtime_item_states.1_17_40.json => runtime_item_states.1_18_30.json} (96%) diff --git a/README.md b/README.md index 23bde93d2..bbb9532a5 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,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! -### Currently supporting Minecraft Bedrock 1.17.41 + 1.18.0 - 1.18.10 and Minecraft Java 1.18.2. +### Currently supporting Minecraft Bedrock 1.18.0 - 1.18.30 and Minecraft Java 1.18.2. ## Setting Up Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser. diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 96d98b18b..ce8593adc 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -30,7 +30,7 @@ object Versions { const val guavaVersion = "29.0-jre" const val nbtVersion = "2.1.0" const val websocketVersion = "1.5.1" - const val protocolVersion = "0cd24c0" + const val protocolVersion = "29ecd7a" const val raknetVersion = "1.6.28-SNAPSHOT" const val mcauthlibVersion = "d9d773e" const val mcprotocollibversion = "0771504" diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 31018c454..03260fc8f 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -29,7 +29,7 @@ dependencies { // Network libraries implementation("org.java-websocket", "Java-WebSocket", Versions.websocketVersion) - api("com.github.CloudburstMC.Protocol", "bedrock-v486", Versions.protocolVersion) { + api("com.github.CloudburstMC.Protocol", "bedrock-v503", Versions.protocolVersion) { exclude("com.nukkitx.network", "raknet") exclude("com.nukkitx", "nbt") } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java index f152efa53..630e2ffd6 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -36,8 +36,8 @@ import lombok.Getter; import lombok.Setter; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.network.AuthType; -import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.network.CIDRMatcher; +import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.text.GeyserLocale; import java.io.IOException; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java index 58f04a756..0d6c0dac1 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java @@ -37,6 +37,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.AttributeData; +import com.nukkitx.protocol.bedrock.data.GameType; import com.nukkitx.protocol.bedrock.data.PlayerPermission; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityData; @@ -126,6 +127,7 @@ public class PlayerEntity extends LivingEntity { addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.MEMBER); addPlayerPacket.setDeviceId(""); addPlayerPacket.setPlatformChatId(""); + addPlayerPacket.setGameType(GameType.SURVIVAL); //TODO addPlayerPacket.getMetadata().putFlags(flags); dirtyMetadata.apply(addPlayerPacket.getMetadata()); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java index ce1615816..f1a447b57 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.player; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.protocol.bedrock.data.GameType; import com.nukkitx.protocol.bedrock.data.PlayerPermission; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityData; @@ -84,6 +85,7 @@ public class SkullPlayerEntity extends PlayerEntity { addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.MEMBER); addPlayerPacket.setDeviceId(""); addPlayerPacket.setPlatformChatId(""); + addPlayerPacket.setGameType(GameType.SURVIVAL); addPlayerPacket.getMetadata().putFlags(flags); dirtyMetadata.apply(addPlayerPacket.getMetadata()); diff --git a/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java b/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java new file mode 100644 index 000000000..78c6b2c6a --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.level; + +/** + * A data structure to represent what Bedrock believes are the height requirements for a specific dimension. + * As of 1.18.30, biome count is representative of the height of the world, and out-of-bounds chunks can crash + * the client. + * + * @param minY The minimum height Bedrock Edition will accept. + * @param height The maximum chunk height Bedrock Edition will accept, from the lowest point to the highest. + * @param doUpperHeightWarn whether to warn in the console if the Java dimension height exceeds Bedrock's. + */ +public record BedrockDimension(int minY, int height, boolean doUpperHeightWarn) { + public static BedrockDimension OVERWORLD = new BedrockDimension(-64, 384, true); + public static BedrockDimension THE_NETHER = new BedrockDimension(0, 128, false); + public static BedrockDimension THE_END = new BedrockDimension(0, 256, true); +} diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java index 425c78f18..d22150ccf 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java @@ -26,8 +26,6 @@ package org.geysermc.geyser.level.block; import com.nukkitx.network.util.Preconditions; -import lombok.Getter; - public class BlockPositionIterator { private final int minX; 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 4164976b6..1f28a0264 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -28,11 +28,14 @@ package org.geysermc.geyser.network; import com.github.steveice10.mc.protocol.codec.MinecraftCodec; import com.github.steveice10.mc.protocol.codec.PacketCodec; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; -import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; import com.nukkitx.protocol.bedrock.v475.Bedrock_v475; import com.nukkitx.protocol.bedrock.v486.Bedrock_v486; +import com.nukkitx.protocol.bedrock.v503.Bedrock_v503; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.StringJoiner; /** * Contains information about the supported protocols in Geyser. @@ -42,7 +45,7 @@ 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 BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v486.V486_CODEC; + public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v503.V503_CODEC; /** * A list of all supported Bedrock versions that can join Geyser */ @@ -55,11 +58,11 @@ public final class GameProtocol { private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC; static { - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v471.V471_CODEC); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v475.V475_CODEC.toBuilder().minecraftVersion("1.18.0/1.18.1/1.18.2").build()); - SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder() + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v486.V486_CODEC.toBuilder() .minecraftVersion("1.18.10/1.18.12") // 1.18.11 is also supported, but was only on Switch and since that auto-updates it's not needed .build()); + SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); } /** diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index 8714dfb37..66cda8f16 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -32,16 +32,17 @@ import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.packet.*; import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.configuration.GeyserConfiguration; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.pack.ResourcePack; import org.geysermc.geyser.pack.ResourcePackManifest; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.util.*; +import org.geysermc.geyser.util.LoginEncryptionUtils; +import org.geysermc.geyser.util.MathUtils; import java.io.FileInputStream; import java.io.InputStream; 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 8238bcea1..d8aa6a456 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 @@ -28,8 +28,9 @@ package org.geysermc.geyser.registry.populator; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.ImmutableMap; import com.nukkitx.nbt.*; -import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; +import com.nukkitx.protocol.bedrock.v475.Bedrock_v475; import com.nukkitx.protocol.bedrock.v486.Bedrock_v486; +import com.nukkitx.protocol.bedrock.v503.Bedrock_v503; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.Object2IntMap; @@ -60,35 +61,50 @@ public class BlockRegistryPopulator { private static final ImmutableMap, BiFunction> BLOCK_MAPPERS; private static final BiFunction EMPTY_MAPPER = (bedrockIdentifier, statesBuilder) -> null; + private static final BiFunction V486_MAPPER = (bedrockIdentifier, statesBuilder) -> { + statesBuilder.remove("no_drop_bit"); // Used in skulls + if (bedrockIdentifier.equals("minecraft:glow_lichen")) { + // Moved around north, south, west + int bits = (int) statesBuilder.get("multi_face_direction_bits"); + boolean north = (bits & (1 << 2)) != 0; + boolean south = (bits & (1 << 3)) != 0; + boolean west = (bits & (1 << 4)) != 0; + if (north) { + bits |= 1 << 4; + } else { + bits &= ~(1 << 4); + } + if (south) { + bits |= 1 << 2; + } else { + bits &= ~(1 << 2); + } + if (west) { + bits |= 1 << 3; + } else { + bits &= ~(1 << 3); + } + statesBuilder.put("multi_face_direction_bits", bits); + } + return null; + }; + static { ImmutableMap.Builder, BiFunction> stateMapperBuilder = ImmutableMap., BiFunction>builder() - .put(ObjectIntPair.of("1_17_40", Bedrock_v471.V471_CODEC.getProtocolVersion()), EMPTY_MAPPER) - .put(ObjectIntPair.of("1_18_10", Bedrock_v486.V486_CODEC.getProtocolVersion()), (bedrockIdentifier, statesBuilder) -> { - statesBuilder.remove("no_drop_bit"); // Used in skulls - if (bedrockIdentifier.equals("minecraft:glow_lichen")) { - // Moved around north, south, west - int bits = (int) statesBuilder.get("multi_face_direction_bits"); - boolean north = (bits & (1 << 2)) != 0; - boolean south = (bits & (1 << 3)) != 0; - boolean west = (bits & (1 << 4)) != 0; - if (north) { - bits |= 1 << 4; - } else { - bits &= ~(1 << 4); - } - if (south) { - bits |= 1 << 2; - } else { - bits &= ~(1 << 2); - } - if (west) { - bits |= 1 << 3; - } else { - bits &= ~(1 << 3); - } - statesBuilder.put("multi_face_direction_bits", bits); - } - return null; + .put(ObjectIntPair.of("1_18_0", Bedrock_v475.V475_CODEC.getProtocolVersion()), EMPTY_MAPPER) + .put(ObjectIntPair.of("1_18_10", Bedrock_v486.V486_CODEC.getProtocolVersion()), V486_MAPPER) + .put(ObjectIntPair.of("1_18_30", Bedrock_v503.V503_CODEC.getProtocolVersion()), (bedrockIdentifier, statesBuilder) -> { + // Apply these fixes too + V486_MAPPER.apply(bedrockIdentifier, statesBuilder); + return switch (bedrockIdentifier) { + case "minecraft:pistonArmCollision" -> "minecraft:piston_arm_collision"; + case "minecraft:stickyPistonArmCollision" -> "minecraft:sticky_piston_arm_collision"; + case "minecraft:movingBlock" -> "minecraft:moving_block"; + case "minecraft:tripWire" -> "minecraft:trip_wire"; + case "minecraft:seaLantern" -> "minecraft:sea_lantern"; + case "minecraft:concretePowder" -> "minecraft:concrete_powder"; + default -> null; + }; }); BLOCK_MAPPERS = stateMapperBuilder.build(); 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 0e12669e3..37b6c49f4 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 @@ -35,10 +35,12 @@ import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; -import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; import com.nukkitx.protocol.bedrock.v475.Bedrock_v475; import com.nukkitx.protocol.bedrock.v486.Bedrock_v486; -import it.unimi.dsi.fastutil.ints.*; +import com.nukkitx.protocol.bedrock.v503.Bedrock_v503; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.objects.*; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; @@ -58,19 +60,16 @@ import java.util.*; * Populates the item registries. */ public class ItemRegistryPopulator { - private static final Map PALETTE_VERSIONS; - - static { - PALETTE_VERSIONS = new Object2ObjectOpenHashMap<>(); - PALETTE_VERSIONS.put("1_17_40", new PaletteVersion(Bedrock_v471.V471_CODEC.getProtocolVersion(), Collections.emptyMap())); - PALETTE_VERSIONS.put("1_18_0", new PaletteVersion(Bedrock_v475.V475_CODEC.getProtocolVersion(), Collections.emptyMap())); - PALETTE_VERSIONS.put("1_18_10", new PaletteVersion(Bedrock_v486.V486_CODEC.getProtocolVersion(), Collections.emptyMap())); - } private record PaletteVersion(int protocolVersion, Map additionalTranslatedItems) { } public static void populate() { + Map paletteVersions = new Object2ObjectOpenHashMap<>(); + paletteVersions.put("1_18_0", new PaletteVersion(Bedrock_v475.V475_CODEC.getProtocolVersion(), Collections.emptyMap())); + paletteVersions.put("1_18_10", new PaletteVersion(Bedrock_v486.V486_CODEC.getProtocolVersion(), Collections.emptyMap())); + paletteVersions.put("1_18_30", new PaletteVersion(Bedrock_v503.V503_CODEC.getProtocolVersion(), Collections.emptyMap())); + GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); TypeReference> mappingItemsType = new TypeReference<>() { }; @@ -88,7 +87,7 @@ public class ItemRegistryPopulator { Int2IntMap dyeColors = new FixedInt2IntMap(); /* Load item palette */ - for (Map.Entry palette : PALETTE_VERSIONS.entrySet()) { + for (Map.Entry palette : paletteVersions.entrySet()) { TypeReference> paletteEntriesType = new TypeReference<>() {}; // Used to get the Bedrock namespaced ID (in instances where there are small differences) @@ -232,12 +231,15 @@ public class ItemRegistryPopulator { } String bedrockIdentifier; - if (javaIdentifier.equals("minecraft:music_disc_otherside") && palette.getValue().protocolVersion() <= Bedrock_v471.V471_CODEC.getProtocolVersion()) { - bedrockIdentifier = "minecraft:music_disc_pigstep"; - } else if (javaIdentifier.equals("minecraft:globe_banner_pattern") && palette.getValue().protocolVersion() < Bedrock_v486.V486_CODEC.getProtocolVersion()) { + if (javaIdentifier.equals("minecraft:globe_banner_pattern") && palette.getValue().protocolVersion() < Bedrock_v486.V486_CODEC.getProtocolVersion()) { bedrockIdentifier = "minecraft:banner_pattern"; } else { bedrockIdentifier = mappingItem.getBedrockIdentifier(); + if (palette.getValue().protocolVersion() >= Bedrock_v503.V503_CODEC.getProtocolVersion()) { + if (bedrockIdentifier.equals("minecraft:sealantern")) { + bedrockIdentifier = "minecraft:sea_lantern"; + } + } } if (usingFurnaceMinecart && javaIdentifier.equals("minecraft:furnace_minecart")) { 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 b72fe29a2..6d8fa477b 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.session; +import com.fasterxml.jackson.databind.JsonNode; import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.auth.exception.request.InvalidCredentialsException; import com.github.steveice10.mc.auth.exception.request.RequestException; @@ -66,7 +67,6 @@ import com.nukkitx.protocol.bedrock.data.*; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.*; -import com.nukkitx.protocol.bedrock.v471.Bedrock_v471; import io.netty.channel.Channel; import io.netty.channel.EventLoop; import it.unimi.dsi.fastutil.ints.*; @@ -144,6 +144,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private AuthData authData; @Setter private BedrockClientData clientData; + /** + * Used for Floodgate skin uploading + */ + @Setter + private JsonNode certChainData; @Accessors(fluent = true) @Setter @@ -1377,7 +1382,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { startGamePacket.setPlayerPosition(Vector3f.from(0, 69, 0)); startGamePacket.setRotation(Vector2f.from(1, 1)); - startGamePacket.setSeed(-1); + startGamePacket.setSeed(-1L); startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(dimension)); startGamePacket.setGeneratorId(1); startGamePacket.setLevelGameType(GameType.SURVIVAL); @@ -1426,10 +1431,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { settings.setServerAuthoritativeBlockBreaking(false); startGamePacket.setPlayerMovementSettings(settings); - if (upstream.getProtocolVersion() <= Bedrock_v471.V471_CODEC.getProtocolVersion()) { - startGamePacket.getExperiments().add(new ExperimentData("caves_and_cliffs", true)); - } - upstream.sendPacket(startGamePacket); } diff --git a/core/src/main/java/org/geysermc/geyser/session/auth/AuthData.java b/core/src/main/java/org/geysermc/geyser/session/auth/AuthData.java index 802ee3ca0..99b7ae3af 100644 --- a/core/src/main/java/org/geysermc/geyser/session/auth/AuthData.java +++ b/core/src/main/java/org/geysermc/geyser/session/auth/AuthData.java @@ -25,18 +25,7 @@ package org.geysermc.geyser.session.auth; -import com.fasterxml.jackson.databind.JsonNode; -import org.geysermc.geyser.GeyserImpl; - import java.util.UUID; -public record AuthData(String name, UUID uuid, String xuid, - JsonNode certChainData, String clientData) { - - public void upload(GeyserImpl geyser) { - // we can't upload the skin in LoginEncryptionUtil since the global server would return - // the skin too fast, that's why we upload it after we know for sure that the target server - // is ready to handle the result of the global server - geyser.getSkinUploader().uploadSkin(certChainData, clientData); - } +public record AuthData(String name, UUID uuid, String xuid) { } diff --git a/core/src/main/java/org/geysermc/geyser/session/auth/BedrockClientData.java b/core/src/main/java/org/geysermc/geyser/session/auth/BedrockClientData.java index b3601f6c3..07dd38491 100644 --- a/core/src/main/java/org/geysermc/geyser/session/auth/BedrockClientData.java +++ b/core/src/main/java/org/geysermc/geyser/session/auth/BedrockClientData.java @@ -25,9 +25,11 @@ package org.geysermc.geyser.session.auth; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; +import lombok.Setter; import org.geysermc.floodgate.util.DeviceOs; import org.geysermc.floodgate.util.InputMode; import org.geysermc.floodgate.util.UiProfile; @@ -107,6 +109,10 @@ public final class BedrockClientData { @JsonProperty(value = "PlayFabId") private String playFabId; + @JsonIgnore + @Setter + private String originalString = null; + public DeviceOs getDeviceOs() { return deviceOs != null ? deviceOs : DeviceOs.UNKNOWN; } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/ChunkCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/ChunkCache.java index feb1cf3a8..91d6b33d6 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/ChunkCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/ChunkCache.java @@ -33,6 +33,7 @@ import lombok.Setter; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.chunk.GeyserChunk; +import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.util.MathUtils; public class ChunkCache { @@ -45,11 +46,11 @@ public class ChunkCache { private int heightY; /** - * Whether the Bedrock client believes they are in a world with a minimum of -64 and maximum of 320 + * Which dimension Bedrock understands themselves to be in. */ @Getter @Setter - private boolean isExtendedHeight = false; + private BedrockDimension bedrockDimension = BedrockDimension.OVERWORLD; public ChunkCache(GeyserSession session) { this.cache = !session.getGeyser().getWorldManager().hasOwnChunkCache(); // To prevent Spigot from initializing diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java index 1a6771cc5..91ed5aa2b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java @@ -40,11 +40,8 @@ public class BedrockTextTranslator extends PacketTranslator { public void translate(GeyserSession session, TextPacket packet) { String message = packet.getMessage(); - if (message.isBlank()) { - // Java Edition (as of 1.17.1) just doesn't pass on these messages, so... we won't either! - return; - } - + // The order here is important - strip out illegal characters first, then check if it's blank + // (in case the message is blank after removing) if (message.indexOf(ChatColor.ESCAPE) != -1) { // Filter out all escape characters - Java doesn't let you type these StringBuilder builder = new StringBuilder(); @@ -57,6 +54,11 @@ public class BedrockTextTranslator extends PacketTranslator { message = builder.toString(); } + if (message.isBlank()) { + // Java Edition (as of 1.17.1) just doesn't pass on these messages, so... we won't either! + return; + } + if (MessageTranslator.isTooLong(message, session)) { return; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java index 2fccbe482..a63c0f334 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -37,14 +37,12 @@ import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @Translator(packet = MovePlayerPacket.class) public class BedrockMovePlayerTranslator extends PacketTranslator { - /* The upper and lower bounds to check for the void floor that only exists in Bedrock. These are the constants for the overworld. */ - private static final int BEDROCK_OVERWORLD_VOID_FLOOR_UPPER_Y = -104; - private static final int BEDROCK_OVERWORLD_VOID_FLOOR_LOWER_Y = BEDROCK_OVERWORLD_VOID_FLOOR_UPPER_Y + 2; @Override public void translate(GeyserSession session, MovePlayerPacket packet) { @@ -124,11 +122,10 @@ public class BedrockMovePlayerTranslator extends PacketTranslator= (extendedWorld ? BEDROCK_OVERWORLD_VOID_FLOOR_UPPER_Y : -40)) { + // The void floor is offset about 40 blocks below the bottom of the world + BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension(); + int voidFloorLocation = bedrockDimension.minY() - 40; + if (floorY <= (voidFloorLocation + 2) && floorY >= voidFloorLocation) { // Work around there being a floor at the bottom of the world and teleport the player below it // Moving from below to above the void floor works fine entity.setPosition(entity.getPosition().sub(0, 4f, 0)); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaGameProfileTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaGameProfileTranslator.java index 3978db0df..6dd826bf7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaGameProfileTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaGameProfileTranslator.java @@ -57,7 +57,13 @@ public class JavaGameProfileTranslator extends PacketTranslator> 4) - 1; + BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension(); + int maxBedrockSectionY = (bedrockDimension.height() >> 4) - 1; int sectionCount; byte[] payload; ByteBuf byteBuf = null; - GeyserChunkSection[] sections = new GeyserChunkSection[javaChunks.length - (yOffset + ((overworld ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT) >> 4))]; + GeyserChunkSection[] sections = new GeyserChunkSection[javaChunks.length - (yOffset + (bedrockDimension.minY() >> 4))]; try { NetInput in = new StreamNetInput(new ByteArrayInputStream(packet.getChunkData())); @@ -113,7 +116,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator> 4)); + int bedrockSectionY = sectionY + (yOffset - (bedrockDimension.minY() >> 4)); if (bedrockSectionY < 0 || maxBedrockSectionY < bedrockSectionY) { // Ignore this chunk section since it goes outside the bounds accepted by the Bedrock client continue; @@ -309,11 +312,11 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator= Bedrock_v475.V475_CODEC.getProtocolVersion(); - int biomeCount = isNewVersion ? 25 : 32; - int dimensionOffset = (overworld ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT) >> 4; + // As of 1.18.0, Bedrock hardcodes to always read 25 biome sections + // As of 1.18.30, the hardcode may now be tied to the dimension definition + boolean isNewVersion = session.getUpstream().getProtocolVersion() >= Bedrock_v503.V503_CODEC.getProtocolVersion(); + int biomeCount = isNewVersion ? bedrockDimension.height() >> 4 : 25; + int dimensionOffset = bedrockDimension.minY() >> 4; for (int i = 0; i < biomeCount; i++) { int biomeYOffset = dimensionOffset + i; if (biomeYOffset < yOffset) { 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 581433a5f..bc4e8c1ff 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 @@ -42,7 +42,10 @@ public class JavaSetTimeTranslator extends PacketTranslator= 0) { // Client thinks there is no daylight cycle but there is diff --git a/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java b/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java index 7fdf12ec9..445ffb882 100644 --- a/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java @@ -46,23 +46,13 @@ import org.geysermc.geyser.level.chunk.bitarray.SingletonBitArray; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.translator.level.block.entity.BedrockOnlyBlockEntity; import static org.geysermc.geyser.level.block.BlockStateValues.JAVA_AIR_ID; @UtilityClass public class ChunkUtils { - /** - * The minimum height Bedrock Edition will accept. - */ - public static final int MINIMUM_ACCEPTED_HEIGHT = 0; - public static final int MINIMUM_ACCEPTED_HEIGHT_OVERWORLD = -64; - /** - * The maximum chunk height Bedrock Edition will accept, from the lowest point to the highest. - */ - public static final int MAXIMUM_ACCEPTED_HEIGHT = 256; - public static final int MAXIMUM_ACCEPTED_HEIGHT_OVERWORLD = 384; - /** * An empty subchunk. */ @@ -249,17 +239,20 @@ public class ChunkUtils { throw new RuntimeException("Maximum Y must be a multiple of 16!"); } - int dimension = DimensionUtils.javaToBedrock(session.getDimension()); - boolean extendedHeight = dimension == 0; - session.getChunkCache().setExtendedHeight(extendedHeight); + BedrockDimension bedrockDimension = switch (session.getDimension()) { + case DimensionUtils.THE_END -> BedrockDimension.THE_END; + case DimensionUtils.NETHER -> DimensionUtils.isCustomBedrockNetherId() ? BedrockDimension.THE_END : BedrockDimension.THE_NETHER; + default -> BedrockDimension.OVERWORLD; + }; + session.getChunkCache().setBedrockDimension(bedrockDimension); // Yell in the console if the world height is too height in the current scenario // The constraints change depending on if the player is in the overworld or not, and if experimental height is enabled - if (minY < (extendedHeight ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT) - || maxY > (extendedHeight ? MAXIMUM_ACCEPTED_HEIGHT_OVERWORLD : MAXIMUM_ACCEPTED_HEIGHT)) { + // (Ignore this for the Nether. We can't change that at the moment without the workaround. :/ ) + if (minY < bedrockDimension.minY() || (bedrockDimension.doUpperHeightWarn() && maxY > bedrockDimension.height())) { session.getGeyser().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.translator.chunk.out_of_bounds", - extendedHeight ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT, - extendedHeight ? MAXIMUM_ACCEPTED_HEIGHT_OVERWORLD : MAXIMUM_ACCEPTED_HEIGHT, + String.valueOf(bedrockDimension.minY()), + String.valueOf(bedrockDimension.height()), session.getDimension())); } diff --git a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java index 5af5e2c2b..f1aca63e8 100644 --- a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java @@ -109,7 +109,7 @@ public class DimensionUtils { // we check if the player is entering the nether and apply the nether fog to fake the fact that the client // thinks they are in the end dimension. if (BEDROCK_NETHER_ID == 2) { - if (bedrockDimension == BEDROCK_NETHER_ID) { + if (NETHER.equals(javaDimension)) { session.sendFog("minecraft:fog_hell"); } else if (previousDimension == BEDROCK_NETHER_ID) { session.removeFog("minecraft:fog_hell"); 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 3488f713c..d8d3e3c49 100644 --- a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java @@ -155,10 +155,11 @@ public class LoginEncryptionUtils { session.setAuthenticationData(new AuthData( extraData.get("displayName").asText(), UUID.fromString(extraData.get("identity").asText()), - extraData.get("XUID").asText(), - certChainData, clientData + extraData.get("XUID").asText() )); + session.setCertChainData(certChainData); + if (payload.get("identityPublicKey").getNodeType() != JsonNodeType.STRING) { throw new RuntimeException("Identity Public Key was not found!"); } @@ -169,6 +170,7 @@ public class LoginEncryptionUtils { JsonNode clientDataJson = JSON_MAPPER.readTree(clientJwt.getPayload().toBytes()); BedrockClientData data = JSON_MAPPER.convertValue(clientDataJson, BedrockClientData.class); + data.setOriginalString(clientData); session.setClientData(data); if (EncryptionUtils.canUseEncryption()) { diff --git a/core/src/main/resources/bedrock/block_palette.1_17_40.nbt b/core/src/main/resources/bedrock/block_palette.1_18_0.nbt similarity index 100% rename from core/src/main/resources/bedrock/block_palette.1_17_40.nbt rename to core/src/main/resources/bedrock/block_palette.1_18_0.nbt diff --git a/core/src/main/resources/bedrock/block_palette.1_18_30.nbt b/core/src/main/resources/bedrock/block_palette.1_18_30.nbt new file mode 100644 index 0000000000000000000000000000000000000000..bf7690970f792f71b62d7b7f0c9a2f80a3292a21 GIT binary patch literal 45536 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