mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-24 01:09:30 +00:00
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)
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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<T> extends GeyserTestInstance {
|
||||
import java.util.List;
|
||||
|
||||
public class GeyserComponentHashTestInstance<T> extends GameTestInstance {
|
||||
|
||||
private static final MapCodec<TypedDataComponent<?>> TYPED_COMPONENT_CODEC = DataComponentType.PERSISTENT_CODEC
|
||||
.dispatchMap("component", TypedDataComponent::type, GeyserComponentHashTestInstance::typedComponentCodec);
|
||||
@@ -53,12 +57,15 @@ public class GeyserComponentHashTestInstance<T> extends GeyserTestInstance {
|
||||
|
||||
private final TypedDataComponent<T> testValue;
|
||||
|
||||
protected GeyserComponentHashTestInstance(TypedDataComponent<T> testValue) {
|
||||
public GeyserComponentHashTestInstance(TypedDataComponent<T> 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<T> extends GeyserTestInstance {
|
||||
// Hash both and compare
|
||||
RegistryOps<HashCode> 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<T> extends GeyserTestInstance {
|
||||
|
||||
@Override
|
||||
protected @NotNull MutableComponent typeDescription() {
|
||||
// TODO more descriptive?
|
||||
return Component.literal("Geyser Data Component Hash Test");
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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<? extends GameTestInstance> codec) {
|
||||
Registry.register(BuiltInRegistries.TEST_INSTANCE_TYPE, createKey(name), codec);
|
||||
}
|
||||
|
||||
public static void bootstrap() {
|
||||
register("component_hash", GeyserComponentHashTestInstance.CODEC);
|
||||
}
|
||||
}
|
||||
@@ -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<UUID, GeyserSession> 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);
|
||||
}
|
||||
@@ -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<Boolean> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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": {
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, Map<String, Integer>> platformTypeMap = new HashMap<>();
|
||||
Map<String, Integer> 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<String, Integer> 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<String, Integer> 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<String, Map<String, Integer>> versionMap = new HashMap<>();
|
||||
Map<String, Integer> 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<String, Map<String, Integer>> map = new HashMap<>();
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
Map<String, Integer> 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<Map<String, String>> type = new TypeReference<>() { };
|
||||
|
||||
Map<String, String> authChainFile = null;
|
||||
try {
|
||||
authChainFile = JSON_MAPPER.readValue(authChainsFile, type);
|
||||
} catch (IOException e) {
|
||||
logger.error("Cannot load saved user tokens!", e);
|
||||
}
|
||||
if (authChainFile != null) {
|
||||
List<String> validUsers = config.getSavedUserLogins();
|
||||
boolean doWrite = false;
|
||||
for (Map.Entry<String, String> 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<String, Map<String, Integer>> platformTypeMap = new HashMap<>();
|
||||
Map<String, Integer> 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<String, Integer> 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<String, Integer> 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<String, Map<String, Integer>> versionMap = new HashMap<>();
|
||||
Map<String, Integer> 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<String, Map<String, Integer>> map = new HashMap<>();
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
Map<String, Integer> 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<Map<String, String>> type = new TypeReference<>() { };
|
||||
|
||||
Map<String, String> authChainFile = null;
|
||||
try {
|
||||
authChainFile = JSON_MAPPER.readValue(authChainsFile, type);
|
||||
} catch (IOException e) {
|
||||
logger.error("Cannot load saved user tokens!", e);
|
||||
}
|
||||
if (authChainFile != null) {
|
||||
List<String> validUsers = config.getSavedUserLogins();
|
||||
boolean doWrite = false;
|
||||
for (Map.Entry<String, String> 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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
Reference in New Issue
Block a user