diff --git a/README.md b/README.md index fb93a880842..0e027bf129e 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.20.80 - 1.21.21 and Minecraft Java 1.21/1.21.1. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/). +Geyser is currently supporting Minecraft Bedrock 1.20.80 - 1.21.22 and Minecraft Java 1.21/1.21.1. 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/api/src/main/java/org/geysermc/geyser/api/util/MinecraftVersion.java b/api/src/main/java/org/geysermc/geyser/api/util/MinecraftVersion.java index 34a4b59af7e..6171956a63e 100644 --- a/api/src/main/java/org/geysermc/geyser/api/util/MinecraftVersion.java +++ b/api/src/main/java/org/geysermc/geyser/api/util/MinecraftVersion.java @@ -34,7 +34,7 @@ public interface MinecraftVersion { /** * Gets the Minecraft version as a String. - * Example: "1.20.2", or "1.20.40/1.20.41" + * Example formats: "1.21", "1.21.1", "1.21.22" * * @return the version string */ diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 8febf4d2179..3adb447b45d 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -44,7 +44,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; import org.geysermc.api.Geyser; import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.util.FormBuilder; @@ -112,7 +111,6 @@ import java.nio.file.Path; import java.security.Key; import java.text.DecimalFormat; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -826,11 +824,7 @@ public PlatformType platformType() { @Override public @NonNull List supportedBedrockVersions() { - ArrayList versions = new ArrayList<>(); - for (BedrockCodec codec : GameProtocol.SUPPORTED_BEDROCK_CODECS) { - versions.add(new MinecraftVersionImpl(codec.getMinecraftVersion(), codec.getProtocolVersion())); - } - return Collections.unmodifiableList(versions); + return Collections.unmodifiableList(GameProtocol.SUPPORTED_BEDROCK_VERSIONS); } @Override 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 8d34c1bf047..ef5535cb87b 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 @@ -26,8 +26,8 @@ package org.geysermc.geyser.command.defaults; import com.fasterxml.jackson.databind.JsonNode; -import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.util.MinecraftVersion; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.api.util.TriState; import org.geysermc.geyser.command.GeyserCommand; @@ -43,6 +43,25 @@ public class VersionCommand extends GeyserCommand { + private static final String SUPPORTED_BEDROCK_RANGE; + private static final String SUPPORTED_JAVA_RANGE; + + static { + List bedrockVersions = GameProtocol.SUPPORTED_BEDROCK_VERSIONS; + if (bedrockVersions.size() > 1) { + SUPPORTED_BEDROCK_RANGE = bedrockVersions.get(0).versionString() + " - " + bedrockVersions.get(bedrockVersions.size() - 1).versionString(); + } else { + SUPPORTED_BEDROCK_RANGE = bedrockVersions.get(0).versionString(); + } + + List javaVersions = GameProtocol.getJavaVersions(); + if (javaVersions.size() > 1) { + SUPPORTED_JAVA_RANGE = javaVersions.get(0) + " - " + javaVersions.get(javaVersions.size() - 1); + } else { + SUPPORTED_JAVA_RANGE = javaVersions.get(0); + } + } + private final GeyserImpl geyser; public VersionCommand(GeyserImpl geyser, String name, String description, String permission) { @@ -54,23 +73,8 @@ public VersionCommand(GeyserImpl geyser, String name, String description, String public void execute(CommandContext context) { GeyserCommandSource source = context.sender(); - String bedrockVersions; - List supportedCodecs = GameProtocol.SUPPORTED_BEDROCK_CODECS; - if (supportedCodecs.size() > 1) { - bedrockVersions = supportedCodecs.get(0).getMinecraftVersion() + " - " + supportedCodecs.get(supportedCodecs.size() - 1).getMinecraftVersion(); - } else { - bedrockVersions = GameProtocol.SUPPORTED_BEDROCK_CODECS.get(0).getMinecraftVersion(); - } - String javaVersions; - List supportedJavaVersions = GameProtocol.getJavaVersions(); - if (supportedJavaVersions.size() > 1) { - javaVersions = supportedJavaVersions.get(0) + " - " + supportedJavaVersions.get(supportedJavaVersions.size() - 1); - } else { - javaVersions = supportedJavaVersions.get(0); - } - source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.version", source.locale(), - GeyserImpl.NAME, GeyserImpl.VERSION, javaVersions, bedrockVersions)); + GeyserImpl.NAME, GeyserImpl.VERSION, SUPPORTED_JAVA_RANGE, SUPPORTED_BEDROCK_RANGE)); // Disable update checking in dev mode and for players in Geyser Standalone if (!GeyserImpl.getInstance().isProductionEnvironment() || (!source.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) { 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 515e1a6296a..b17e80c176d 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -34,12 +34,12 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.Getter; -import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; import org.geysermc.floodgate.util.DeviceOs; import org.geysermc.floodgate.util.FloodgateInfoHolder; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.util.MinecraftVersion; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; @@ -225,9 +225,9 @@ public static class MCInfo { private final int javaProtocol; MCInfo() { - this.bedrockVersions = GameProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockCodec::getMinecraftVersion).toList(); - this.bedrockProtocols = GameProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockCodec::getProtocolVersion).toList(); - this.defaultBedrockProtocol = GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion(); + this.bedrockVersions = GameProtocol.SUPPORTED_BEDROCK_VERSIONS.stream().map(MinecraftVersion::versionString).toList(); + this.bedrockProtocols = GameProtocol.SUPPORTED_BEDROCK_PROTOCOLS; + this.defaultBedrockProtocol = GameProtocol.DEFAULT_BEDROCK_PROTOCOL; this.javaVersions = GameProtocol.getJavaVersions(); this.javaProtocol = GameProtocol.getJavaProtocolVersion(); } 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 baa1d24d09a..2bb70337e12 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.network; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671; @@ -32,13 +34,15 @@ import org.cloudburstmc.protocol.bedrock.codec.v686.Bedrock_v686; import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712; import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec; +import org.geysermc.geyser.api.util.MinecraftVersion; +import org.geysermc.geyser.impl.MinecraftVersionImpl; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec; import org.geysermc.mcprotocollib.protocol.codec.PacketCodec; import java.util.ArrayList; import java.util.List; -import java.util.StringJoiner; +import java.util.stream.Collectors; /** * Contains information about the supported protocols in Geyser. @@ -46,17 +50,30 @@ 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. + * All Bedrock protocol codecs that Geyser uses */ - public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v712.CODEC.toBuilder() - .minecraftVersion("1.21.20/1.21.21") - .build()); + private static final List SUPPORTED_BEDROCK_CODECS = new ArrayList<>(); /** - * A list of all supported Bedrock versions that can join Geyser + * All bedrock protocol versions that Geyser supports */ - public static final List SUPPORTED_BEDROCK_CODECS = new ArrayList<>(); + public static final IntList SUPPORTED_BEDROCK_PROTOCOLS = new IntArrayList(); + + /** + * All bedrock minecraft versions that Geyser supports. + * There may be multiple MinecraftVersions with the same protocol version. + */ + public static final List SUPPORTED_BEDROCK_VERSIONS = new ArrayList<>(); + + /** + * The latest Bedrock protocol version that Geyser supports. + */ + public static final int DEFAULT_BEDROCK_PROTOCOL; + + /** + * The latest Bedrock Minecraft version that Geyser supports. + */ + public static final String DEFAULT_BEDROCK_VERSION; /** * Java codec that is supported. We only ever support one version for @@ -65,16 +82,47 @@ public final class GameProtocol { private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC; static { - SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v671.CODEC.toBuilder() - .minecraftVersion("1.20.80/1.20.81") - .build())); - SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v685.CODEC.toBuilder() - .minecraftVersion("1.21.0/1.21.1") - .build())); - SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v686.CODEC.toBuilder() - .minecraftVersion("1.21.2/1.21.3") - .build())); - SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); + // Strict ordering + register(Bedrock_v671.CODEC, "1.20.80", "1.20.81"); + register(Bedrock_v685.CODEC, "1.21.0", "1.21.1"); + register(Bedrock_v686.CODEC, "1.21.2", "1.21.3"); + register(Bedrock_v712.CODEC, "1.21.20", "1.21.21", "1.21.22"); + + MinecraftVersion latestBedrock = SUPPORTED_BEDROCK_VERSIONS.get(SUPPORTED_BEDROCK_VERSIONS.size() - 1); + DEFAULT_BEDROCK_VERSION = latestBedrock.versionString(); + DEFAULT_BEDROCK_PROTOCOL = latestBedrock.protocolVersion(); + } + + /** + * Registers a bedrock codec, along with its protocol version and minecraft version(s). + * This method must be called in ascending order in terms of protocol version. + * + * @param codec the codec to register + * @param minecraftVersions all versions the codec supports, in ascending order + */ + private static void register(BedrockCodec codec, String... minecraftVersions) { + // modify packet serializers to better fit our use + codec = CodecProcessor.processCodec(codec); + + SUPPORTED_BEDROCK_CODECS.add(codec); + SUPPORTED_BEDROCK_PROTOCOLS.add(codec.getProtocolVersion()); + + if (minecraftVersions.length < 1) { + throw new IllegalArgumentException("Must provide a minecraft version"); + } + for (String version : minecraftVersions) { + SUPPORTED_BEDROCK_VERSIONS.add(new MinecraftVersionImpl(version, codec.getProtocolVersion())); + } + } + + /** + * Registers a bedrock codec, its protocol version, and a single minecraft version which is taken from the codec. + * This method must be called in ascending order in terms of protocol version. + * + * @param codec the codec to register + */ + private static void register(BedrockCodec codec) { + register(codec, codec.getMinecraftVersion()); } /** @@ -101,15 +149,6 @@ public static boolean isPre1_21_2(GeyserSession session) { return session.getUpstream().getProtocolVersion() < Bedrock_v686.CODEC.getProtocolVersion(); } - /** - * Gets the {@link PacketCodec} for Minecraft: Java Edition. - * - * @return the packet codec for Minecraft: Java Edition - */ - public static PacketCodec getJavaCodec() { - return DEFAULT_JAVA_CODEC; - } - /** * Gets the supported Minecraft: Java Edition version names. * @@ -141,24 +180,16 @@ public static String getJavaMinecraftVersion() { * @return a string showing all supported Bedrock versions for this Geyser instance */ public static String getAllSupportedBedrockVersions() { - StringJoiner joiner = new StringJoiner(", "); - for (BedrockCodec packetCodec : SUPPORTED_BEDROCK_CODECS) { - joiner.add(packetCodec.getMinecraftVersion()); - } - - return joiner.toString(); + return SUPPORTED_BEDROCK_VERSIONS.stream() + .map(MinecraftVersion::versionString) + .collect(Collectors.joining(", ")); } /** * @return a string showing all supported Java versions for this Geyser instance */ public static String getAllSupportedJavaVersions() { - StringJoiner joiner = new StringJoiner(", "); - for (String version : getJavaVersions()) { - joiner.add(version); - } - - return joiner.toString(); + return String.join(", ", getJavaVersions()); } private GameProtocol() { 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 5c48df1f913..f9382d32f65 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -106,8 +106,9 @@ PacketSignal defaultHandler(BedrockPacket packet) { private boolean setCorrectCodec(int protocolVersion) { BedrockCodec packetCodec = GameProtocol.getBedrockCodec(protocolVersion); if (packetCodec == null) { + // None of our Bedrock codecs support this client version, so we can simply compare it to our default protocol. String supportedVersions = GameProtocol.getAllSupportedBedrockVersions(); - if (protocolVersion > GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { + if (protocolVersion > GameProtocol.DEFAULT_BEDROCK_PROTOCOL) { // Too early to determine session locale String disconnectMessage = GeyserLocale.getLocaleStringLog("geyser.network.outdated.server", supportedVersions); // If the latest release matches this version, then let the user know. @@ -118,7 +119,7 @@ private boolean setCorrectCodec(int protocolVersion) { } session.disconnect(disconnectMessage); return false; - } else if (protocolVersion < GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { + } else if (protocolVersion < GameProtocol.DEFAULT_BEDROCK_PROTOCOL) { // A note on the following line: various older client versions have different forms of DisconnectPacket. // Using only the latest BedrockCompat for such clients leads to inaccurate disconnect messages: https://github.com/GeyserMC/Geyser/issues/4378 // This updates the BedrockCompat protocol if necessary: diff --git a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java index 5166bde4d94..b606eb122ed 100644 --- a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java +++ b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java @@ -84,7 +84,7 @@ public final class GeyserServer { /* The following constants are all used to ensure the ping does not reach a length where it is unparsable by the Bedrock client */ - private static final String PING_VERSION = pingVersion(); + private static final String PING_VERSION = GameProtocol.DEFAULT_BEDROCK_VERSION; private static final int PING_VERSION_BYTES_LENGTH = PING_VERSION.getBytes(StandardCharsets.UTF_8).length; private static final int BRAND_BYTES_LENGTH = GeyserImpl.NAME.getBytes(StandardCharsets.UTF_8).length; /** @@ -316,7 +316,7 @@ public BedrockPong onQuery(Channel channel, InetSocketAddress inetSocketAddress) .edition("MCPE") .gameType("Survival") // Can only be Survival or Creative as of 1.16.210.59 .nintendoLimited(false) - .protocolVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .protocolVersion(GameProtocol.DEFAULT_BEDROCK_PROTOCOL) .version(PING_VERSION) .ipv4Port(this.broadcastPort) .ipv6Port(this.broadcastPort) @@ -391,17 +391,6 @@ public BedrockPong onQuery(Channel channel, InetSocketAddress inetSocketAddress) return pong; } - private static String pingVersion() { - // BedrockPong version is required to not be empty as of 1.16.210.59. - // Can only contain . and numbers, so use the latest version instead of sending all - var version = GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion(); - var versionSplit = version.split("/"); - if (versionSplit.length > 1) { - version = versionSplit[versionSplit.length - 1]; - } - return version; - } - /** * @return the throwable from the given supplier, or the throwable caught while calling the supplier. */ diff --git a/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java b/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java index 6bdae6dfeda..968dfe2b735 100644 --- a/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java @@ -89,7 +89,7 @@ public static CompletableFuture generateAssetCache() { // Get the url for the latest version of the games manifest String latestInfoURL = ""; for (Version version : versionManifest.getVersions()) { - if (version.getId().equals(GameProtocol.getJavaCodec().getMinecraftVersion())) { + if (version.getId().equals(GameProtocol.getJavaMinecraftVersion())) { latestInfoURL = version.getUrl(); break; }