From a942ac53b13f3b3cdfec6755c60d7c2fdae1663e Mon Sep 17 00:00:00 2001 From: Eclipse Date: Sat, 30 Aug 2025 10:49:48 +0000 Subject: [PATCH] Set up proper gametest sourceset within Fabric module stuff (yeah this isn't a good commit message I'll get back to this later who knows) --- bootstrap/mod/fabric/build.gradle.kts | 18 +- .../GeyserComponentHashTestInstance.java | 23 +- ...Test.java => GeyserGameTestBootstrap.java} | 10 +- .../GeyserGameTests.java} | 20 +- .../fabric/gametest/GeyserTestInstance.java | 60 -- .../gametest/mixin/GameTestServerMixin.java | 85 --- .../test_instance/{ => hashing}/test.json | 0 .../src/gametest/resources/fabric.mod.json | 14 +- ...ixins.json => geyser-gametest.mixins.json} | 6 +- .../fabric/GeyserFabricBootstrap.java | 2 +- .../platform/fabric/GeyserFabricPlatform.java | 15 +- .../java/org/geysermc/geyser/GeyserImpl.java | 575 +++++++++--------- .../geyser/util/InternalPlatformType.java | 32 + 13 files changed, 389 insertions(+), 471 deletions(-) rename bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/{platform/fabric => }/gametest/GeyserComponentHashTestInstance.java (82%) rename bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/{GeyserGameTest.java => GeyserGameTestBootstrap.java} (86%) rename bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/{platform/fabric/gametest/GeyserFabricGametestBootstrap.java => gametest/GeyserGameTests.java} (72%) delete mode 100644 bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/GeyserTestInstance.java delete mode 100644 bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/mixin/GameTestServerMixin.java rename bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/{ => hashing}/test.json (100%) rename bootstrap/mod/fabric/src/gametest/resources/{geyser-fabric-tests.mixins.json => geyser-gametest.mixins.json} (58%) create mode 100644 core/src/main/java/org/geysermc/geyser/util/InternalPlatformType.java diff --git a/bootstrap/mod/fabric/build.gradle.kts b/bootstrap/mod/fabric/build.gradle.kts index b4d8046b4..823a992da 100644 --- a/bootstrap/mod/fabric/build.gradle.kts +++ b/bootstrap/mod/fabric/build.gradle.kts @@ -18,15 +18,6 @@ loom { } } -fabricApi { - configureTests { - createSourceSet = true - modId = "geyser-gametest" - enableClientGameTests = false - eula = true - } -} - dependencies { modImplementation(libs.fabric.loader) modApi(libs.fabric.api) @@ -59,6 +50,15 @@ dependencies { relocate("org.cloudburstmc.netty") relocate("org.cloudburstmc.protocol") +fabricApi { + configureTests { + createSourceSet = true + modId = "geyser-gametest" + enableClientGameTests = false + eula = true + } +} + tasks { jar { manifest.attributes["Main-Class"] = "org.geysermc.geyser.platform.fabric.GeyserFabricMain" diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/GeyserComponentHashTestInstance.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserComponentHashTestInstance.java similarity index 82% rename from bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/GeyserComponentHashTestInstance.java rename to bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserComponentHashTestInstance.java index dfcb80af9..49514b4ca 100644 --- a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/GeyserComponentHashTestInstance.java +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserComponentHashTestInstance.java @@ -23,28 +23,32 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.platform.fabric.gametest; +package org.geysermc.geyser.gametest; import com.google.common.hash.HashCode; import com.mojang.serialization.MapCodec; import io.netty.buffer.Unpooled; +import net.minecraft.core.Holder; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.TypedDataComponent; import net.minecraft.gametest.framework.GameTestHelper; import net.minecraft.gametest.framework.GameTestInstance; +import net.minecraft.gametest.framework.TestData; +import net.minecraft.gametest.framework.TestEnvironmentDefinition; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.RegistryOps; +import net.minecraft.resources.ResourceLocation; import net.minecraft.util.HashOps; -import org.geysermc.geyser.item.hashing.DataComponentHashers; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.codec.MinecraftTypes; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; import org.jetbrains.annotations.NotNull; -public class GeyserComponentHashTestInstance extends GeyserTestInstance { +import java.util.List; + +public class GeyserComponentHashTestInstance extends GameTestInstance { private static final MapCodec> TYPED_COMPONENT_CODEC = DataComponentType.PERSISTENT_CODEC .dispatchMap("component", TypedDataComponent::type, GeyserComponentHashTestInstance::typedComponentCodec); @@ -53,12 +57,15 @@ public class GeyserComponentHashTestInstance extends GeyserTestInstance { private final TypedDataComponent testValue; - protected GeyserComponentHashTestInstance(TypedDataComponent testValue) { + public GeyserComponentHashTestInstance(TypedDataComponent testValue) { + // TODO use default vanilla test environment + super(new TestData<>(Holder.direct(new TestEnvironmentDefinition.AllOf(List.of())), + ResourceLocation.withDefaultNamespace("empty"), 1, 1, true)); this.testValue = testValue; } @Override - protected void run(@NotNull GameTestHelper helper, GeyserSession session) { + public void run(@NotNull GameTestHelper helper) { // Encode vanilla component to buffer RegistryFriendlyByteBuf buffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), helper.getLevel().registryAccess()); TypedDataComponent.STREAM_CODEC.encode(buffer, testValue); @@ -70,7 +77,8 @@ public class GeyserComponentHashTestInstance extends GeyserTestInstance { // Hash both and compare RegistryOps ops = RegistryOps.create(HashOps.CRC32C_INSTANCE, helper.getLevel().registryAccess()); int expected = testValue.encodeValue(ops).getOrThrow().asInt(); - int geyser = DataComponentHashers.hash(session, mcplComponent).asInt(); + //int geyser = DataComponentHashers.hash(session, mcplComponent).asInt(); + int geyser = 0; helper.assertValueEqual(expected, geyser, Component.literal("Hash for component " + testValue)); @@ -85,6 +93,7 @@ public class GeyserComponentHashTestInstance extends GeyserTestInstance { @Override protected @NotNull MutableComponent typeDescription() { + // TODO more descriptive? return Component.literal("Geyser Data Component Hash Test"); } diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTest.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTestBootstrap.java similarity index 86% rename from bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTest.java rename to bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTestBootstrap.java index 73aaec820..066d11521 100644 --- a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTest.java +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTestBootstrap.java @@ -25,6 +25,12 @@ package org.geysermc.geyser.gametest; -public class GeyserGameTest { - // TODO actually add gametests +import net.fabricmc.api.ModInitializer; + +public class GeyserGameTestBootstrap implements ModInitializer { + + @Override + public void onInitialize() { + GeyserGameTests.bootstrap(); + } } diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/GeyserFabricGametestBootstrap.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTests.java similarity index 72% rename from bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/GeyserFabricGametestBootstrap.java rename to bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTests.java index 7befc4bc8..59fe49b5e 100644 --- a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/GeyserFabricGametestBootstrap.java +++ b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/gametest/GeyserGameTests.java @@ -23,17 +23,25 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.platform.fabric.gametest; +package org.geysermc.geyser.gametest; -import net.fabricmc.api.ModInitializer; +import com.mojang.serialization.MapCodec; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.gametest.framework.GameTestInstance; import net.minecraft.resources.ResourceLocation; -public class GeyserFabricGametestBootstrap implements ModInitializer { +public class GeyserGameTests { - @Override - public void onInitialize() { - Registry.register(BuiltInRegistries.TEST_INSTANCE_TYPE, ResourceLocation.fromNamespaceAndPath("geyser", "component_hash"), GeyserComponentHashTestInstance.CODEC); + private static ResourceLocation createKey(String name) { + return ResourceLocation.fromNamespaceAndPath("geyser", name); + } + + private static void register(String name, MapCodec codec) { + Registry.register(BuiltInRegistries.TEST_INSTANCE_TYPE, createKey(name), codec); + } + + public static void bootstrap() { + register("component_hash", GeyserComponentHashTestInstance.CODEC); } } diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/GeyserTestInstance.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/GeyserTestInstance.java deleted file mode 100644 index ed5ba2118..000000000 --- a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/GeyserTestInstance.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.platform.fabric.gametest; - -import net.minecraft.core.Holder; -import net.minecraft.gametest.framework.GameTestHelper; -import net.minecraft.gametest.framework.GameTestInstance; -import net.minecraft.gametest.framework.TestData; -import net.minecraft.gametest.framework.TestEnvironmentDefinition; -import net.minecraft.resources.ResourceLocation; -import org.geysermc.geyser.session.GeyserSession; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public abstract class GeyserTestInstance extends GameTestInstance { - - protected GeyserTestInstance() { - // TODO use default vanilla test environment - super(new TestData<>(Holder.direct(new TestEnvironmentDefinition.AllOf(List.of())), - ResourceLocation.withDefaultNamespace("empty"), 1, 1, true)); - } - - @Override - public void run(@NotNull GameTestHelper helper) { - /*Map sessions = GeyserImpl.getInstance().getSessionManager().getSessions(); - while (sessions.isEmpty()) { - try { - Thread.sleep(100L); - } catch (InterruptedException ignored) {} - } - GeyserSession session = sessions.values().stream().findAny().orElseThrow();*/ - run(helper, null); - } - - protected abstract void run(@NotNull GameTestHelper helper, GeyserSession session); -} diff --git a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/mixin/GameTestServerMixin.java b/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/mixin/GameTestServerMixin.java deleted file mode 100644 index aac475bec..000000000 --- a/bootstrap/mod/fabric/src/gametest/java/org/geysermc/geyser/platform/fabric/gametest/mixin/GameTestServerMixin.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.platform.fabric.gametest.mixin; - -import com.mojang.datafixers.DataFixer; -import net.minecraft.gametest.framework.GameTestServer; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.Services; -import net.minecraft.server.WorldStem; -import net.minecraft.server.level.progress.ChunkProgressListenerFactory; -import net.minecraft.server.packs.repository.PackRepository; -import net.minecraft.world.level.storage.LevelStorageSource; -import org.geysermc.geyser.platform.mod.GeyserServerPortGetter; -import org.slf4j.Logger; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.Proxy; - -@Mixin(GameTestServer.class) -public abstract class GameTestServerMixin extends MinecraftServer implements GeyserServerPortGetter { - - @Shadow - @Final - private static Logger LOGGER; - - public GameTestServerMixin(Thread thread, LevelStorageSource.LevelStorageAccess levelStorageAccess, PackRepository packRepository, WorldStem worldStem, - Proxy proxy, DataFixer dataFixer, Services services, ChunkProgressListenerFactory chunkProgressListenerFactory) { - super(thread, levelStorageAccess, packRepository, worldStem, proxy, dataFixer, services, chunkProgressListenerFactory); - } - - @Override - public int geyser$getServerPort() { - return this.getPort(); - } - - @Inject(method = "initServer", at = @At("HEAD"), cancellable = true) - public void startTcpServer(CallbackInfoReturnable callbackInfoReturnable) { - // A bit of copying from dedicated server code - InetAddress address = InetAddress.getLoopbackAddress(); - if (getPort() < 0) { - setPort(25565); - } - LOGGER.info("Starting gametest server on {}:{}", address, getPort()); - LOGGER.info("Geyser's tests will start once a bedrock player connects!"); - - try { - this.getConnection().startTcpServerListener(address, getPort()); - } catch (IOException exception) { - LOGGER.warn("**** FAILED TO BIND TO PORT!"); - LOGGER.warn("The exception was: {}", exception.toString()); - LOGGER.warn("Perhaps a server is already running on that port?"); - callbackInfoReturnable.setReturnValue(false); - } - } -} diff --git a/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/test.json b/bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/test.json similarity index 100% rename from bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/test.json rename to bootstrap/mod/fabric/src/gametest/resources/data/geyser/test_instance/hashing/test.json diff --git a/bootstrap/mod/fabric/src/gametest/resources/fabric.mod.json b/bootstrap/mod/fabric/src/gametest/resources/fabric.mod.json index e64198f90..19fe3feca 100644 --- a/bootstrap/mod/fabric/src/gametest/resources/fabric.mod.json +++ b/bootstrap/mod/fabric/src/gametest/resources/fabric.mod.json @@ -1,8 +1,8 @@ { "schemaVersion": 1, - "id": "${id}-fabric-tests", + "id": "${id}-gametest", "version": "${version}", - "name": "${name}-Fabric-gametest", + "name": "${name}-Gametest", "description": "A bridge/proxy allowing you to connect to Minecraft: Java Edition servers with Minecraft: Bedrock Edition. ", "authors": [ "${author}" @@ -15,16 +15,16 @@ "icon": "assets/geyser/icon.png", "environment": "*", "entrypoints": { - "fabric-gametest": [ - "org.geysermc.geyser.platform.fabric.gametest.GeyserFabricGametestBootstrap" + "main": [ + "org.geysermc.geyser.gametest.GeyserGameTestBootstrap" ] }, "mixins": [ - "geyser-fabric-tests.mixins.json" + "geyser-gametest.mixins.json" ], "depends": { - "fabricloader": ">=0.16.7", + "fabricloader": ">=0.17.2", "fabric-api": "*", - "minecraft": ">=1.21.5" + "minecraft": ">=1.21.9" } } diff --git a/bootstrap/mod/fabric/src/gametest/resources/geyser-fabric-tests.mixins.json b/bootstrap/mod/fabric/src/gametest/resources/geyser-gametest.mixins.json similarity index 58% rename from bootstrap/mod/fabric/src/gametest/resources/geyser-fabric-tests.mixins.json rename to bootstrap/mod/fabric/src/gametest/resources/geyser-gametest.mixins.json index 17dc3df4e..ddc76aa92 100644 --- a/bootstrap/mod/fabric/src/gametest/resources/geyser-fabric-tests.mixins.json +++ b/bootstrap/mod/fabric/src/gametest/resources/geyser-gametest.mixins.json @@ -1,11 +1,9 @@ { "required": true, "minVersion": "0.8", - "package": "org.geysermc.geyser.platform.fabric.gametest.mixin", + "package": "org.geysermc.geyser.platform.gametest.mixin", "compatibilityLevel": "JAVA_17", - "mixins": [ - "GameTestServerMixin" - ], + "mixins": [], "server": [], "client": [], "injectors": { diff --git a/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricBootstrap.java b/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricBootstrap.java index 96a60322b..665b0ec8a 100644 --- a/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricBootstrap.java +++ b/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricBootstrap.java @@ -93,6 +93,6 @@ public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInit @Override public boolean isServer() { - return FabricLoader.getInstance().getEnvironmentType().equals(EnvType.SERVER); + return FabricLoader.getInstance().getEnvironmentType().equals(EnvType.SERVER) && !GeyserFabricPlatform.isGameTestServer(); } } diff --git a/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricPlatform.java b/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricPlatform.java index 2ebfeb0a9..7cfeff4ad 100644 --- a/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricPlatform.java +++ b/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricPlatform.java @@ -34,6 +34,7 @@ import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.platform.mod.GeyserModBootstrap; import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform; +import org.geysermc.geyser.util.InternalPlatformType; import java.io.IOException; import java.io.InputStream; @@ -41,6 +42,7 @@ import java.nio.file.Path; import java.util.Optional; public class GeyserFabricPlatform implements GeyserModPlatform { + private static Boolean isGameTestServer = null; private final ModContainer mod; @@ -50,7 +52,7 @@ public class GeyserFabricPlatform implements GeyserModPlatform { @Override public @NonNull PlatformType platformType() { - return PlatformType.FABRIC; + return isGameTestServer() ? InternalPlatformType.GAMETEST : PlatformType.FABRIC; } @Override @@ -82,9 +84,6 @@ public class GeyserFabricPlatform implements GeyserModPlatform { @Override public @Nullable InputStream resolveResource(@NonNull String resource) { - if (true) { - return this.getClass().getClassLoader().getResourceAsStream(resource); - } // We need to handle this differently, because Fabric shares the classloader across multiple mods Path path = this.mod.findPath(resource).orElse(null); if (path == null) { @@ -99,4 +98,12 @@ public class GeyserFabricPlatform implements GeyserModPlatform { return null; } } + + public static boolean isGameTestServer() { + if (isGameTestServer != null) { + return isGameTestServer; + } + // Property is from GameTestSystemProperties, FAPI internal + return isGameTestServer = System.getProperty("fabric-api.gametest") != null && FabricLoader.getInstance().isDevelopmentEnvironment(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 24d923949..481a9a79c 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -96,6 +96,7 @@ import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.AssetUtils; import org.geysermc.geyser.util.CodeOfConductManager; import org.geysermc.geyser.util.CooldownUtils; +import org.geysermc.geyser.util.InternalPlatformType; import org.geysermc.geyser.util.Metrics; import org.geysermc.geyser.util.NewsHandler; import org.geysermc.geyser.util.VersionCheckUtils; @@ -310,119 +311,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar { Registries.RESOURCE_PACKS.load(); - String geyserUdpPort = System.getProperty("geyserUdpPort", ""); - String pluginUdpPort = geyserUdpPort.isEmpty() ? System.getProperty("pluginUdpPort", "") : geyserUdpPort; - if ("-1".equals(pluginUdpPort)) { - throw new UnsupportedOperationException("This hosting/service provider does not support applications running on the UDP port"); - } - boolean portPropertyApplied = false; - String pluginUdpAddress = System.getProperty("geyserUdpAddress", System.getProperty("pluginUdpAddress", "")); - - if (platformType != PlatformType.STANDALONE) { - int javaPort = bootstrap.getServerPort(); - if (config.getRemote().address().equals("auto")) { - config.setAutoconfiguredRemote(true); - String serverAddress = bootstrap.getServerBindAddress(); - if (!serverAddress.isEmpty() && !"0.0.0.0".equals(serverAddress)) { - config.getRemote().setAddress(serverAddress); - } else { - // Set the remote address to localhost since that is where we are always connecting - try { - config.getRemote().setAddress(InetAddress.getLocalHost().getHostAddress()); - } catch (UnknownHostException ex) { - logger.debug("Unknown host when trying to find localhost."); - if (config.isDebugMode()) { - ex.printStackTrace(); - } - config.getRemote().setAddress(InetAddress.getLoopbackAddress().getHostAddress()); - } - } - if (javaPort != -1) { - config.getRemote().setPort(javaPort); - } - } - - boolean forceMatchServerPort = "server".equals(pluginUdpPort); - if ((config.getBedrock().isCloneRemotePort() || forceMatchServerPort) && javaPort != -1) { - config.getBedrock().setPort(javaPort); - if (forceMatchServerPort) { - if (geyserUdpPort.isEmpty()) { - logger.info("Port set from system generic property to match Java server."); - } else { - logger.info("Port set from system property to match Java server."); - } - portPropertyApplied = true; - } - } - - if ("server".equals(pluginUdpAddress)) { - String address = bootstrap.getServerBindAddress(); - if (!address.isEmpty()) { - config.getBedrock().setAddress(address); - } - } else if (!pluginUdpAddress.isEmpty()) { - config.getBedrock().setAddress(pluginUdpAddress); - } - - if (!portPropertyApplied && !pluginUdpPort.isEmpty()) { - int port = Integer.parseInt(pluginUdpPort); - config.getBedrock().setPort(port); - if (geyserUdpPort.isEmpty()) { - logger.info("Port set from generic system property: " + port); - } else { - logger.info("Port set from system property: " + port); - } - } - - if (platformType != PlatformType.VIAPROXY) { - boolean floodgatePresent = bootstrap.testFloodgatePluginPresent(); - if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) { - logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " - + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); - return; - } else if (config.isAutoconfiguredRemote() && floodgatePresent) { - // Floodgate installed means that the user wants Floodgate authentication - logger.debug("Auto-setting to Floodgate authentication."); - config.getRemote().setAuthType(AuthType.FLOODGATE); - } - } - } - - // Now that the Bedrock port may have been changed, also check the broadcast port (configurable on all platforms) - String broadcastPort = System.getProperty("geyserBroadcastPort", ""); - if (!broadcastPort.isEmpty()) { - try { - int parsedPort = Integer.parseInt(broadcastPort); - if (parsedPort < 1 || parsedPort > 65535) { - throw new NumberFormatException("The broadcast port must be between 1 and 65535 inclusive!"); - } - config.getBedrock().setBroadcastPort(parsedPort); - logger.info("Broadcast port set from system property: " + parsedPort); - } catch (NumberFormatException e) { - logger.error(String.format("Invalid broadcast port from system property: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")")); - } - } - - // It's set to 0 only if no system property or manual config value was set - if (config.getBedrock().broadcastPort() == 0) { - config.getBedrock().setBroadcastPort(config.getBedrock().port()); - } - - String remoteAddress = config.getRemote().address(); - // Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry. - if (!IP_REGEX.matcher(remoteAddress).matches() && !remoteAddress.equalsIgnoreCase("localhost")) { - String[] record = WebUtils.findSrvRecord(this, remoteAddress); - if (record != null) { - int remotePort = Integer.parseInt(record[2]); - config.getRemote().setAddress(remoteAddress = record[3]); - config.getRemote().setPort(remotePort); - logger.debug("Found SRV record \"" + remoteAddress + ":" + remotePort + "\""); - } - } - - pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.getPendingAuthenticationTimeout()); - - this.newsHandler = new NewsHandler(BRANCH, this.buildNumber()); + this.pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.getPendingAuthenticationTimeout()); Packets.initGeyser(); @@ -435,195 +324,309 @@ public class GeyserImpl implements GeyserApi, EventRegistrar { CooldownUtils.setDefaultShowCooldown(config.getShowCooldown()); BedrockDimension.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether - Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads"); - if (bedrockThreadCount == null) { - // Copy the code from Netty's default thread count fallback - bedrockThreadCount = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); - } + if (platformType != InternalPlatformType.GAMETEST) { + this.newsHandler = new NewsHandler(BRANCH, this.buildNumber()); - this.geyserServer = new GeyserServer(this, bedrockThreadCount); - this.geyserServer.bind(new InetSocketAddress(config.getBedrock().address(), config.getBedrock().port())) - .whenComplete((avoid, throwable) -> { - String address = config.getBedrock().address(); - String port = String.valueOf(config.getBedrock().port()); // otherwise we get commas - - if (throwable == null) { - if ("0.0.0.0".equals(address)) { - // basically just hide it in the log because some people get confused and try to change it - logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start.ip_suppressed", port)); - } else { - logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", address, port)); - } - } else { - logger.severe(GeyserLocale.getLocaleStringLog("geyser.core.fail", address, port)); - if (!"0.0.0.0".equals(address)) { - logger.info(Component.text("Suggestion: try setting `address` under `bedrock` in the Geyser config back to 0.0.0.0", NamedTextColor.GREEN)); - logger.info(Component.text("Then, restart this server.", NamedTextColor.GREEN)); - } - } - }).join(); - - if (config.getRemote().authType() == AuthType.FLOODGATE) { - try { - Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath()); - cipher = new AesCipher(new Base64Topping()); - cipher.init(key); - logger.debug("Loaded Floodgate key!"); - // Note: this is positioned after the bind so the skin uploader doesn't try to run if Geyser fails - // to load successfully. Spigot complains about class loader if the plugin is disabled. - skinUploader = new FloodgateSkinUploader(this).start(); - } catch (Exception exception) { - logger.severe(GeyserLocale.getLocaleStringLog("geyser.auth.floodgate.bad_key"), exception); + String geyserUdpPort = System.getProperty("geyserUdpPort", ""); + String pluginUdpPort = geyserUdpPort.isEmpty() ? System.getProperty("pluginUdpPort", "") : geyserUdpPort; + if ("-1".equals(pluginUdpPort)) { + throw new UnsupportedOperationException("This hosting/service provider does not support applications running on the UDP port"); } - } + boolean portPropertyApplied = false; + String pluginUdpAddress = System.getProperty("geyserUdpAddress", System.getProperty("pluginUdpAddress", "")); - if (config.getMetrics().isEnabled()) { - metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger("")); - metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size)); - // Prevent unwanted words best we can - metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().authType().toString().toLowerCase(Locale.ROOT))); - - Map> platformTypeMap = new HashMap<>(); - Map serverPlatform = new HashMap<>(); - serverPlatform.put(bootstrap.getServerPlatform(), 1); - platformTypeMap.put(platformType().platformName(), serverPlatform); - - metrics.addCustomChart(new Metrics.DrilldownPie("platform", () -> { - // By the end, we should return, for example: - // Geyser-Spigot => (Paper, 1) - return platformTypeMap; - })); - - metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", GeyserLocale::getDefaultLocale)); - metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserImpl.VERSION)); - metrics.addCustomChart(new Metrics.SimplePie("javaHaProxyProtocol", () -> String.valueOf(config.getRemote().isUseProxyProtocol()))); - metrics.addCustomChart(new Metrics.SimplePie("bedrockHaProxyProtocol", () -> String.valueOf(config.getBedrock().isEnableProxyProtocol()))); - metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> { - Map valueMap = new HashMap<>(); - for (GeyserSession session : sessionManager.getAllSessions()) { - if (session == null) continue; - if (session.getClientData() == null) continue; - String os = session.getClientData().getDeviceOs().toString(); - if (!valueMap.containsKey(os)) { - valueMap.put(os, 1); + if (platformType != PlatformType.STANDALONE) { + int javaPort = bootstrap.getServerPort(); + if (config.getRemote().address().equals("auto")) { + config.setAutoconfiguredRemote(true); + String serverAddress = bootstrap.getServerBindAddress(); + if (!serverAddress.isEmpty() && !"0.0.0.0".equals(serverAddress)) { + config.getRemote().setAddress(serverAddress); } else { - valueMap.put(os, valueMap.get(os) + 1); - } - } - return valueMap; - })); - metrics.addCustomChart(new Metrics.AdvancedPie("playerVersion", () -> { - Map valueMap = new HashMap<>(); - for (GeyserSession session : sessionManager.getAllSessions()) { - if (session == null) continue; - if (session.getClientData() == null) continue; - String version = session.getClientData().getGameVersion(); - if (!valueMap.containsKey(version)) { - valueMap.put(version, 1); - } else { - valueMap.put(version, valueMap.get(version) + 1); - } - } - return valueMap; - })); - - String minecraftVersion = bootstrap.getMinecraftServerVersion(); - if (minecraftVersion != null) { - Map> versionMap = new HashMap<>(); - Map platformMap = new HashMap<>(); - platformMap.put(bootstrap.getServerPlatform(), 1); - versionMap.put(minecraftVersion, platformMap); - - metrics.addCustomChart(new Metrics.DrilldownPie("minecraftServerVersion", () -> { - // By the end, we should return, for example: - // 1.16.5 => (Spigot, 1) - return versionMap; - })); - } - - // The following code can be attributed to the PaperMC project - // https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0005-Paper-Metrics.patch#L614 - metrics.addCustomChart(new Metrics.DrilldownPie("javaVersion", () -> { - Map> map = new HashMap<>(); - String javaVersion = System.getProperty("java.version"); - Map entry = new HashMap<>(); - entry.put(javaVersion, 1); - - // http://openjdk.java.net/jeps/223 - // Java decided to change their versioning scheme and in doing so modified the - // java.version system property to return $major[.$minor][.$security][-ea], as opposed to - // 1.$major.0_$identifier we can handle pre-9 by checking if the "major" is equal to "1", - // otherwise, 9+ - String majorVersion = javaVersion.split("\\.")[0]; - String release; - - int indexOf = javaVersion.lastIndexOf('.'); - - if (majorVersion.equals("1")) { - release = "Java " + javaVersion.substring(0, indexOf); - } else { - // of course, it really wouldn't be all that simple if they didn't add a quirk, now - // would it valid strings for the major may potentially include values such as -ea to - // denote a pre release - Matcher versionMatcher = Pattern.compile("\\d+").matcher(majorVersion); - if (versionMatcher.find()) { - majorVersion = versionMatcher.group(0); - } - release = "Java " + majorVersion; - } - map.put(release, entry); - return map; - })); - } else { - metrics = null; - } - - if (config.getRemote().authType() == AuthType.ONLINE) { - // May be written/read to on multiple threads from each GeyserSession as well as writing the config - savedAuthChains = new ConcurrentHashMap<>(); - - File authChainsFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_AUTH_CHAINS_FILE).toFile(); - if (authChainsFile.exists()) { - TypeReference> type = new TypeReference<>() { }; - - Map authChainFile = null; - try { - authChainFile = JSON_MAPPER.readValue(authChainsFile, type); - } catch (IOException e) { - logger.error("Cannot load saved user tokens!", e); - } - if (authChainFile != null) { - List validUsers = config.getSavedUserLogins(); - boolean doWrite = false; - for (Map.Entry entry : authChainFile.entrySet()) { - String user = entry.getKey(); - if (!validUsers.contains(user)) { - // Perform a write to this file to purge the now-unused name - doWrite = true; - continue; + // Set the remote address to localhost since that is where we are always connecting + try { + config.getRemote().setAddress(InetAddress.getLocalHost().getHostAddress()); + } catch (UnknownHostException ex) { + logger.debug("Unknown host when trying to find localhost."); + if (config.isDebugMode()) { + ex.printStackTrace(); + } + config.getRemote().setAddress(InetAddress.getLoopbackAddress().getHostAddress()); } - savedAuthChains.put(user, entry.getValue()); } - if (doWrite) { - scheduleAuthChainsWrite(); + if (javaPort != -1) { + config.getRemote().setPort(javaPort); + } + } + + boolean forceMatchServerPort = "server".equals(pluginUdpPort); + if ((config.getBedrock().isCloneRemotePort() || forceMatchServerPort) && javaPort != -1) { + config.getBedrock().setPort(javaPort); + if (forceMatchServerPort) { + if (geyserUdpPort.isEmpty()) { + logger.info("Port set from system generic property to match Java server."); + } else { + logger.info("Port set from system property to match Java server."); + } + portPropertyApplied = true; + } + } + + if ("server".equals(pluginUdpAddress)) { + String address = bootstrap.getServerBindAddress(); + if (!address.isEmpty()) { + config.getBedrock().setAddress(address); + } + } else if (!pluginUdpAddress.isEmpty()) { + config.getBedrock().setAddress(pluginUdpAddress); + } + + if (!portPropertyApplied && !pluginUdpPort.isEmpty()) { + int port = Integer.parseInt(pluginUdpPort); + config.getBedrock().setPort(port); + if (geyserUdpPort.isEmpty()) { + logger.info("Port set from generic system property: " + port); + } else { + logger.info("Port set from system property: " + port); + } + } + + if (platformType != PlatformType.VIAPROXY) { + boolean floodgatePresent = bootstrap.testFloodgatePluginPresent(); + if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) { + logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); + return; + } else if (config.isAutoconfiguredRemote() && floodgatePresent) { + // Floodgate installed means that the user wants Floodgate authentication + logger.debug("Auto-setting to Floodgate authentication."); + config.getRemote().setAuthType(AuthType.FLOODGATE); } } } - } else { - savedAuthChains = null; - } - newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED); + // Now that the Bedrock port may have been changed, also check the broadcast port (configurable on all platforms) + String broadcastPort = System.getProperty("geyserBroadcastPort", ""); + if (!broadcastPort.isEmpty()) { + try { + int parsedPort = Integer.parseInt(broadcastPort); + if (parsedPort < 1 || parsedPort > 65535) { + throw new NumberFormatException("The broadcast port must be between 1 and 65535 inclusive!"); + } + config.getBedrock().setBroadcastPort(parsedPort); + logger.info("Broadcast port set from system property: " + parsedPort); + } catch (NumberFormatException e) { + logger.error(String.format("Invalid broadcast port from system property: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")")); + } + } + + // It's set to 0 only if no system property or manual config value was set + if (config.getBedrock().broadcastPort() == 0) { + config.getBedrock().setBroadcastPort(config.getBedrock().port()); + } + + String remoteAddress = config.getRemote().address(); + // Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry. + if (!IP_REGEX.matcher(remoteAddress).matches() && !remoteAddress.equalsIgnoreCase("localhost")) { + String[] record = WebUtils.findSrvRecord(this, remoteAddress); + if (record != null) { + int remotePort = Integer.parseInt(record[2]); + config.getRemote().setAddress(remoteAddress = record[3]); + config.getRemote().setPort(remotePort); + logger.debug("Found SRV record \"" + remoteAddress + ":" + remotePort + "\""); + } + } + + Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads"); + if (bedrockThreadCount == null) { + // Copy the code from Netty's default thread count fallback + bedrockThreadCount = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); + } + + this.geyserServer = new GeyserServer(this, bedrockThreadCount); + this.geyserServer.bind(new InetSocketAddress(config.getBedrock().address(), config.getBedrock().port())) + .whenComplete((avoid, throwable) -> { + String address = config.getBedrock().address(); + String port = String.valueOf(config.getBedrock().port()); // otherwise we get commas + + if (throwable == null) { + if ("0.0.0.0".equals(address)) { + // basically just hide it in the log because some people get confused and try to change it + logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start.ip_suppressed", port)); + } else { + logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", address, port)); + } + } else { + logger.severe(GeyserLocale.getLocaleStringLog("geyser.core.fail", address, port)); + if (!"0.0.0.0".equals(address)) { + logger.info(Component.text("Suggestion: try setting `address` under `bedrock` in the Geyser config back to 0.0.0.0", NamedTextColor.GREEN)); + logger.info(Component.text("Then, restart this server.", NamedTextColor.GREEN)); + } + } + }).join(); + + if (config.getRemote().authType() == AuthType.FLOODGATE) { + try { + Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath()); + cipher = new AesCipher(new Base64Topping()); + cipher.init(key); + logger.debug("Loaded Floodgate key!"); + // Note: this is positioned after the bind so the skin uploader doesn't try to run if Geyser fails + // to load successfully. Spigot complains about class loader if the plugin is disabled. + skinUploader = new FloodgateSkinUploader(this).start(); + } catch (Exception exception) { + logger.severe(GeyserLocale.getLocaleStringLog("geyser.auth.floodgate.bad_key"), exception); + } + } + + if (config.getMetrics().isEnabled()) { + metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger("")); + metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size)); + // Prevent unwanted words best we can + metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().authType().toString().toLowerCase(Locale.ROOT))); + + Map> platformTypeMap = new HashMap<>(); + Map serverPlatform = new HashMap<>(); + serverPlatform.put(bootstrap.getServerPlatform(), 1); + platformTypeMap.put(platformType().platformName(), serverPlatform); + + metrics.addCustomChart(new Metrics.DrilldownPie("platform", () -> { + // By the end, we should return, for example: + // Geyser-Spigot => (Paper, 1) + return platformTypeMap; + })); + + metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", GeyserLocale::getDefaultLocale)); + metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserImpl.VERSION)); + metrics.addCustomChart(new Metrics.SimplePie("javaHaProxyProtocol", () -> String.valueOf(config.getRemote().isUseProxyProtocol()))); + metrics.addCustomChart(new Metrics.SimplePie("bedrockHaProxyProtocol", () -> String.valueOf(config.getBedrock().isEnableProxyProtocol()))); + metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> { + Map valueMap = new HashMap<>(); + for (GeyserSession session : sessionManager.getAllSessions()) { + if (session == null) continue; + if (session.getClientData() == null) continue; + String os = session.getClientData().getDeviceOs().toString(); + if (!valueMap.containsKey(os)) { + valueMap.put(os, 1); + } else { + valueMap.put(os, valueMap.get(os) + 1); + } + } + return valueMap; + })); + metrics.addCustomChart(new Metrics.AdvancedPie("playerVersion", () -> { + Map valueMap = new HashMap<>(); + for (GeyserSession session : sessionManager.getAllSessions()) { + if (session == null) continue; + if (session.getClientData() == null) continue; + String version = session.getClientData().getGameVersion(); + if (!valueMap.containsKey(version)) { + valueMap.put(version, 1); + } else { + valueMap.put(version, valueMap.get(version) + 1); + } + } + return valueMap; + })); + + String minecraftVersion = bootstrap.getMinecraftServerVersion(); + if (minecraftVersion != null) { + Map> versionMap = new HashMap<>(); + Map platformMap = new HashMap<>(); + platformMap.put(bootstrap.getServerPlatform(), 1); + versionMap.put(minecraftVersion, platformMap); + + metrics.addCustomChart(new Metrics.DrilldownPie("minecraftServerVersion", () -> { + // By the end, we should return, for example: + // 1.16.5 => (Spigot, 1) + return versionMap; + })); + } + + // The following code can be attributed to the PaperMC project + // https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0005-Paper-Metrics.patch#L614 + metrics.addCustomChart(new Metrics.DrilldownPie("javaVersion", () -> { + Map> map = new HashMap<>(); + String javaVersion = System.getProperty("java.version"); + Map entry = new HashMap<>(); + entry.put(javaVersion, 1); + + // http://openjdk.java.net/jeps/223 + // Java decided to change their versioning scheme and in doing so modified the + // java.version system property to return $major[.$minor][.$security][-ea], as opposed to + // 1.$major.0_$identifier we can handle pre-9 by checking if the "major" is equal to "1", + // otherwise, 9+ + String majorVersion = javaVersion.split("\\.")[0]; + String release; + + int indexOf = javaVersion.lastIndexOf('.'); + + if (majorVersion.equals("1")) { + release = "Java " + javaVersion.substring(0, indexOf); + } else { + // of course, it really wouldn't be all that simple if they didn't add a quirk, now + // would it valid strings for the major may potentially include values such as -ea to + // denote a pre release + Matcher versionMatcher = Pattern.compile("\\d+").matcher(majorVersion); + if (versionMatcher.find()) { + majorVersion = versionMatcher.group(0); + } + release = "Java " + majorVersion; + } + map.put(release, entry); + return map; + })); + } else { + metrics = null; + } + + if (config.getRemote().authType() == AuthType.ONLINE) { + // May be written/read to on multiple threads from each GeyserSession as well as writing the config + savedAuthChains = new ConcurrentHashMap<>(); + + File authChainsFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_AUTH_CHAINS_FILE).toFile(); + if (authChainsFile.exists()) { + TypeReference> type = new TypeReference<>() { }; + + Map authChainFile = null; + try { + authChainFile = JSON_MAPPER.readValue(authChainsFile, type); + } catch (IOException e) { + logger.error("Cannot load saved user tokens!", e); + } + if (authChainFile != null) { + List validUsers = config.getSavedUserLogins(); + boolean doWrite = false; + for (Map.Entry entry : authChainFile.entrySet()) { + String user = entry.getKey(); + if (!validUsers.contains(user)) { + // Perform a write to this file to purge the now-unused name + doWrite = true; + continue; + } + savedAuthChains.put(user, entry.getValue()); + } + if (doWrite) { + scheduleAuthChainsWrite(); + } + } + } + } else { + savedAuthChains = null; + } + + newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED); + + if (config.isNotifyOnNewBedrockUpdate()) { + VersionCheckUtils.checkForGeyserUpdate(this::getLogger); + } + } if (isReloading) { this.eventBus.fire(new GeyserPostReloadEvent(this.extensionManager, this.eventBus)); } else { this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus)); } - - if (config.isNotifyOnNewBedrockUpdate()) { - VersionCheckUtils.checkForGeyserUpdate(this::getLogger); - } } @Override diff --git a/core/src/main/java/org/geysermc/geyser/util/InternalPlatformType.java b/core/src/main/java/org/geysermc/geyser/util/InternalPlatformType.java new file mode 100644 index 000000000..55b2b42e7 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/util/InternalPlatformType.java @@ -0,0 +1,32 @@ +/* + * 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.util; + +import org.geysermc.geyser.api.util.PlatformType; + +public final class InternalPlatformType { + public static final PlatformType GAMETEST = new PlatformType("gametest"); +}