Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions common/src/main/java/org/geysermc/floodgate/util/BedrockData.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public final class BedrockData implements Cloneable {
public static final int EXPECTED_LENGTH = 12;
public static final int EXPECTED_LENGTH = 15;

private final String version;
private final String username;
Expand All @@ -53,23 +53,46 @@ public final class BedrockData implements Cloneable {
private final int subscribeId;
private final String verifyCode;

private final boolean education;
private final String tenantId;
private final int adRole;

private final int dataLength;

public static BedrockData of(
String version, String username, String xuid, int deviceOs,
String languageCode, int uiProfile, int inputMode, String ip,
LinkedPlayer linkedPlayer, boolean fromProxy, int subscribeId,
String verifyCode) {
String verifyCode, boolean education, String tenantId, int adRole) {
return new BedrockData(version, username, xuid, deviceOs, languageCode, inputMode,
uiProfile, ip, linkedPlayer, fromProxy, subscribeId, verifyCode, EXPECTED_LENGTH);
uiProfile, ip, linkedPlayer, fromProxy, subscribeId, verifyCode,
education, tenantId, adRole, EXPECTED_LENGTH);
}

public static BedrockData of(
String version, String username, String xuid, int deviceOs,
String languageCode, int uiProfile, int inputMode, String ip,
LinkedPlayer linkedPlayer, boolean fromProxy, int subscribeId,
String verifyCode) {
return of(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip,
linkedPlayer, fromProxy, subscribeId, verifyCode, false, "", -1);
}

public static BedrockData of(
String version, String username, String xuid, int deviceOs,
String languageCode, int uiProfile, int inputMode, String ip,
int subscribeId, String verifyCode) {
return of(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip, null,
false, subscribeId, verifyCode);
false, subscribeId, verifyCode, false, "", -1);
}

public static BedrockData of(
String version, String username, String xuid, int deviceOs,
String languageCode, int uiProfile, int inputMode, String ip,
int subscribeId, String verifyCode,
boolean education, String tenantId, int adRole) {
return of(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip, null,
false, subscribeId, verifyCode, education, tenantId, adRole);
}

@SuppressWarnings("unused")
Expand All @@ -84,13 +107,14 @@ public static BedrockData fromString(String data) {
return new BedrockData(
split[0], split[1], split[2], Integer.parseInt(split[3]), split[4],
Integer.parseInt(split[5]), Integer.parseInt(split[6]), split[7], linkedPlayer,
"1".equals(split[9]), Integer.parseInt(split[10]), split[11], split.length
"1".equals(split[9]), Integer.parseInt(split[10]), split[11],
"1".equals(split[12]), split[13], Integer.parseInt(split[14]), split.length
);
}

private static BedrockData emptyData(int dataLength) {
return new BedrockData(null, null, null, -1, null, -1, -1, null, null, false, -1, null,
dataLength);
false, "", -1, dataLength);
}

