1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-19 23:09:29 +00:00

Refactor GameProtocol to remove version slash-combinations (#4979)

* Refactor GameProtocol

* Document 1.21.22 as a supported version, update MinecraftVersion javadoc

* Only expose latest Bedrock protocol & version, ensure latest version in ping

* Also indicate 1.21.8 Java edition support in the geyser api

---------

Co-authored-by: onebeastchris <github@onechris.mozmail.com>
This commit is contained in:
Konicai
2025-07-27 14:37:51 -04:00
committed by GitHub
parent a6f707fb10
commit 70bdb7f66a
9 changed files with 102 additions and 94 deletions

View File

@@ -34,7 +34,7 @@ public interface MinecraftVersion {
/** /**
* Gets the Minecraft version as a String. * 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 * @return the version string
*/ */

View File

@@ -57,14 +57,6 @@ repositories {
name = "viaversion" name = "viaversion"
} }
// BungeeCord
// maven("https://oss.sonatype.org/content/repositories/snapshots") {
// mavenContent { snapshotsOnly() }
// }
// For Adventure snapshots
//maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
// Jitpack for e.g. MCPL // Jitpack for e.g. MCPL
maven("https://jitpack.io") { maven("https://jitpack.io") {
content { includeGroupByRegex("com\\.github\\..*") } content { includeGroupByRegex("com\\.github\\..*") }

View File

@@ -41,7 +41,6 @@ import net.kyori.adventure.text.format.NamedTextColor;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.cloudburstmc.protocol.bedrock.util.EncryptionUtils; import org.cloudburstmc.protocol.bedrock.util.EncryptionUtils;
import org.geysermc.api.Geyser; import org.geysermc.api.Geyser;
import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.Form;
@@ -111,7 +110,6 @@ import java.net.UnknownHostException;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.Key; import java.security.Key;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -792,11 +790,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
@Override @Override
public @NonNull List<MinecraftVersion> supportedBedrockVersions() { public @NonNull List<MinecraftVersion> supportedBedrockVersions() {
ArrayList<MinecraftVersion> versions = new ArrayList<>(); return Collections.unmodifiableList(GameProtocol.SUPPORTED_BEDROCK_VERSIONS);
for (BedrockCodec codec : GameProtocol.SUPPORTED_BEDROCK_CODECS) {
versions.add(new MinecraftVersionImpl(codec.getMinecraftVersion(), codec.getProtocolVersion()));
}
return Collections.unmodifiableList(versions);
} }
@Override @Override

View File

@@ -26,8 +26,8 @@
package org.geysermc.geyser.command.defaults; package org.geysermc.geyser.command.defaults;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.geysermc.geyser.GeyserImpl; 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.PlatformType;
import org.geysermc.geyser.api.util.TriState; import org.geysermc.geyser.api.util.TriState;
import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommand;
@@ -43,6 +43,25 @@ import java.util.List;
public class VersionCommand extends GeyserCommand { public class VersionCommand extends GeyserCommand {
private static final String SUPPORTED_BEDROCK_RANGE;
private static final String SUPPORTED_JAVA_RANGE;
static {
List<MinecraftVersion> 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<String> 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; private final GeyserImpl geyser;
public VersionCommand(GeyserImpl geyser, String name, String description, String permission) { public VersionCommand(GeyserImpl geyser, String name, String description, String permission) {
@@ -54,23 +73,8 @@ public class VersionCommand extends GeyserCommand {
public void execute(CommandContext<GeyserCommandSource> context) { public void execute(CommandContext<GeyserCommandSource> context) {
GeyserCommandSource source = context.sender(); GeyserCommandSource source = context.sender();
String bedrockVersions;
List<BedrockCodec> 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<String> 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(), 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 // Disable update checking in dev mode and for players in Geyser Standalone
if (!GeyserImpl.getInstance().isProductionEnvironment() || (!source.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) { if (!GeyserImpl.getInstance().isProductionEnvironment() || (!source.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) {

View File

@@ -34,12 +34,12 @@ import com.google.common.io.Files;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.Getter; import lombok.Getter;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.geysermc.floodgate.util.DeviceOs; import org.geysermc.floodgate.util.DeviceOs;
import org.geysermc.floodgate.util.FloodgateInfoHolder; import org.geysermc.floodgate.util.FloodgateInfoHolder;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.GeyserApi;
import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.extension.Extension;
import org.geysermc.geyser.api.util.MinecraftVersion;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
@@ -224,9 +224,9 @@ public class DumpInfo {
private final int javaProtocol; private final int javaProtocol;
MCInfo() { MCInfo() {
this.bedrockVersions = GameProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockCodec::getMinecraftVersion).toList(); this.bedrockVersions = GameProtocol.SUPPORTED_BEDROCK_VERSIONS.stream().map(MinecraftVersion::versionString).toList();
this.bedrockProtocols = GameProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockCodec::getProtocolVersion).toList(); this.bedrockProtocols = GameProtocol.SUPPORTED_BEDROCK_PROTOCOLS;
this.defaultBedrockProtocol = GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion(); this.defaultBedrockProtocol = GameProtocol.DEFAULT_BEDROCK_PROTOCOL;
this.javaVersions = GameProtocol.getJavaVersions(); this.javaVersions = GameProtocol.getJavaVersions();
this.javaProtocol = GameProtocol.getJavaProtocolVersion(); this.javaProtocol = GameProtocol.getJavaProtocolVersion();
} }

View File

@@ -25,6 +25,8 @@
package org.geysermc.geyser.network; 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.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.cloudburstmc.protocol.bedrock.codec.v786.Bedrock_v786; import org.cloudburstmc.protocol.bedrock.codec.v786.Bedrock_v786;
@@ -32,13 +34,15 @@ import org.cloudburstmc.protocol.bedrock.codec.v800.Bedrock_v800;
import org.cloudburstmc.protocol.bedrock.codec.v818.Bedrock_v818; import org.cloudburstmc.protocol.bedrock.codec.v818.Bedrock_v818;
import org.cloudburstmc.protocol.bedrock.codec.v819.Bedrock_v819; import org.cloudburstmc.protocol.bedrock.codec.v819.Bedrock_v819;
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec; 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.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec; import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec;
import org.geysermc.mcprotocollib.protocol.codec.PacketCodec; import org.geysermc.mcprotocollib.protocol.codec.PacketCodec;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.StringJoiner; import java.util.stream.Collectors;
/** /**
* Contains information about the supported protocols in Geyser. * Contains information about the supported protocols in Geyser.
@@ -46,17 +50,30 @@ import java.util.StringJoiner;
public final class GameProtocol { public final class GameProtocol {
/** /**
* Default Bedrock codec that should act as a fallback. Should represent the latest available * All Bedrock protocol codecs that Geyser uses
* release of the game that Geyser supports.
*/ */
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v819.CODEC.toBuilder() private static final List<BedrockCodec> SUPPORTED_BEDROCK_CODECS = new ArrayList<>();
.minecraftVersion("1.21.93")
.build());
/** /**
* A list of all supported Bedrock versions that can join Geyser * All bedrock protocol versions that Geyser supports
*/ */
public static final List<BedrockCodec> 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<MinecraftVersion> 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 * Java codec that is supported. We only ever support one version for
@@ -65,16 +82,44 @@ public final class GameProtocol {
private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC; private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC;
static { static {
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v786.CODEC.toBuilder() // Strict ordering
.minecraftVersion("1.21.70 - 1.21.73") register(Bedrock_v786.CODEC, "1.21.70", "1.21.71", "1.21.72", "1.21.73");
.build())); register(Bedrock_v800.CODEC, "1.21.80", "1.21.81", "1.21.82", "1.21.83", "1.21.84");
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v800.CODEC.toBuilder() register(Bedrock_v818.CODEC, "1.21.90", "1.21.91", "1.21.92");
.minecraftVersion("1.21.80 - 1.21.84") register(Bedrock_v819.CODEC, "1.21.93", "1.21.94");
.build()));
SUPPORTED_BEDROCK_CODECS.add((CodecProcessor.processCodec(Bedrock_v818.CODEC.toBuilder() MinecraftVersion latestBedrock = SUPPORTED_BEDROCK_VERSIONS.get(SUPPORTED_BEDROCK_VERSIONS.size() - 1);
.minecraftVersion("1.21.90 - 1.21.92") DEFAULT_BEDROCK_VERSION = latestBedrock.versionString();
.build()))); DEFAULT_BEDROCK_PROTOCOL = latestBedrock.protocolVersion();
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); }
/**
* 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());
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());
} }
/** /**
@@ -109,22 +154,13 @@ public final class GameProtocol {
return session.protocolVersion() == Bedrock_v800.CODEC.getProtocolVersion(); return session.protocolVersion() == Bedrock_v800.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. * Gets the supported Minecraft: Java Edition version names.
* *
* @return the supported Minecraft: Java Edition version names * @return the supported Minecraft: Java Edition version names
*/ */
public static List<String> getJavaVersions() { public static List<String> getJavaVersions() {
return List.of(DEFAULT_JAVA_CODEC.getMinecraftVersion()); return List.of(DEFAULT_JAVA_CODEC.getMinecraftVersion(), "1.21.8");
} }
/** /**
@@ -142,31 +178,23 @@ public final class GameProtocol {
* @return the supported Minecraft: Java Edition version * @return the supported Minecraft: Java Edition version
*/ */
public static String getJavaMinecraftVersion() { public static String getJavaMinecraftVersion() {
return DEFAULT_JAVA_CODEC.getMinecraftVersion(); return "1.21.8";
} }
/** /**
* @return a string showing all supported Bedrock versions for this Geyser instance * @return a string showing all supported Bedrock versions for this Geyser instance
*/ */
public static String getAllSupportedBedrockVersions() { public static String getAllSupportedBedrockVersions() {
StringJoiner joiner = new StringJoiner(", "); return SUPPORTED_BEDROCK_VERSIONS.stream()
for (BedrockCodec packetCodec : SUPPORTED_BEDROCK_CODECS) { .map(MinecraftVersion::versionString)
joiner.add(packetCodec.getMinecraftVersion()); .collect(Collectors.joining(", "));
}
return joiner.toString();
} }
/** /**
* @return a string showing all supported Java versions for this Geyser instance * @return a string showing all supported Java versions for this Geyser instance
*/ */
public static String getAllSupportedJavaVersions() { public static String getAllSupportedJavaVersions() {
StringJoiner joiner = new StringJoiner(", "); return String.join(", ", getJavaVersions());
for (String version : getJavaVersions()) {
joiner.add(version);
}
return joiner.toString();
} }
private GameProtocol() { private GameProtocol() {

View File

@@ -120,8 +120,9 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
private boolean setCorrectCodec(int protocolVersion) { private boolean setCorrectCodec(int protocolVersion) {
BedrockCodec packetCodec = GameProtocol.getBedrockCodec(protocolVersion); BedrockCodec packetCodec = GameProtocol.getBedrockCodec(protocolVersion);
if (packetCodec == null) { 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(); String supportedVersions = GameProtocol.getAllSupportedBedrockVersions();
if (protocolVersion > GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { if (protocolVersion > GameProtocol.DEFAULT_BEDROCK_PROTOCOL) {
// Too early to determine session locale // Too early to determine session locale
String disconnectMessage = GeyserLocale.getLocaleStringLog("geyser.network.outdated.server", supportedVersions); String disconnectMessage = GeyserLocale.getLocaleStringLog("geyser.network.outdated.server", supportedVersions);
// If the latest release matches this version, then let the user know. // If the latest release matches this version, then let the user know.
@@ -132,7 +133,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
} }
session.disconnect(disconnectMessage); session.disconnect(disconnectMessage);
return false; 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. // 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 // 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: // This updates the BedrockCompat protocol if necessary:

View File

@@ -78,7 +78,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 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 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; private static final int BRAND_BYTES_LENGTH = GeyserImpl.NAME.getBytes(StandardCharsets.UTF_8).length;
/** /**
@@ -311,7 +311,7 @@ public final class GeyserServer {
.edition("MCPE") .edition("MCPE")
.gameType("Survival") // Can only be Survival or Creative as of 1.16.210.59 .gameType("Survival") // Can only be Survival or Creative as of 1.16.210.59
.nintendoLimited(false) .nintendoLimited(false)
.protocolVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) .protocolVersion(GameProtocol.DEFAULT_BEDROCK_PROTOCOL)
.version(PING_VERSION) .version(PING_VERSION)
.ipv4Port(this.broadcastPort) .ipv4Port(this.broadcastPort)
.ipv6Port(this.broadcastPort) .ipv6Port(this.broadcastPort)
@@ -386,17 +386,6 @@ public final class GeyserServer {
return pong; 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. * @return the throwable from the given supplier, or the throwable caught while calling the supplier.
*/ */

View File

@@ -89,7 +89,7 @@ public final class AssetUtils {
// Get the url for the latest version of the games manifest // Get the url for the latest version of the games manifest
String latestInfoURL = ""; String latestInfoURL = "";
for (Version version : versionManifest.getVersions()) { for (Version version : versionManifest.getVersions()) {
if (version.getId().equals(GameProtocol.getJavaCodec().getMinecraftVersion())) { if (version.getId().equals(GameProtocol.getJavaMinecraftVersion())) {
latestInfoURL = version.getUrl(); latestInfoURL = version.getUrl();
break; break;
} }