@SuppressWarnings("unused")
Expand All @@ -104,7 +128,8 @@ public String toString() {
return version + '\0' + username + '\0' + xuid + '\0' + deviceOs + '\0' +
languageCode + '\0' + uiProfile + '\0' + inputMode + '\0' + ip + '\0' +
(linkedPlayer != null ? linkedPlayer.toString() : "null") + '\0' +
(fromProxy ? 1 : 0) + '\0' + subscribeId + '\0' + verifyCode;
(fromProxy ? 1 : 0) + '\0' + subscribeId + '\0' + verifyCode + '\0' +
(education ? 1 : 0) + '\0' + tenantId + '\0' + adRole;
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/geysermc/geyser/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public final class Constants {

public static final String MINECRAFT_SKIN_SERVER_URL = "https://textures.minecraft.net/texture/";

public static final int CONFIG_VERSION = 7;
public static final int CONFIG_VERSION = 8;

public static final int BSTATS_ID = 5273;

Expand Down
24 changes: 23 additions & 1 deletion core/src/main/java/org/geysermc/geyser/GeyserImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
import org.geysermc.geyser.impl.MinecraftVersionImpl;
import org.geysermc.geyser.level.BedrockDimension;
import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.network.EducationAuthManager;
import org.geysermc.geyser.network.EducationTenancyMode;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.network.netty.GeyserServer;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
Expand Down Expand Up @@ -169,6 +171,9 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
private ScheduledExecutorService scheduledThread;

private GeyserServer geyserServer;
@Getter
private EducationAuthManager educationAuthManager;

private final GeyserBootstrap bootstrap;

private final GeyserEventBus eventBus;
Expand Down Expand Up @@ -396,7 +401,6 @@ private void startInstance() {
}
}


if (platformType() != PlatformType.VIAPROXY) {
boolean floodgatePresent = bootstrap.testFloodgatePluginPresent();
if (config.java().authType() == AuthType.FLOODGATE && !floodgatePresent) {
Expand Down Expand Up @@ -505,6 +509,23 @@ private void startInstance() {
}
}

// Initialize Education Edition auth manager and token pool
EducationTenancyMode tenancyMode = config.education().tenancyMode();
this.educationAuthManager = new EducationAuthManager();
this.educationAuthManager.setup(this);
if (tenancyMode != EducationTenancyMode.OFF) {
Comment on lines +514 to +516
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EducationAuthManager#setup() generates/writes edu_official.yml and edu_standalone.yml. Right now this runs unconditionally on startup, even when tenancy-mode is OFF, which introduces new files/IO side effects for servers that aren’t using Education support. Consider only constructing/setting up the auth manager (and generating these files) when tenancy-mode != OFF, or lazily when the feature/command is first used.

Suggested change
this.educationAuthManager = new EducationAuthManager();
this.educationAuthManager.setup(this);
if (tenancyMode != EducationTenancyMode.OFF) {
if (tenancyMode != EducationTenancyMode.OFF) {
this.educationAuthManager = new EducationAuthManager();
this.educationAuthManager.setup(this);

Copilot uses AI. Check for mistakes.
if (tenancyMode != EducationTenancyMode.STANDALONE) {
this.educationAuthManager.initialize();
} else {
logger.debug("[EduTenancy] Standalone tenancy mode - skipping MESS registration");
}
if (tenancyMode != EducationTenancyMode.OFFICIAL) {
this.educationAuthManager.loadManualTokens();
this.educationAuthManager.loadDeviceCodeTokens();
}
logger.info(String.format("[EduTenancy] Tenancy mode: %s, registered tenants: %s", tenancyMode, educationAuthManager.getRegisteredTenantCount()));
}

MetricsPlatform metricsPlatform = bootstrap.createMetricsPlatform();
if (metricsPlatform != null && metricsPlatform.enabled()) {
metrics = new MetricsBase(
Expand Down Expand Up @@ -745,6 +766,7 @@ public void disable() {
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.kick.done"));
}

runIfNonNull(educationAuthManager, EducationAuthManager::shutdown);
runIfNonNull(scheduledThread, ScheduledExecutorService::shutdown);
runIfNonNull(geyserServer, GeyserServer::shutdown);
runIfNonNull(skinUploader, FloodgateSkinUploader::close);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.geysermc.geyser.command.defaults.ConnectionTestCommand;
import org.geysermc.geyser.command.defaults.CustomOptionsCommand;
import org.geysermc.geyser.command.defaults.DumpCommand;
import org.geysermc.geyser.command.defaults.EduCommand;
import org.geysermc.geyser.command.defaults.ExtensionsCommand;
import org.geysermc.geyser.command.defaults.HelpCommand;
import org.geysermc.geyser.command.defaults.ListCommand;
Expand Down Expand Up @@ -170,6 +171,7 @@ public CommandRegistry(GeyserImpl geyser, CommandManager<GeyserCommandSource> cl
registerBuiltInCommand(new PingCommand("ping", "geyser.commands.ping.desc", "geyser.command.ping"));
registerBuiltInCommand(new CustomOptionsCommand("options", "geyser.commands.options.desc", "geyser.command.options"));
registerBuiltInCommand(new QuickActionsCommand("quickactions", "geyser.commands.quickactions.desc", "geyser.command.quickactions"));
registerBuiltInCommand(new EduCommand(geyser, "edu", "Education Edition server management", "geyser.command.edu"));

if (this.geyser.platformType() == PlatformType.STANDALONE) {
registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop"));
Expand Down
Loading
Loading